Introduce a BinaryReader/Writer to make working with binary protocols easier especially with regards to error handling.

fixes #762
fixes #264
This commit is contained in:
IntegratedQuantum 2025-03-05 19:14:49 +01:00
parent 72a68d6b6d
commit f3b508bf47
3 changed files with 431 additions and 383 deletions

View File

@ -6,7 +6,8 @@ const Block = main.blocks.Block;
const Item = main.items.Item; const Item = main.items.Item;
const ItemStack = main.items.ItemStack; const ItemStack = main.items.ItemStack;
const Tool = main.items.Tool; const Tool = main.items.Tool;
const NeverFailingAllocator = main.utils.NeverFailingAllocator; const utils = main.utils;
const NeverFailingAllocator = utils.NeverFailingAllocator;
const vec = main.vec; const vec = main.vec;
const Vec3d = vec.Vec3d; const Vec3d = vec.Vec3d;
const Vec3f = vec.Vec3f; const Vec3f = vec.Vec3f;
@ -35,7 +36,10 @@ pub const Sync = struct { // MARK: Sync
pub fn deinit() void { pub fn deinit() void {
mutex.lock(); mutex.lock();
while(commands.dequeue()) |cmd| { while(commands.dequeue()) |cmd| {
cmd.finalize(main.globalAllocator, .client, &.{}); var reader = utils.BinaryReader.init(&.{}, main.network.networkEndian);
cmd.finalize(main.globalAllocator, .client, &reader) catch |err| {
std.log.err("Got error while cleaning remaining inventory commands: {s}", .{@errorName(err)});
};
} }
mutex.unlock(); mutex.unlock();
commands.deinit(); commands.deinit();
@ -98,10 +102,10 @@ pub const Sync = struct { // MARK: Sync
return serverToClientMap.get(serverId); return serverToClientMap.get(serverId);
} }
pub fn receiveConfirmation(data: []const u8) void { pub fn receiveConfirmation(reader: *utils.BinaryReader) !void {
mutex.lock(); mutex.lock();
defer mutex.unlock(); defer mutex.unlock();
commands.dequeue().?.finalize(main.globalAllocator, .client, data); try commands.dequeue().?.finalize(main.globalAllocator, .client, reader);
} }
pub fn receiveFailure() void { pub fn receiveFailure() void {
@ -116,7 +120,10 @@ pub const Sync = struct { // MARK: Sync
} }
if(tempData.popOrNull()) |_cmd| { if(tempData.popOrNull()) |_cmd| {
var cmd = _cmd; var cmd = _cmd;
cmd.finalize(main.globalAllocator, .client, &.{}); var reader = utils.BinaryReader.init(&.{}, main.network.networkEndian);
cmd.finalize(main.globalAllocator, .client, &reader) catch |err| {
std.log.err("Got error while cleaning rejected inventory command: {s}", .{@errorName(err)});
};
} }
while(tempData.popOrNull()) |_cmd| { while(tempData.popOrNull()) |_cmd| {
var cmd = _cmd; var cmd = _cmd;
@ -125,7 +132,7 @@ pub const Sync = struct { // MARK: Sync
} }
} }
pub fn receiveSyncOperation(data: []const u8) !void { pub fn receiveSyncOperation(reader: *utils.BinaryReader) !void {
mutex.lock(); mutex.lock();
defer mutex.unlock(); defer mutex.unlock();
var tempData = main.List(Command).init(main.stackAllocator); var tempData = main.List(Command).init(main.stackAllocator);
@ -135,7 +142,7 @@ pub const Sync = struct { // MARK: Sync
cmd.undo(); cmd.undo();
tempData.append(cmd); tempData.append(cmd);
} }
try Command.SyncOperation.executeFromData(data); try Command.SyncOperation.executeFromData(reader);
while(tempData.popOrNull()) |_cmd| { while(tempData.popOrNull()) |_cmd| {
var cmd = _cmd; var cmd = _cmd;
cmd.do(main.globalAllocator, .client, null, main.game.Player.gamemode.raw) catch unreachable; cmd.do(main.globalAllocator, .client, null, main.game.Player.gamemode.raw) catch unreachable;
@ -284,16 +291,19 @@ pub const Sync = struct { // MARK: Sync
} }
} }
} }
command.finalize(main.globalAllocator, .server, &.{}); var reader = utils.BinaryReader.init(&.{}, main.network.networkEndian);
command.finalize(main.globalAllocator, .server, &reader) catch |err| {
std.log.err("Got error while finalizing command on the server side: {s}", .{@errorName(err)});
};
} }
pub fn receiveCommand(source: *main.server.User, data: []const u8) !void { pub fn receiveCommand(source: *main.server.User, reader: *utils.BinaryReader) !void {
mutex.lock(); mutex.lock();
defer mutex.unlock(); defer mutex.unlock();
const typ: Command.PayloadType = @enumFromInt(data[0]); const typ = try reader.readEnum(Command.PayloadType);
@setEvalBranchQuota(100000); @setEvalBranchQuota(100000);
const payload: Command.Payload = switch(typ) { const payload: Command.Payload = switch(typ) {
inline else => |_typ| @unionInit(Command.Payload, @tagName(_typ), try std.meta.FieldType(Command.Payload, _typ).deserialize(data[1..], .server, source)), inline else => |_typ| @unionInit(Command.Payload, @tagName(_typ), try std.meta.FieldType(Command.Payload, _typ).deserialize(reader, .server, source)),
}; };
executeCommand(payload, source); executeCommand(payload, source);
} }
@ -486,16 +496,16 @@ pub const Command = struct { // MARK: Command
return &self.inv._items[self.slot]; return &self.inv._items[self.slot];
} }
fn write(self: InventoryAndSlot, data: *[8]u8) void { fn write(self: InventoryAndSlot, writer: *utils.BinaryWriter) void {
std.mem.writeInt(u32, data[0..4], self.inv.id, .big); writer.writeInt(u32, self.inv.id);
std.mem.writeInt(u32, data[4..8], self.slot, .big); writer.writeInt(u32, self.slot);
} }
fn read(data: *const [8]u8, side: Side, user: ?*main.server.User) !InventoryAndSlot { fn read(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !InventoryAndSlot {
const id = std.mem.readInt(u32, data[0..4], .big); const id = try reader.readInt(u32);
return .{ return .{
.inv = Sync.getInventory(id, side, user) orelse return error.Invalid, .inv = Sync.getInventory(id, side, user) orelse return error.Invalid,
.slot = std.mem.readInt(u32, data[4..8], .big), .slot = try reader.readInt(u32),
}; };
} }
}; };
@ -565,10 +575,8 @@ pub const Command = struct { // MARK: Command
target: ?*main.server.User, target: ?*main.server.User,
}, },
pub fn executeFromData(data: []const u8) !void { pub fn executeFromData(reader: *utils.BinaryReader) !void {
std.debug.assert(data.len >= 1); switch(try deserialize(reader)) {
switch(try deserialize(data)) {
.create => |create| { .create => |create| {
if(create.item) |item| { if(create.item) |item| {
create.inv.ref().item = item; create.inv.ref().item = item;
@ -632,28 +640,19 @@ pub const Command = struct { // MARK: Command
}; };
} }
fn deserialize(fullData: []const u8) !SyncOperation { fn deserialize(reader: *utils.BinaryReader) !SyncOperation {
if(fullData.len == 0) { const typ = try reader.readEnum(SyncOperationType);
return error.Invalid;
}
const typ: SyncOperationType = @enumFromInt(fullData[0]);
const data = fullData[1..];
switch(typ) { switch(typ) {
.create => { .create => {
if(data.len < 10) {
return error.Invalid;
}
var out: SyncOperation = .{.create = .{ var out: SyncOperation = .{.create = .{
.inv = try InventoryAndSlot.read(data[0..8], .client, null), .inv = try InventoryAndSlot.read(reader, .client, null),
.amount = std.mem.readInt(u16, data[8..10], .big), .amount = try reader.readInt(u16),
.item = null, .item = null,
}}; }};
if(data.len > 10) { if(reader.remaining.len != 0) {
const zon = ZonElement.parseFromString(main.stackAllocator, null, data[10..]); const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
out.create.item = try Item.init(zon); out.create.item = try Item.init(zon);
} }
@ -661,42 +660,28 @@ pub const Command = struct { // MARK: Command
return out; return out;
}, },
.delete => { .delete => {
if(data.len != 10) {
return error.Invalid;
}
const out: SyncOperation = .{.delete = .{ const out: SyncOperation = .{.delete = .{
.inv = try InventoryAndSlot.read(data[0..8], .client, null), .inv = try InventoryAndSlot.read(reader, .client, null),
.amount = std.mem.readInt(u16, data[8..10], .big), .amount = try reader.readInt(u16),
}}; }};
return out; return out;
}, },
.useDurability => { .useDurability => {
if(data.len != 12) {
return error.Invalid;
}
const out: SyncOperation = .{.useDurability = .{ const out: SyncOperation = .{.useDurability = .{
.inv = try InventoryAndSlot.read(data[0..8], .client, null), .inv = try InventoryAndSlot.read(reader, .client, null),
.durability = std.mem.readInt(u32, data[8..12], .big), .durability = try reader.readInt(u32),
}}; }};
return out; return out;
}, },
.health => { .health => {
if(data.len != 4) {
return error.Invalid;
}
return .{.health = .{ return .{.health = .{
.target = null, .target = null,
.health = @bitCast(std.mem.readInt(u32, data[0..4], .big)), .health = @bitCast(try reader.readInt(u32)),
}}; }};
}, },
.kill => { .kill => {
if(data.len != 0) {
return error.Invalid;
}
return .{.kill = .{ return .{.kill = .{
.target = null, .target = null,
}}; }};
@ -705,35 +690,35 @@ pub const Command = struct { // MARK: Command
} }
pub fn serialize(self: SyncOperation, allocator: NeverFailingAllocator) []const u8 { pub fn serialize(self: SyncOperation, allocator: NeverFailingAllocator) []const u8 {
var data = main.List(u8).initCapacity(allocator, 13); var writer = utils.BinaryWriter.initCapacity(allocator, main.network.networkEndian, 13);
data.append(@intFromEnum(self)); writer.writeEnum(SyncOperationType, self);
switch(self) { switch(self) {
.create => |create| { .create => |create| {
create.inv.write(data.addMany(8)[0..8]); create.inv.write(&writer);
std.mem.writeInt(u16, data.addMany(2)[0..2], create.amount, .big); writer.writeInt(u16, create.amount);
if(create.item) |item| { if(create.item) |item| {
const zon = ZonElement.initObject(main.stackAllocator); const zon = ZonElement.initObject(main.stackAllocator);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
item.insertIntoZon(main.stackAllocator, zon); item.insertIntoZon(main.stackAllocator, zon);
const string = zon.toStringEfficient(main.stackAllocator, &.{}); const string = zon.toStringEfficient(main.stackAllocator, &.{});
defer main.stackAllocator.free(string); defer main.stackAllocator.free(string);
data.appendSlice(string); writer.writeSlice(string);
} }
}, },
.delete => |delete| { .delete => |delete| {
delete.inv.write(data.addMany(8)[0..8]); delete.inv.write(&writer);
std.mem.writeInt(u16, data.addMany(2)[0..2], delete.amount, .big); writer.writeInt(u16, delete.amount);
}, },
.useDurability => |durability| { .useDurability => |durability| {
durability.inv.write(data.addMany(8)[0..8]); durability.inv.write(&writer);
std.mem.writeInt(u32, data.addMany(4)[0..4], durability.durability, .big); writer.writeInt(u32, durability.durability);
}, },
.health => |health| { .health => |health| {
std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(health.health), .big); writer.writeInt(u32, @bitCast(health.health));
}, },
.kill => {}, .kill => {},
} }
return data.toOwnedSlice(); return writer.data.toOwnedSlice();
} }
}; };
@ -742,13 +727,14 @@ pub const Command = struct { // MARK: Command
syncOperations: main.ListUnmanaged(SyncOperation) = .{}, syncOperations: main.ListUnmanaged(SyncOperation) = .{},
fn serializePayload(self: *Command, allocator: NeverFailingAllocator) []const u8 { fn serializePayload(self: *Command, allocator: NeverFailingAllocator) []const u8 {
var list = main.List(u8).init(allocator); var writer = utils.BinaryWriter.init(allocator, main.network.networkEndian);
defer writer.deinit();
switch(self.payload) { switch(self.payload) {
inline else => |payload| { inline else => |payload| {
payload.serialize(&list); payload.serialize(&writer);
}, },
} }
return list.toOwnedSlice(); return writer.data.toOwnedSlice();
} }
fn do(self: *Command, allocator: NeverFailingAllocator, side: Side, user: ?*main.server.User, gamemode: main.game.Gamemode) error{serverFailure}!void { // MARK: do() fn do(self: *Command, allocator: NeverFailingAllocator, side: Side, user: ?*main.server.User, gamemode: main.game.Gamemode) error{serverFailure}!void { // MARK: do()
@ -811,7 +797,7 @@ pub const Command = struct { // MARK: Command
} }
} }
fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, data: []const u8) void { fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, reader: *utils.BinaryReader) !void {
for(self.baseOperations.items) |step| { for(self.baseOperations.items) |step| {
switch(step) { switch(step) {
.move, .swap, .create, .addHealth => {}, .move, .swap, .create, .addHealth => {},
@ -835,7 +821,7 @@ pub const Command = struct { // MARK: Command
switch(self.payload) { switch(self.payload) {
inline else => |payload| { inline else => |payload| {
if(@hasDecl(@TypeOf(payload), "finalize")) { if(@hasDecl(@TypeOf(payload), "finalize")) {
payload.finalize(side, data); try payload.finalize(side, reader);
} }
}, },
} }
@ -1047,37 +1033,35 @@ pub const Command = struct { // MARK: Command
fn run(_: Open, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User, _: Gamemode) error{serverFailure}!void {} fn run(_: Open, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User, _: Gamemode) error{serverFailure}!void {}
fn finalize(self: Open, side: Side, data: []const u8) void { fn finalize(self: Open, side: Side, reader: *utils.BinaryReader) !void {
if(side != .client) return; if(side != .client) return;
if(data.len >= 4) { if(reader.remaining.len != 0) {
const serverId = std.mem.readInt(u32, data[0..4], .big); const serverId = try reader.readInt(u32);
Sync.ClientSide.mapServerId(serverId, self.inv); Sync.ClientSide.mapServerId(serverId, self.inv);
} }
} }
fn confirmationData(self: Open, allocator: NeverFailingAllocator) []const u8 { fn confirmationData(self: Open, allocator: NeverFailingAllocator) []const u8 {
const data = allocator.alloc(u8, 4); var writer = utils.BinaryWriter.initCapacity(allocator, main.network.networkEndian, 4);
std.mem.writeInt(u32, data[0..4], self.inv.id, .big); writer.writeInt(u32, self.inv.id);
return data; return writer.data.toOwnedSlice();
} }
fn serialize(self: Open, data: *main.List(u8)) void { fn serialize(self: Open, writer: *utils.BinaryWriter) void {
std.mem.writeInt(u32, data.addMany(4)[0..4], self.inv.id, .big); writer.writeInt(u32, self.inv.id);
std.mem.writeInt(usize, data.addMany(8)[0..8], self.inv._items.len, .big); writer.writeInt(usize, self.inv._items.len);
data.append(@intFromEnum(std.meta.activeTag(self.inv.type))); writer.writeEnum(TypeEnum, self.inv.type);
data.append(@intFromEnum(self.source)); writer.writeEnum(SourceType, self.source);
switch(self.source) { switch(self.source) {
.playerInventory, .hand => |val| { .playerInventory, .hand => |val| {
std.mem.writeInt(u32, data.addMany(4)[0..4], val, .big); writer.writeInt(u32, val);
}, },
.recipe => |val| { .recipe => |val| {
std.mem.writeInt(u16, data.addMany(2)[0..2], val.resultAmount, .big); writer.writeInt(u16, val.resultAmount);
data.appendSlice(val.resultItem.id); writer.writeWithDelimiter(val.resultItem.id, 0);
data.append(0);
for(0..val.sourceItems.len) |i| { for(0..val.sourceItems.len) |i| {
std.mem.writeInt(u16, data.addMany(2)[0..2], val.sourceAmounts[i], .big); writer.writeInt(u16, val.sourceAmounts[i]);
data.appendSlice(val.sourceItems[i].id); writer.writeWithDelimiter(val.sourceItems[i].id, 0);
data.append(0);
} }
}, },
.sharedTestingInventory, .other => {}, .sharedTestingInventory, .other => {},
@ -1086,35 +1070,29 @@ pub const Command = struct { // MARK: Command
switch(self.inv.type) { switch(self.inv.type) {
.normal, .creative, .crafting => {}, .normal, .creative, .crafting => {},
.workbench => { .workbench => {
data.appendSlice(self.inv.type.workbench.id); writer.writeSlice(self.inv.type.workbench.id);
}, },
} }
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Open { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Open {
if(data.len < 14) return error.Invalid;
if(side != .server or user == null) return error.Invalid; if(side != .server or user == null) return error.Invalid;
const id = std.mem.readInt(u32, data[0..4], .big); const id = try reader.readInt(u32);
const len = std.mem.readInt(usize, data[4..12], .big); const len = try reader.readInt(u64);
const typeEnum: TypeEnum = @enumFromInt(data[12]); const typeEnum = try reader.readEnum(TypeEnum);
const sourceType: SourceType = @enumFromInt(data[13]); const sourceType = try reader.readEnum(SourceType);
const source: Source = switch(sourceType) { const source: Source = switch(sourceType) {
.playerInventory => .{.playerInventory = std.mem.readInt(u32, data[14..18], .big)}, .playerInventory => .{.playerInventory = try reader.readInt(u32)},
.sharedTestingInventory => .{.sharedTestingInventory = {}}, .sharedTestingInventory => .{.sharedTestingInventory = {}},
.hand => .{.hand = std.mem.readInt(u32, data[14..18], .big)}, .hand => .{.hand = try reader.readInt(u32)},
.recipe => .{ .recipe => .{
.recipe = blk: { .recipe = blk: {
var itemList = main.List(struct {amount: u16, item: *const main.items.BaseItem}).initCapacity(main.stackAllocator, len); var itemList = main.List(struct {amount: u16, item: *const main.items.BaseItem}).initCapacity(main.stackAllocator, len);
defer itemList.deinit(); defer itemList.deinit();
var index: usize = 14; while(reader.remaining.len >= 2) {
while(index + 2 < data.len) { const resultAmount = try reader.readInt(u16);
const resultAmount = std.mem.readInt(u16, data[index..][0..2], .big); const itemId = try reader.readUntilDelimiter(0);
index += 2; const resultItem = main.items.getByID(itemId) orelse return error.Invalid;
const resultItem = if(std.mem.indexOfScalarPos(u8, data, index, 0)) |endIndex| blk2: {
const itemId = data[index..endIndex];
index = endIndex + 1;
break :blk2 main.items.getByID(itemId) orelse return error.Invalid;
} else return error.Invalid;
itemList.append(.{.amount = resultAmount, .item = resultItem}); itemList.append(.{.amount = resultAmount, .item = resultItem});
} }
if(itemList.items.len != len) return error.Invalid; if(itemList.items.len != len) return error.Invalid;
@ -1133,15 +1111,9 @@ pub const Command = struct { // MARK: Command
.other => .{.other = {}}, .other => .{.other = {}},
.alreadyFreed => unreachable, .alreadyFreed => unreachable,
}; };
const remainingLen: usize = switch(sourceType) {
.playerInventory, .hand => 18,
.sharedTestingInventory, .other => 14,
.recipe => data.len,
.alreadyFreed => unreachable,
};
const typ: Type = switch(typeEnum) { const typ: Type = switch(typeEnum) {
inline .normal, .creative, .crafting => |tag| tag, inline .normal, .creative, .crafting => |tag| tag,
.workbench => .{.workbench = main.items.getToolTypeByID(data[remainingLen..]) orelse return error.Invalid}, .workbench => .{.workbench = main.items.getToolTypeByID(reader.remaining) orelse return error.Invalid},
}; };
Sync.ServerSide.createInventory(user.?, id, len, typ, source); Sync.ServerSide.createInventory(user.?, id, len, typ, source);
return .{ return .{
@ -1157,23 +1129,22 @@ pub const Command = struct { // MARK: Command
fn run(_: Close, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User, _: Gamemode) error{serverFailure}!void {} fn run(_: Close, _: NeverFailingAllocator, _: *Command, _: Side, _: ?*main.server.User, _: Gamemode) error{serverFailure}!void {}
fn finalize(self: Close, side: Side, data: []const u8) void { fn finalize(self: Close, side: Side, reader: *utils.BinaryReader) !void {
if(side != .client) return; if(side != .client) return;
self.inv._deinit(self.allocator, .client); self.inv._deinit(self.allocator, .client);
if(data.len >= 4) { if(reader.remaining.len != 0) {
const serverId = std.mem.readInt(u32, data[0..4], .big); const serverId = try reader.readInt(u32);
Sync.ClientSide.unmapServerId(serverId, self.inv.id); Sync.ClientSide.unmapServerId(serverId, self.inv.id);
} }
} }
fn serialize(self: Close, data: *main.List(u8)) void { fn serialize(self: Close, writer: *utils.BinaryWriter) void {
std.mem.writeInt(u32, data.addMany(4)[0..4], self.inv.id, .big); writer.writeInt(u32, self.inv.id);
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Close { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Close {
if(data.len != 4) return error.Invalid;
if(side != .server or user == null) return error.Invalid; if(side != .server or user == null) return error.Invalid;
const id = std.mem.readInt(u32, data[0..4], .big); const id = try reader.readInt(u32);
try Sync.ServerSide.closeInventory(user.?, id); try Sync.ServerSide.closeInventory(user.?, id);
return undefined; return undefined;
} }
@ -1228,16 +1199,15 @@ pub const Command = struct { // MARK: Command
}}, side); }}, side);
} }
fn serialize(self: DepositOrSwap, data: *main.List(u8)) void { fn serialize(self: DepositOrSwap, writer: *utils.BinaryWriter) void {
self.dest.write(data.addMany(8)[0..8]); self.dest.write(writer);
self.source.write(data.addMany(8)[0..8]); self.source.write(writer);
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !DepositOrSwap { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !DepositOrSwap {
if(data.len != 16) return error.Invalid;
return .{ return .{
.dest = try InventoryAndSlot.read(data[0..8], side, user), .dest = try InventoryAndSlot.read(reader, side, user),
.source = try InventoryAndSlot.read(data[8..16], side, user), .source = try InventoryAndSlot.read(reader, side, user),
}; };
} }
}; };
@ -1273,18 +1243,17 @@ pub const Command = struct { // MARK: Command
} }
} }
fn serialize(self: Deposit, data: *main.List(u8)) void { fn serialize(self: Deposit, writer: *utils.BinaryWriter) void {
self.dest.write(data.addMany(8)[0..8]); self.dest.write(writer);
self.source.write(data.addMany(8)[0..8]); self.source.write(writer);
std.mem.writeInt(u16, data.addMany(2)[0..2], self.amount, .big); writer.writeInt(u16, self.amount);
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Deposit { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Deposit {
if(data.len != 18) return error.Invalid;
return .{ return .{
.dest = try InventoryAndSlot.read(data[0..8], side, user), .dest = try InventoryAndSlot.read(reader, side, user),
.source = try InventoryAndSlot.read(data[8..16], side, user), .source = try InventoryAndSlot.read(reader, side, user),
.amount = std.mem.readInt(u16, data[16..18], .big), .amount = try reader.readInt(u16),
}; };
} }
}; };
@ -1339,16 +1308,15 @@ pub const Command = struct { // MARK: Command
} }
} }
fn serialize(self: TakeHalf, data: *main.List(u8)) void { fn serialize(self: TakeHalf, writer: *utils.BinaryWriter) void {
self.dest.write(data.addMany(8)[0..8]); self.dest.write(writer);
self.source.write(data.addMany(8)[0..8]); self.source.write(writer);
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !TakeHalf { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !TakeHalf {
if(data.len != 16) return error.Invalid;
return .{ return .{
.dest = try InventoryAndSlot.read(data[0..8], side, user), .dest = try InventoryAndSlot.read(reader, side, user),
.source = try InventoryAndSlot.read(data[8..16], side, user), .source = try InventoryAndSlot.read(reader, side, user),
}; };
} }
}; };
@ -1393,18 +1361,17 @@ pub const Command = struct { // MARK: Command
}}, side); }}, side);
} }
fn serialize(self: Drop, data: *main.List(u8)) void { fn serialize(self: Drop, writer: *utils.BinaryWriter) void {
self.source.write(data.addMany(8)[0..8]); self.source.write(writer);
if(self.desiredAmount != 0xffff) { if(self.desiredAmount != 0xffff) {
std.mem.writeInt(u16, data.addMany(2)[0..2], self.desiredAmount, .big); writer.writeInt(u16, self.desiredAmount);
} }
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Drop { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Drop {
if(data.len != 8 and data.len != 10) return error.Invalid;
return .{ return .{
.source = try InventoryAndSlot.read(data[0..8], side, user), .source = try InventoryAndSlot.read(reader, side, user),
.desiredAmount = if(data.len == 10) std.mem.readInt(u16, data[8..10], .big) else 0xffff, .desiredAmount = reader.readInt(u16) catch 0xffff,
}; };
} }
}; };
@ -1434,30 +1401,30 @@ pub const Command = struct { // MARK: Command
} }
} }
fn serialize(self: FillFromCreative, data: *main.List(u8)) void { fn serialize(self: FillFromCreative, writer: *utils.BinaryWriter) void {
self.dest.write(data.addMany(8)[0..8]); self.dest.write(writer);
std.mem.writeInt(u16, data.addMany(2)[0..2], self.amount, .big); writer.writeInt(u16, self.amount);
if(self.item) |item| { if(self.item) |item| {
const zon = ZonElement.initObject(main.stackAllocator); const zon = ZonElement.initObject(main.stackAllocator);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
item.insertIntoZon(main.stackAllocator, zon); item.insertIntoZon(main.stackAllocator, zon);
const string = zon.toStringEfficient(main.stackAllocator, &.{}); const string = zon.toStringEfficient(main.stackAllocator, &.{});
defer main.stackAllocator.free(string); defer main.stackAllocator.free(string);
data.appendSlice(string); writer.writeSlice(string);
} }
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !FillFromCreative { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !FillFromCreative {
if(data.len < 10) return error.Invalid; const dest = try InventoryAndSlot.read(reader, side, user);
const amount = std.mem.readInt(u16, data[8..10], .big); const amount = try reader.readInt(u16);
var item: ?Item = null; var item: ?Item = null;
if(data.len > 10) { if(reader.remaining.len != 0) {
const zon = ZonElement.parseFromString(main.stackAllocator, null, data[10..]); const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
item = try Item.init(zon); item = try Item.init(zon);
} }
return .{ return .{
.dest = try InventoryAndSlot.read(data[0..8], side, user), .dest = dest,
.item = item, .item = item,
.amount = amount, .amount = amount,
}; };
@ -1509,15 +1476,14 @@ pub const Command = struct { // MARK: Command
} }
} }
fn serialize(self: DepositOrDrop, data: *main.List(u8)) void { fn serialize(self: DepositOrDrop, writer: *utils.BinaryWriter) void {
std.mem.writeInt(u32, data.addMany(4)[0..4], self.dest.id, .big); writer.writeInt(u32, self.dest.id);
std.mem.writeInt(u32, data.addMany(4)[0..4], self.source.id, .big); writer.writeInt(u32, self.source.id);
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !DepositOrDrop { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !DepositOrDrop {
if(data.len != 8) return error.Invalid; const destId = try reader.readInt(u32);
const destId = std.mem.readInt(u32, data[0..4], .big); const sourceId = try reader.readInt(u32);
const sourceId = std.mem.readInt(u32, data[4..8], .big);
return .{ return .{
.dest = Sync.getInventory(destId, side, user) orelse return error.Invalid, .dest = Sync.getInventory(destId, side, user) orelse return error.Invalid,
.source = Sync.getInventory(sourceId, side, user) orelse return error.Invalid, .source = Sync.getInventory(sourceId, side, user) orelse return error.Invalid,
@ -1543,13 +1509,12 @@ pub const Command = struct { // MARK: Command
} }
} }
fn serialize(self: Clear, data: *main.List(u8)) void { fn serialize(self: Clear, writer: *utils.BinaryWriter) void {
std.mem.writeInt(u32, data.addMany(4)[0..4], self.inv.id, .big); writer.writeInt(u32, self.inv.id);
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Clear { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Clear {
if(data.len != 4) return error.Invalid; const invId = try reader.readInt(u32);
const invId = std.mem.readInt(u32, data[0..4], .big);
return .{ return .{
.inv = Sync.getInventory(invId, side, user) orelse return error.Invalid, .inv = Sync.getInventory(invId, side, user) orelse return error.Invalid,
}; };
@ -1639,26 +1604,25 @@ pub const Command = struct { // MARK: Command
} }
} }
fn serialize(self: UpdateBlock, data: *main.List(u8)) void { fn serialize(self: UpdateBlock, writer: *utils.BinaryWriter) void {
self.source.write(data.addMany(8)[0..8]); self.source.write(writer);
std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[0], .big); writer.writeInt(i32, self.pos[0]);
std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[1], .big); writer.writeInt(i32, self.pos[1]);
std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[2], .big); writer.writeInt(i32, self.pos[2]);
std.mem.writeInt(u32, data.addMany(4)[0..4], @as(u32, @bitCast(self.oldBlock)), .big); writer.writeInt(u32, @as(u32, @bitCast(self.oldBlock)));
std.mem.writeInt(u32, data.addMany(4)[0..4], @as(u32, @bitCast(self.newBlock)), .big); writer.writeInt(u32, @as(u32, @bitCast(self.newBlock)));
} }
fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !UpdateBlock { fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !UpdateBlock {
if(data.len != 28) return error.Invalid;
return .{ return .{
.source = try InventoryAndSlot.read(data[0..8], side, user), .source = try InventoryAndSlot.read(reader, side, user),
.pos = .{ .pos = .{
std.mem.readInt(i32, data[8..12], .big), try reader.readInt(i32),
std.mem.readInt(i32, data[12..16], .big), try reader.readInt(i32),
std.mem.readInt(i32, data[16..20], .big), try reader.readInt(i32),
}, },
.oldBlock = @bitCast(std.mem.readInt(u32, data[20..24], .big)), .oldBlock = @bitCast(try reader.readInt(u32)),
.newBlock = @bitCast(std.mem.readInt(u32, data[24..28], .big)), .newBlock = @bitCast(try reader.readInt(u32)),
}; };
} }
}; };
@ -1696,19 +1660,17 @@ pub const Command = struct { // MARK: Command
}}, side); }}, side);
} }
fn serialize(self: AddHealth, data: *main.List(u8)) void { fn serialize(self: AddHealth, writer: *utils.BinaryWriter) void {
std.mem.writeInt(u32, data.addMany(4)[0..4], self.target, .big); writer.writeInt(u32, self.target);
std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(self.health), .big); writer.writeInt(u32, @bitCast(self.health));
data.append(@intFromEnum(self.cause)); writer.writeEnum(main.game.DamageType, self.cause);
} }
fn deserialize(data: []const u8, _: Side, _: ?*main.server.User) !AddHealth { fn deserialize(reader: *utils.BinaryReader, _: Side, _: ?*main.server.User) !AddHealth {
if(data.len != 9) return error.Invalid;
return .{ return .{
.target = std.mem.readInt(u32, data[0..4], .big), .target = try reader.readInt(u32),
.health = @bitCast(std.mem.readInt(u32, data[4..8], .big)), .health = @bitCast(try reader.readInt(u32)),
.cause = @enumFromInt(data[8]), .cause = try reader.readEnum(main.game.DamageType),
}; };
} }
}; };

View File

@ -21,6 +21,8 @@ const Vec3f = vec.Vec3f;
const Vec3i = vec.Vec3i; const Vec3i = vec.Vec3i;
const NeverFailingAllocator = main.utils.NeverFailingAllocator; const NeverFailingAllocator = main.utils.NeverFailingAllocator;
pub const networkEndian: std.builtin.Endian = .big;
//TODO: Might want to use SSL or something similar to encode the message //TODO: Might want to use SSL or something similar to encode the message
const Socket = struct { const Socket = struct {
@ -632,7 +634,7 @@ const UnconfirmedPacket = struct {
pub var bytesReceived: [256]Atomic(usize) = .{Atomic(usize).init(0)} ** 256; pub var bytesReceived: [256]Atomic(usize) = .{Atomic(usize).init(0)} ** 256;
pub var packetsReceived: [256]Atomic(usize) = .{Atomic(usize).init(0)} ** 256; pub var packetsReceived: [256]Atomic(usize) = .{Atomic(usize).init(0)} ** 256;
pub const Protocols = struct { pub const Protocols = struct {
pub var list: [256]?*const fn(*Connection, []const u8) anyerror!void = [_]?*const fn(*Connection, []const u8) anyerror!void{null} ** 256; pub var list: [256]?*const fn(*Connection, *utils.BinaryReader) anyerror!void = [_]?*const fn(*Connection, *utils.BinaryReader) anyerror!void{null} ** 256;
pub var isAsynchronous: [256]bool = .{false} ** 256; pub var isAsynchronous: [256]bool = .{false} ** 256;
pub const keepAlive: u8 = 0; pub const keepAlive: u8 = 0;
@ -646,12 +648,13 @@ pub const Protocols = struct {
const stepServerData: u8 = 3; const stepServerData: u8 = 3;
pub const stepComplete: u8 = 255; pub const stepComplete: u8 = 255;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
if(conn.handShakeState.load(.monotonic) < data[0]) { const newState = try reader.readInt(u8);
conn.handShakeState.store(data[0], .monotonic); if(conn.handShakeState.load(.monotonic) < newState) {
switch(data[0]) { conn.handShakeState.store(newState, .monotonic);
switch(newState) {
stepUserData => { stepUserData => {
const zon = ZonElement.parseFromString(main.stackAllocator, null, data[1..]); const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
const name = zon.get([]const u8, "name", "unnamed"); const name = zon.get([]const u8, "name", "unnamed");
if(name.len > 500 or main.graphics.TextBuffer.Parser.countVisibleCharacters(name) > 50) { if(name.len > 500 or main.graphics.TextBuffer.Parser.countVisibleCharacters(name) > 50) {
@ -696,10 +699,10 @@ pub const Protocols = struct {
std.fs.cwd().deleteTree("serverAssets") catch {}; // Delete old assets. std.fs.cwd().deleteTree("serverAssets") catch {}; // Delete old assets.
var dir = try std.fs.cwd().makeOpenPath("serverAssets", .{}); var dir = try std.fs.cwd().makeOpenPath("serverAssets", .{});
defer dir.close(); defer dir.close();
try utils.Compression.unpack(dir, data[1..]); try utils.Compression.unpack(dir, reader.remaining);
}, },
stepServerData => { stepServerData => {
const zon = ZonElement.parseFromString(main.stackAllocator, null, data[1..]); const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
try conn.manager.world.?.finishHandshake(zon); try conn.manager.world.?.finishHandshake(zon);
conn.handShakeState.store(stepComplete, .monotonic); conn.handShakeState.store(stepComplete, .monotonic);
@ -707,7 +710,7 @@ pub const Protocols = struct {
}, },
stepComplete => {}, stepComplete => {},
else => { else => {
std.log.err("Unknown state in HandShakeProtocol {}", .{data[0]}); std.log.err("Unknown state in HandShakeProtocol {}", .{newState});
}, },
} }
} else { } else {
@ -738,66 +741,63 @@ pub const Protocols = struct {
pub const chunkRequest = struct { pub const chunkRequest = struct {
pub const id: u8 = 2; pub const id: u8 = 2;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
var remaining = data[0..];
const basePosition = Vec3i{ const basePosition = Vec3i{
std.mem.readInt(i32, remaining[0..4], .big), try reader.readInt(i32),
std.mem.readInt(i32, remaining[4..8], .big), try reader.readInt(i32),
std.mem.readInt(i32, remaining[8..12], .big), try reader.readInt(i32),
}; };
conn.user.?.clientUpdatePos = basePosition; conn.user.?.clientUpdatePos = basePosition;
conn.user.?.renderDistance = std.mem.readInt(u16, remaining[12..14], .big); conn.user.?.renderDistance = try reader.readInt(u16);
remaining = remaining[14..]; while(reader.remaining.len >= 4) {
while(remaining.len >= 4) { const x: i32 = try reader.readInt(i8);
const voxelSizeShift: u5 = @intCast(remaining[3]); const y: i32 = try reader.readInt(i8);
const z: i32 = try reader.readInt(i8);
const voxelSizeShift: u5 = try reader.readInt(u5);
const positionMask = ~((@as(i32, 1) << voxelSizeShift + chunk.chunkShift) - 1); const positionMask = ~((@as(i32, 1) << voxelSizeShift + chunk.chunkShift) - 1);
const request = chunk.ChunkPosition{ const request = chunk.ChunkPosition{
.wx = (@as(i32, @as(i8, @bitCast(remaining[0]))) << voxelSizeShift + chunk.chunkShift) +% (basePosition[0] & positionMask), .wx = (x << voxelSizeShift + chunk.chunkShift) +% (basePosition[0] & positionMask),
.wy = (@as(i32, @as(i8, @bitCast(remaining[1]))) << voxelSizeShift + chunk.chunkShift) +% (basePosition[1] & positionMask), .wy = (y << voxelSizeShift + chunk.chunkShift) +% (basePosition[1] & positionMask),
.wz = (@as(i32, @as(i8, @bitCast(remaining[2]))) << voxelSizeShift + chunk.chunkShift) +% (basePosition[2] & positionMask), .wz = (z << voxelSizeShift + chunk.chunkShift) +% (basePosition[2] & positionMask),
.voxelSize = @as(u31, 1) << voxelSizeShift, .voxelSize = @as(u31, 1) << voxelSizeShift,
}; };
if(conn.user) |user| { if(conn.user) |user| {
user.increaseRefCount(); user.increaseRefCount();
main.server.world.?.queueChunkAndDecreaseRefCount(request, user); main.server.world.?.queueChunkAndDecreaseRefCount(request, user);
} }
remaining = remaining[4..];
} }
} }
pub fn sendRequest(conn: *Connection, requests: []chunk.ChunkPosition, basePosition: Vec3i, renderDistance: u16) void { pub fn sendRequest(conn: *Connection, requests: []chunk.ChunkPosition, basePosition: Vec3i, renderDistance: u16) void {
if(requests.len == 0) return; if(requests.len == 0) return;
const data = main.stackAllocator.alloc(u8, 14 + 4*requests.len); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 14 + 4*requests.len);
defer main.stackAllocator.free(data); defer writer.deinit();
var remaining = data; writer.writeInt(i32, basePosition[0]);
std.mem.writeInt(i32, remaining[0..4], basePosition[0], .big); writer.writeInt(i32, basePosition[1]);
std.mem.writeInt(i32, remaining[4..8], basePosition[1], .big); writer.writeInt(i32, basePosition[2]);
std.mem.writeInt(i32, remaining[8..12], basePosition[2], .big); writer.writeInt(u16, renderDistance);
std.mem.writeInt(u16, remaining[12..14], renderDistance, .big);
remaining = remaining[14..];
for(requests) |req| { for(requests) |req| {
const voxelSizeShift: u5 = std.math.log2_int(u31, req.voxelSize); const voxelSizeShift: u5 = std.math.log2_int(u31, req.voxelSize);
const positionMask = ~((@as(i32, 1) << voxelSizeShift + chunk.chunkShift) - 1); const positionMask = ~((@as(i32, 1) << voxelSizeShift + chunk.chunkShift) - 1);
remaining[0] = @bitCast(@as(i8, @intCast((req.wx -% (basePosition[0] & positionMask)) >> voxelSizeShift + chunk.chunkShift))); writer.writeInt(i8, @intCast((req.wx -% (basePosition[0] & positionMask)) >> voxelSizeShift + chunk.chunkShift));
remaining[1] = @bitCast(@as(i8, @intCast((req.wy -% (basePosition[1] & positionMask)) >> voxelSizeShift + chunk.chunkShift))); writer.writeInt(i8, @intCast((req.wy -% (basePosition[1] & positionMask)) >> voxelSizeShift + chunk.chunkShift));
remaining[2] = @bitCast(@as(i8, @intCast((req.wz -% (basePosition[2] & positionMask)) >> voxelSizeShift + chunk.chunkShift))); writer.writeInt(i8, @intCast((req.wz -% (basePosition[2] & positionMask)) >> voxelSizeShift + chunk.chunkShift));
remaining[3] = voxelSizeShift; writer.writeInt(u5, voxelSizeShift);
remaining = remaining[4..];
} }
conn.sendImportant(id, data); conn.sendImportant(id, writer.data.items);
} }
}; };
pub const chunkTransmission = struct { pub const chunkTransmission = struct {
pub const id: u8 = 3; pub const id: u8 = 3;
pub const asynchronous = true; pub const asynchronous = true;
fn receive(_: *Connection, data: []const u8) !void { fn receive(_: *Connection, reader: *utils.BinaryReader) !void {
const pos = chunk.ChunkPosition{ const pos = chunk.ChunkPosition{
.wx = std.mem.readInt(i32, data[0..4], .big), .wx = try reader.readInt(i32),
.wy = std.mem.readInt(i32, data[4..8], .big), .wy = try reader.readInt(i32),
.wz = std.mem.readInt(i32, data[8..12], .big), .wz = try reader.readInt(i32),
.voxelSize = @intCast(std.mem.readInt(i32, data[12..16], .big)), .voxelSize = try reader.readInt(u31),
}; };
const ch = chunk.Chunk.init(pos); const ch = chunk.Chunk.init(pos);
try main.server.storage.ChunkCompression.decompressChunk(ch, data[16..]); try main.server.storage.ChunkCompression.decompressChunk(ch, reader.remaining);
renderer.mesh_storage.updateChunkMesh(ch); renderer.mesh_storage.updateChunkMesh(ch);
} }
fn sendChunkOverTheNetwork(conn: *Connection, ch: *chunk.ServerChunk) void { fn sendChunkOverTheNetwork(conn: *Connection, ch: *chunk.ServerChunk) void {
@ -805,14 +805,14 @@ pub const Protocols = struct {
const chunkData = main.server.storage.ChunkCompression.compressChunk(main.stackAllocator, &ch.super, ch.super.pos.voxelSize != 1); const chunkData = main.server.storage.ChunkCompression.compressChunk(main.stackAllocator, &ch.super, ch.super.pos.voxelSize != 1);
ch.mutex.unlock(); ch.mutex.unlock();
defer main.stackAllocator.free(chunkData); defer main.stackAllocator.free(chunkData);
const data = main.stackAllocator.alloc(u8, chunkData.len + 16); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, chunkData.len + 16);
defer main.stackAllocator.free(data); defer writer.deinit();
std.mem.writeInt(i32, data[0..4], ch.super.pos.wx, .big); writer.writeInt(i32, ch.super.pos.wx);
std.mem.writeInt(i32, data[4..8], ch.super.pos.wy, .big); writer.writeInt(i32, ch.super.pos.wy);
std.mem.writeInt(i32, data[8..12], ch.super.pos.wz, .big); writer.writeInt(i32, ch.super.pos.wz);
std.mem.writeInt(i32, data[12..16], ch.super.pos.voxelSize, .big); writer.writeInt(u31, ch.super.pos.voxelSize);
@memcpy(data[16..], chunkData); writer.writeSlice(chunkData);
conn.sendImportant(id, data); conn.sendImportant(id, writer.data.items);
} }
fn sendChunkLocally(ch: *chunk.ServerChunk) void { fn sendChunkLocally(ch: *chunk.ServerChunk) void {
const chunkCopy = chunk.Chunk.init(ch.super.pos); const chunkCopy = chunk.Chunk.init(ch.super.pos);
@ -831,8 +831,8 @@ pub const Protocols = struct {
pub const playerPosition = struct { pub const playerPosition = struct {
pub const id: u8 = 4; pub const id: u8 = 4;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
conn.user.?.receiveData(data); conn.user.?.receiveData(reader.remaining);
} }
var lastPositionSent: u16 = 0; var lastPositionSent: u16 = 0;
pub fn send(conn: *Connection, playerPos: Vec3d, playerVel: Vec3d, time: u16) void { pub fn send(conn: *Connection, playerPos: Vec3d, playerVel: Vec3d, time: u16) void {
@ -840,24 +840,25 @@ pub const Protocols = struct {
return; // Only send at most once every 50 ms. return; // Only send at most once every 50 ms.
} }
lastPositionSent = time; lastPositionSent = time;
var data: [62]u8 = undefined; var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 62);
std.mem.writeInt(u64, data[0..8], @as(u64, @bitCast(playerPos[0])), .big); defer writer.deinit();
std.mem.writeInt(u64, data[8..16], @as(u64, @bitCast(playerPos[1])), .big); writer.writeInt(u64, @bitCast(playerPos[0]));
std.mem.writeInt(u64, data[16..24], @as(u64, @bitCast(playerPos[2])), .big); writer.writeInt(u64, @bitCast(playerPos[1]));
std.mem.writeInt(u64, data[24..32], @as(u64, @bitCast(playerVel[0])), .big); writer.writeInt(u64, @bitCast(playerPos[2]));
std.mem.writeInt(u64, data[32..40], @as(u64, @bitCast(playerVel[1])), .big); writer.writeInt(u64, @bitCast(playerVel[0]));
std.mem.writeInt(u64, data[40..48], @as(u64, @bitCast(playerVel[2])), .big); writer.writeInt(u64, @bitCast(playerVel[1]));
std.mem.writeInt(u32, data[48..52], @as(u32, @bitCast(game.camera.rotation[0])), .big); writer.writeInt(u64, @bitCast(playerVel[2]));
std.mem.writeInt(u32, data[52..56], @as(u32, @bitCast(game.camera.rotation[1])), .big); writer.writeInt(u32, @bitCast(game.camera.rotation[0]));
std.mem.writeInt(u32, data[56..60], @as(u32, @bitCast(game.camera.rotation[2])), .big); writer.writeInt(u32, @bitCast(game.camera.rotation[1]));
std.mem.writeInt(u16, data[60..62], time, .big); writer.writeInt(u32, @bitCast(game.camera.rotation[2]));
conn.sendUnimportant(id, &data); writer.writeInt(u16, time);
conn.sendUnimportant(id, writer.data.items);
} }
}; };
pub const disconnect = struct { pub const disconnect = struct {
pub const id: u8 = 5; pub const id: u8 = 5;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, _: []const u8) !void { fn receive(conn: *Connection, _: *utils.BinaryReader) !void {
conn.disconnect(); conn.disconnect();
if(conn.user) |user| { if(conn.user) |user| {
main.server.disconnect(user); main.server.disconnect(user);
@ -875,44 +876,45 @@ pub const Protocols = struct {
pub const asynchronous = false; pub const asynchronous = false;
const type_entity: u8 = 0; const type_entity: u8 = 0;
const type_item: u8 = 1; const type_item: u8 = 1;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
if(conn.manager.world) |world| { if(conn.manager.world) |world| {
const time = std.mem.readInt(i16, data[1..3], .big); const typ = try reader.readInt(u8);
if(data[0] == type_entity) { const time = try reader.readInt(i16);
main.entity.ClientEntityManager.serverUpdate(time, data[3..]); if(typ == type_entity) {
} else if(data[0] == type_item) { main.entity.ClientEntityManager.serverUpdate(time, reader.remaining);
world.itemDrops.readPosition(data[3..], time); } else if(typ == type_item) {
world.itemDrops.readPosition(reader.remaining, time);
} }
} }
} }
pub fn send(conn: *Connection, entityData: []const u8, itemData: []const u8) void { pub fn send(conn: *Connection, entityData: []const u8, itemData: []const u8) void {
if(entityData.len != 0) { if(entityData.len != 0) {
const fullEntityData = main.stackAllocator.alloc(u8, entityData.len + 3); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, entityData.len + 3);
defer main.stackAllocator.free(fullEntityData); defer writer.deinit();
fullEntityData[0] = type_entity; writer.writeInt(u8, type_entity);
std.mem.writeInt(i16, fullEntityData[1..3], @as(i16, @truncate(std.time.milliTimestamp())), .big); writer.writeInt(i16, @truncate(std.time.milliTimestamp()));
@memcpy(fullEntityData[3..], entityData); writer.writeSlice(entityData);
conn.sendUnimportant(id, fullEntityData); conn.sendUnimportant(id, writer.data.items);
} }
if(itemData.len != 0) { if(itemData.len != 0) {
const fullItemData = main.stackAllocator.alloc(u8, itemData.len + 3); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, itemData.len + 3);
defer main.stackAllocator.free(fullItemData); defer writer.deinit();
fullItemData[0] = type_item; writer.writeInt(u8, type_item);
std.mem.writeInt(i16, fullItemData[1..3], @as(i16, @truncate(std.time.milliTimestamp())), .big); writer.writeInt(i16, @truncate(std.time.milliTimestamp()));
@memcpy(fullItemData[3..], itemData); writer.writeSlice(itemData);
conn.sendUnimportant(id, fullItemData); conn.sendUnimportant(id, writer.data.items);
} }
} }
}; };
pub const blockUpdate = struct { pub const blockUpdate = struct {
pub const id: u8 = 7; pub const id: u8 = 7;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
const x = std.mem.readInt(i32, data[0..4], .big); const x = try reader.readInt(i32);
const y = std.mem.readInt(i32, data[4..8], .big); const y = try reader.readInt(i32);
const z = std.mem.readInt(i32, data[8..12], .big); const z = try reader.readInt(i32);
const newBlock = Block.fromInt(std.mem.readInt(u32, data[12..16], .big)); const newBlock = Block.fromInt(try reader.readInt(u32));
if(conn.user != null) { if(conn.user != null) {
return error.InvalidPacket; return error.InvalidPacket;
} else { } else {
@ -920,19 +922,20 @@ pub const Protocols = struct {
} }
} }
pub fn send(conn: *Connection, x: i32, y: i32, z: i32, newBlock: Block) void { pub fn send(conn: *Connection, x: i32, y: i32, z: i32, newBlock: Block) void {
var data: [16]u8 = undefined; var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 16);
std.mem.writeInt(i32, data[0..4], x, .big); defer writer.deinit();
std.mem.writeInt(i32, data[4..8], y, .big); writer.writeInt(i32, x);
std.mem.writeInt(i32, data[8..12], z, .big); writer.writeInt(i32, y);
std.mem.writeInt(u32, data[12..16], newBlock.toInt(), .big); writer.writeInt(i32, z);
conn.sendImportant(id, &data); writer.writeInt(u32, newBlock.toInt());
conn.sendImportant(id, writer.data.items);
} }
}; };
pub const entity = struct { pub const entity = struct {
pub const id: u8 = 8; pub const id: u8 = 8;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
const zonArray = ZonElement.parseFromString(main.stackAllocator, null, data); const zonArray = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining);
defer zonArray.deinit(main.stackAllocator); defer zonArray.deinit(main.stackAllocator);
var i: u32 = 0; var i: u32 = 0;
while(i < zonArray.array.items.len) : (i += 1) { while(i < zonArray.array.items.len) : (i += 1) {
@ -949,7 +952,7 @@ pub const Protocols = struct {
break; break;
}, },
else => { else => {
std.log.err("Unrecognized zon parameters for protocol {}: {s}", .{id, data}); std.log.err("Unrecognized zon parameters for protocol {}: {s}", .{id, reader.remaining});
}, },
} }
} }
@ -980,18 +983,17 @@ pub const Protocols = struct {
const type_reserved5: u8 = 6; const type_reserved5: u8 = 6;
const type_reserved6: u8 = 7; const type_reserved6: u8 = 7;
const type_timeAndBiome: u8 = 8; const type_timeAndBiome: u8 = 8;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
switch(data[0]) { switch(try reader.readInt(u8)) {
type_gamemode => { type_gamemode => {
if(conn.user != null) return error.InvalidPacket; if(conn.user != null) return error.InvalidPacket;
if(data.len != 2) return error.InvalidPacket; main.items.Inventory.Sync.setGamemode(null, try reader.readEnum(main.game.Gamemode));
main.items.Inventory.Sync.setGamemode(null, @enumFromInt(data[1]));
}, },
type_teleport => { type_teleport => {
game.Player.setPosBlocking(Vec3d{ game.Player.setPosBlocking(Vec3d{
@bitCast(std.mem.readInt(u64, data[1..9], .big)), @bitCast(try reader.readInt(u64)),
@bitCast(std.mem.readInt(u64, data[9..17], .big)), @bitCast(try reader.readInt(u64)),
@bitCast(std.mem.readInt(u64, data[17..25], .big)), @bitCast(try reader.readInt(u64)),
}); });
}, },
type_cure => { type_cure => {
@ -1004,7 +1006,7 @@ pub const Protocols = struct {
type_reserved6 => {}, type_reserved6 => {},
type_timeAndBiome => { type_timeAndBiome => {
if(conn.manager.world) |world| { if(conn.manager.world) |world| {
const zon = ZonElement.parseFromString(main.stackAllocator, null, data[1..]); const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining);
defer zon.deinit(main.stackAllocator); defer zon.deinit(main.stackAllocator);
const expectedTime = zon.get(i64, "time", 0); const expectedTime = zon.get(i64, "time", 0);
var curTime = world.gameTime.load(.monotonic); var curTime = world.gameTime.load(.monotonic);
@ -1027,7 +1029,8 @@ pub const Protocols = struct {
} }
}, },
else => |unrecognizedType| { else => |unrecognizedType| {
std.log.err("Unrecognized type for genericUpdateProtocol: {}. Data: {any}", .{unrecognizedType, data}); std.log.err("Unrecognized type for genericUpdateProtocol: {}. Data: {any}", .{unrecognizedType, reader.remaining});
return error.Invalid;
}, },
} }
} }
@ -1053,12 +1056,13 @@ pub const Protocols = struct {
} }
pub fn sendTPCoordinates(conn: *Connection, pos: Vec3d) void { pub fn sendTPCoordinates(conn: *Connection, pos: Vec3d) void {
var data: [1 + 24]u8 = undefined; var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 25);
data[0] = type_teleport; defer writer.deinit();
std.mem.writeInt(u64, data[1..9], @as(u64, @bitCast(pos[0])), .big); writer.writeInt(u8, type_teleport);
std.mem.writeInt(u64, data[9..17], @as(u64, @bitCast(pos[1])), .big); writer.writeInt(u64, @bitCast(pos[0]));
std.mem.writeInt(u64, data[17..25], @as(u64, @bitCast(pos[2])), .big); writer.writeInt(u64, @bitCast(pos[1]));
conn.sendImportant(id, &data); writer.writeInt(u64, @bitCast(pos[2]));
conn.sendImportant(id, writer.data.items);
} }
pub fn sendCure(conn: *Connection) void { pub fn sendCure(conn: *Connection) void {
@ -1081,131 +1085,135 @@ pub const Protocols = struct {
pub const chat = struct { pub const chat = struct {
pub const id: u8 = 10; pub const id: u8 = 10;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
const msg = reader.remaining;
if(conn.user) |user| { if(conn.user) |user| {
if(data.len > 10000 or main.graphics.TextBuffer.Parser.countVisibleCharacters(data) > 1000) { if(msg.len > 10000 or main.graphics.TextBuffer.Parser.countVisibleCharacters(msg) > 1000) {
std.log.err("Received too long chat message with {}/{} characters.", .{main.graphics.TextBuffer.Parser.countVisibleCharacters(data), data.len}); std.log.err("Received too long chat message with {}/{} characters.", .{main.graphics.TextBuffer.Parser.countVisibleCharacters(msg), msg.len});
return error.Invalid; return error.Invalid;
} }
main.server.messageFrom(data, user); main.server.messageFrom(msg, user);
} else { } else {
main.gui.windowlist.chat.addMessage(data); main.gui.windowlist.chat.addMessage(msg);
} }
} }
pub fn send(conn: *Connection, data: []const u8) void { pub fn send(conn: *Connection, msg: []const u8) void {
conn.sendImportant(id, data); conn.sendImportant(id, msg);
} }
}; };
pub const lightMapRequest = struct { pub const lightMapRequest = struct {
pub const id: u8 = 11; pub const id: u8 = 11;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
var remaining = data[0..]; while(reader.remaining.len >= 9) {
while(remaining.len >= 9) { const wx = try reader.readInt(i32);
const wy = try reader.readInt(i32);
const voxelSizeShift = try reader.readInt(u5);
const request = main.server.terrain.SurfaceMap.MapFragmentPosition{ const request = main.server.terrain.SurfaceMap.MapFragmentPosition{
.wx = std.mem.readInt(i32, remaining[0..4], .big), .wx = wx,
.wy = std.mem.readInt(i32, remaining[4..8], .big), .wy = wy,
.voxelSize = @as(u31, 1) << @intCast(std.mem.readInt(u8, remaining[8..9], .big)), .voxelSize = @as(u31, 1) << voxelSizeShift,
.voxelSizeShift = @intCast(std.mem.readInt(u8, remaining[8..9], .big)), .voxelSizeShift = voxelSizeShift,
}; };
if(conn.user) |user| { if(conn.user) |user| {
user.increaseRefCount(); user.increaseRefCount();
main.server.world.?.queueLightMapAndDecreaseRefCount(request, user); main.server.world.?.queueLightMapAndDecreaseRefCount(request, user);
} }
remaining = remaining[9..];
} }
} }
pub fn sendRequest(conn: *Connection, requests: []main.server.terrain.SurfaceMap.MapFragmentPosition) void { pub fn sendRequest(conn: *Connection, requests: []main.server.terrain.SurfaceMap.MapFragmentPosition) void {
if(requests.len == 0) return; if(requests.len == 0) return;
const data = main.stackAllocator.alloc(u8, 9*requests.len); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 9*requests.len);
defer main.stackAllocator.free(data); defer writer.deinit();
var remaining = data;
for(requests) |req| { for(requests) |req| {
std.mem.writeInt(i32, remaining[0..4], req.wx, .big); writer.writeInt(i32, req.wx);
std.mem.writeInt(i32, remaining[4..8], req.wy, .big); writer.writeInt(i32, req.wy);
std.mem.writeInt(u8, remaining[8..9], req.voxelSizeShift, .big); writer.writeInt(u8, req.voxelSizeShift);
remaining = remaining[9..];
} }
conn.sendImportant(id, data); conn.sendImportant(id, writer.data.items);
} }
}; };
pub const lightMapTransmission = struct { pub const lightMapTransmission = struct {
pub const id: u8 = 12; pub const id: u8 = 12;
pub const asynchronous = true; pub const asynchronous = true;
fn receive(_: *Connection, _data: []const u8) !void { fn receive(_: *Connection, reader: *utils.BinaryReader) !void {
var data = _data; const wx = try reader.readInt(i32);
const wy = try reader.readInt(i32);
const voxelSizeShift = try reader.readInt(u5);
const pos = main.server.terrain.SurfaceMap.MapFragmentPosition{ const pos = main.server.terrain.SurfaceMap.MapFragmentPosition{
.wx = std.mem.readInt(i32, data[0..4], .big), .wx = wx,
.wy = std.mem.readInt(i32, data[4..8], .big), .wy = wy,
.voxelSize = @as(u31, 1) << @intCast(std.mem.readInt(u8, data[8..9], .big)), .voxelSize = @as(u31, 1) << voxelSizeShift,
.voxelSizeShift = @intCast(std.mem.readInt(u8, data[8..9], .big)), .voxelSizeShift = voxelSizeShift,
}; };
const _inflatedData = main.stackAllocator.alloc(u8, main.server.terrain.LightMap.LightMapFragment.mapSize*main.server.terrain.LightMap.LightMapFragment.mapSize*2); const _inflatedData = main.stackAllocator.alloc(u8, main.server.terrain.LightMap.LightMapFragment.mapSize*main.server.terrain.LightMap.LightMapFragment.mapSize*2);
defer main.stackAllocator.free(_inflatedData); defer main.stackAllocator.free(_inflatedData);
const _inflatedLen = try utils.Compression.inflateTo(_inflatedData, data[9..]); const _inflatedLen = try utils.Compression.inflateTo(_inflatedData, reader.remaining);
if(_inflatedLen != main.server.terrain.LightMap.LightMapFragment.mapSize*main.server.terrain.LightMap.LightMapFragment.mapSize*2) { if(_inflatedLen != main.server.terrain.LightMap.LightMapFragment.mapSize*main.server.terrain.LightMap.LightMapFragment.mapSize*2) {
std.log.err("Transmission of light map has invalid size: {}. Input data: {any}, After inflate: {any}", .{_inflatedLen, data, _inflatedData[0.._inflatedLen]}); std.log.err("Transmission of light map has invalid size: {}. Input data: {any}, After inflate: {any}", .{_inflatedLen, reader.remaining, _inflatedData[0.._inflatedLen]});
return error.Invalid;
} }
data = _inflatedData; var ligthMapReader = utils.BinaryReader.init(_inflatedData, networkEndian);
const map = main.globalAllocator.create(main.server.terrain.LightMap.LightMapFragment); const map = main.globalAllocator.create(main.server.terrain.LightMap.LightMapFragment);
map.init(pos.wx, pos.wy, pos.voxelSize); map.init(pos.wx, pos.wy, pos.voxelSize);
_ = map.refCount.fetchAdd(1, .monotonic); _ = map.refCount.fetchAdd(1, .monotonic);
for(&map.startHeight) |*val| { for(&map.startHeight) |*val| {
val.* = std.mem.readInt(i16, data[0..2], .big); val.* = try ligthMapReader.readInt(i16);
data = data[2..];
} }
renderer.mesh_storage.updateLightMap(map); renderer.mesh_storage.updateLightMap(map);
} }
pub fn sendLightMap(conn: *Connection, map: *main.server.terrain.LightMap.LightMapFragment) void { pub fn sendLightMap(conn: *Connection, map: *main.server.terrain.LightMap.LightMapFragment) void {
var uncompressedData: [@sizeOf(@TypeOf(map.startHeight))]u8 = undefined; // TODO: #15280 var ligthMapWriter = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, @sizeOf(@TypeOf(map.startHeight)));
for(&map.startHeight, 0..) |val, i| { defer ligthMapWriter.deinit();
std.mem.writeInt(i16, uncompressedData[2*i ..][0..2], val, .big); for(&map.startHeight) |val| {
ligthMapWriter.writeInt(i16, val);
} }
const compressedData = utils.Compression.deflate(main.stackAllocator, &uncompressedData, .default); const compressedData = utils.Compression.deflate(main.stackAllocator, ligthMapWriter.data.items, .default);
defer main.stackAllocator.free(compressedData); defer main.stackAllocator.free(compressedData);
const data = main.stackAllocator.alloc(u8, 9 + compressedData.len); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 9 + compressedData.len);
defer main.stackAllocator.free(data); defer writer.deinit();
@memcpy(data[9..], compressedData); writer.writeInt(i32, map.pos.wx);
std.mem.writeInt(i32, data[0..4], map.pos.wx, .big); writer.writeInt(i32, map.pos.wy);
std.mem.writeInt(i32, data[4..8], map.pos.wy, .big); writer.writeInt(u8, map.pos.voxelSizeShift);
std.mem.writeInt(u8, data[8..9], map.pos.voxelSizeShift, .big); writer.writeSlice(compressedData);
conn.sendImportant(id, data); conn.sendImportant(id, writer.data.items);
} }
}; };
pub const inventory = struct { pub const inventory = struct {
pub const id: u8 = 13; pub const id: u8 = 13;
pub const asynchronous = false; pub const asynchronous = false;
fn receive(conn: *Connection, data: []const u8) !void { fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
if(conn.user) |user| { if(conn.user) |user| {
if(data[0] == 0xff) return error.InvalidPacket; if(reader.remaining[0] == 0xff) return error.InvalidPacket;
try items.Inventory.Sync.ServerSide.receiveCommand(user, data); try items.Inventory.Sync.ServerSide.receiveCommand(user, reader);
} else { } else {
if(data[0] == 0xff) { // Confirmation const typ = try reader.readInt(u8);
items.Inventory.Sync.ClientSide.receiveConfirmation(data[1..]); if(typ == 0xff) { // Confirmation
} else if(data[0] == 0xfe) { // Failure try items.Inventory.Sync.ClientSide.receiveConfirmation(reader);
} else if(typ == 0xfe) { // Failure
items.Inventory.Sync.ClientSide.receiveFailure(); items.Inventory.Sync.ClientSide.receiveFailure();
} else { } else {
try items.Inventory.Sync.ClientSide.receiveSyncOperation(data[1..]); try items.Inventory.Sync.ClientSide.receiveSyncOperation(reader);
} }
} }
} }
pub fn sendCommand(conn: *Connection, payloadType: items.Inventory.Command.PayloadType, _data: []const u8) void { pub fn sendCommand(conn: *Connection, payloadType: items.Inventory.Command.PayloadType, _data: []const u8) void {
std.debug.assert(conn.user == null); std.debug.assert(conn.user == null);
var data = main.stackAllocator.alloc(u8, _data.len + 1); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, _data.len + 1);
defer main.stackAllocator.free(data); defer writer.deinit();
data[0] = @intFromEnum(payloadType); writer.writeEnum(items.Inventory.Command.PayloadType, payloadType);
std.debug.assert(data[0] != 0xff); std.debug.assert(writer.data.items[0] != 0xff);
@memcpy(data[1..], _data); writer.writeSlice(_data);
conn.sendImportant(id, data); conn.sendImportant(id, writer.data.items);
} }
pub fn sendConfirmation(conn: *Connection, _data: []const u8) void { pub fn sendConfirmation(conn: *Connection, _data: []const u8) void {
std.debug.assert(conn.user != null); std.debug.assert(conn.user != null);
var data = main.stackAllocator.alloc(u8, _data.len + 1); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, _data.len + 1);
defer main.stackAllocator.free(data); defer writer.deinit();
data[0] = 0xff; writer.writeInt(u8, 0xff);
@memcpy(data[1..], _data); writer.writeSlice(_data);
conn.sendImportant(id, data); conn.sendImportant(id, writer.data.items);
} }
pub fn sendFailure(conn: *Connection) void { pub fn sendFailure(conn: *Connection) void {
std.debug.assert(conn.user != null); std.debug.assert(conn.user != null);
@ -1213,11 +1221,11 @@ pub const Protocols = struct {
} }
pub fn sendSyncOperation(conn: *Connection, _data: []const u8) void { pub fn sendSyncOperation(conn: *Connection, _data: []const u8) void {
std.debug.assert(conn.user != null); std.debug.assert(conn.user != null);
var data = main.stackAllocator.alloc(u8, _data.len + 1); var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, _data.len + 1);
defer main.stackAllocator.free(data); defer writer.deinit();
data[0] = 0; writer.writeInt(u8, 0);
@memcpy(data[1..], _data); writer.writeSlice(_data);
conn.sendImportant(id, data); conn.sendImportant(id, writer.data.items);
} }
}; };
}; };
@ -1680,7 +1688,8 @@ pub const Connection = struct { // MARK: Connection
if(Protocols.isAsynchronous[protocol]) { if(Protocols.isAsynchronous[protocol]) {
ProtocolTask.schedule(self, protocol, data); ProtocolTask.schedule(self, protocol, data);
} else { } else {
try prot(self, data); var reader = utils.BinaryReader.init(data, networkEndian);
try prot(self, &reader);
} }
} else { } else {
std.log.err("Received unknown important protocol with id {}", .{protocol}); std.log.err("Received unknown important protocol with id {}", .{protocol});
@ -1721,7 +1730,7 @@ pub const Connection = struct { // MARK: Connection
} }
} }
if(id - @as(i33, self.lastIncompletePacket) >= 65536) { if(id - @as(i33, self.lastIncompletePacket) >= 65536) {
std.log.warn("Many incomplete packages. Cannot process any more packages for now.", .{}); std.log.warn("Many incomplete packets. Cannot process any more packets for now.", .{});
return; return;
} }
self.receivedPackets[0].append(id); self.receivedPackets[0].append(id);
@ -1737,9 +1746,11 @@ pub const Connection = struct { // MARK: Connection
self.receiveKeepAlive(data[1..]); self.receiveKeepAlive(data[1..]);
} else { } else {
if(Protocols.list[protocol]) |prot| { if(Protocols.list[protocol]) |prot| {
try prot(self, data[1..]); var reader = utils.BinaryReader.init(data[1..], networkEndian);
try prot(self, &reader);
} else { } else {
std.log.err("Received unknown protocol with id {}", .{protocol}); std.log.err("Received unknown protocol with id {}", .{protocol});
return error.Invalid;
} }
} }
} }
@ -1791,7 +1802,8 @@ const ProtocolTask = struct {
pub fn run(self: *ProtocolTask) void { pub fn run(self: *ProtocolTask) void {
defer self.clean(); defer self.clean();
Protocols.list[self.protocol].?(self.conn, self.data) catch |err| { var reader = utils.BinaryReader.init(self.data, networkEndian);
Protocols.list[self.protocol].?(self.conn, &reader) catch |err| {
std.log.err("Got error {s} while executing protocol {} with data {any}", .{@errorName(err), self.protocol, self.data}); // TODO: Maybe disconnect on error std.log.err("Got error {s} while executing protocol {} with data {any}", .{@errorName(err), self.protocol, self.data}); // TODO: Maybe disconnect on error
}; };
} }

View File

@ -1843,3 +1843,77 @@ pub const Side = enum {
client, client,
server, server,
}; };
pub const BinaryReader = struct {
remaining: []const u8,
endian: std.builtin.Endian,
pub fn init(data: []const u8, endian: std.builtin.Endian) BinaryReader {
return .{.remaining = data, .endian = endian};
}
pub fn readInt(self: *BinaryReader, T: type) error{OutOfBounds, IntOutOfBounds}!T {
if(@mod(@typeInfo(T).int.bits, 8) != 0) {
const fullBits = comptime std.mem.alignForward(u16, @typeInfo(T).int.bits, 8);
const FullType = std.meta.Int(@typeInfo(T).int.signedness, fullBits);
const val = try self.readInt(FullType);
return std.math.cast(T, val) orelse return error.IntOutOfBounds;
}
const bufSize = @divExact(@typeInfo(T).int.bits, 8);
if(self.remaining.len < bufSize) return error.OutOfBounds;
defer self.remaining = self.remaining[bufSize..];
return std.mem.readInt(T, self.remaining[0..bufSize], self.endian);
}
pub fn readEnum(self: *BinaryReader, T: type) error{OutOfBounds, IntOutOfBounds, InvalidEnumTag}!T {
const int = try self.readInt(@typeInfo(T).@"enum".tag_type);
return std.meta.intToEnum(T, int);
}
pub fn readUntilDelimiter(self: *BinaryReader, comptime delimiter: u8) ![:delimiter]const u8 {
const len = std.mem.indexOfScalar(u8, self.remaining, delimiter) orelse return error.OutOfBounds;
defer self.remaining = self.remaining[len+1..];
return self.remaining[0..len:delimiter];
}
};
pub const BinaryWriter = struct {
data: main.List(u8),
endian: std.builtin.Endian,
pub fn init(allocator: NeverFailingAllocator, endian: std.builtin.Endian) BinaryWriter {
return .{.data = .init(allocator), .endian = endian};
}
pub fn initCapacity(allocator: NeverFailingAllocator, endian: std.builtin.Endian, capacity: usize) BinaryWriter {
return .{.data = .initCapacity(allocator, capacity), .endian = endian};
}
pub fn deinit(self: *BinaryWriter) void {
self.data.deinit();
}
pub fn writeInt(self: *BinaryWriter, T: type, value: T) void {
if(@mod(@typeInfo(T).int.bits, 8) != 0) {
const fullBits = comptime std.mem.alignForward(u16, @typeInfo(T).int.bits, 8);
const FullType = std.meta.Int(@typeInfo(T).int.signedness, fullBits);
return self.writeInt(FullType, value);
}
const bufSize = @divExact(@typeInfo(T).int.bits, 8);
std.mem.writeInt(T, self.data.addMany(bufSize)[0..bufSize], value, self.endian);
}
pub fn writeEnum(self: *BinaryWriter, T: type, value: T) void {
self.writeInt(@typeInfo(T).@"enum".tag_type, @intFromEnum(value));
}
pub fn writeSlice(self: *BinaryWriter, slice: []const u8) void {
self.data.appendSlice(slice);
}
pub fn writeWithDelimiter(self: *BinaryWriter, slice: []const u8, delimiter: u8) void {
std.debug.assert(!std.mem.containsAtLeast(u8, slice, 1, &.{delimiter}));
self.writeSlice(slice);
self.data.append(delimiter);
}
};