diff --git a/src/Inventory.zig b/src/Inventory.zig index 79a52ab2..d352ae39 100644 --- a/src/Inventory.zig +++ b/src/Inventory.zig @@ -6,7 +6,8 @@ const Block = main.blocks.Block; const Item = main.items.Item; const ItemStack = main.items.ItemStack; const Tool = main.items.Tool; -const NeverFailingAllocator = main.utils.NeverFailingAllocator; +const utils = main.utils; +const NeverFailingAllocator = utils.NeverFailingAllocator; const vec = main.vec; const Vec3d = vec.Vec3d; const Vec3f = vec.Vec3f; @@ -35,7 +36,10 @@ pub const Sync = struct { // MARK: Sync pub fn deinit() void { mutex.lock(); 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(); commands.deinit(); @@ -98,10 +102,10 @@ pub const Sync = struct { // MARK: Sync return serverToClientMap.get(serverId); } - pub fn receiveConfirmation(data: []const u8) void { + pub fn receiveConfirmation(reader: *utils.BinaryReader) !void { mutex.lock(); defer mutex.unlock(); - commands.dequeue().?.finalize(main.globalAllocator, .client, data); + try commands.dequeue().?.finalize(main.globalAllocator, .client, reader); } pub fn receiveFailure() void { @@ -116,7 +120,10 @@ pub const Sync = struct { // MARK: Sync } if(tempData.popOrNull()) |_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| { 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(); defer mutex.unlock(); var tempData = main.List(Command).init(main.stackAllocator); @@ -135,7 +142,7 @@ pub const Sync = struct { // MARK: Sync cmd.undo(); tempData.append(cmd); } - try Command.SyncOperation.executeFromData(data); + try Command.SyncOperation.executeFromData(reader); while(tempData.popOrNull()) |_cmd| { var cmd = _cmd; 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(); defer mutex.unlock(); - const typ: Command.PayloadType = @enumFromInt(data[0]); + const typ = try reader.readEnum(Command.PayloadType); @setEvalBranchQuota(100000); 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); } @@ -486,16 +496,16 @@ pub const Command = struct { // MARK: Command return &self.inv._items[self.slot]; } - fn write(self: InventoryAndSlot, data: *[8]u8) void { - std.mem.writeInt(u32, data[0..4], self.inv.id, .big); - std.mem.writeInt(u32, data[4..8], self.slot, .big); + fn write(self: InventoryAndSlot, writer: *utils.BinaryWriter) void { + writer.writeInt(u32, self.inv.id); + writer.writeInt(u32, self.slot); } - fn read(data: *const [8]u8, side: Side, user: ?*main.server.User) !InventoryAndSlot { - const id = std.mem.readInt(u32, data[0..4], .big); + fn read(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !InventoryAndSlot { + const id = try reader.readInt(u32); return .{ .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, }, - pub fn executeFromData(data: []const u8) !void { - std.debug.assert(data.len >= 1); - - switch(try deserialize(data)) { + pub fn executeFromData(reader: *utils.BinaryReader) !void { + switch(try deserialize(reader)) { .create => |create| { if(create.item) |item| { create.inv.ref().item = item; @@ -632,28 +640,19 @@ pub const Command = struct { // MARK: Command }; } - fn deserialize(fullData: []const u8) !SyncOperation { - if(fullData.len == 0) { - return error.Invalid; - } - - const typ: SyncOperationType = @enumFromInt(fullData[0]); - - const data = fullData[1..]; + fn deserialize(reader: *utils.BinaryReader) !SyncOperation { + const typ = try reader.readEnum(SyncOperationType); switch(typ) { .create => { - if(data.len < 10) { - return error.Invalid; - } var out: SyncOperation = .{.create = .{ - .inv = try InventoryAndSlot.read(data[0..8], .client, null), - .amount = std.mem.readInt(u16, data[8..10], .big), + .inv = try InventoryAndSlot.read(reader, .client, null), + .amount = try reader.readInt(u16), .item = null, }}; - if(data.len > 10) { - const zon = ZonElement.parseFromString(main.stackAllocator, null, data[10..]); + if(reader.remaining.len != 0) { + const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining); defer zon.deinit(main.stackAllocator); out.create.item = try Item.init(zon); } @@ -661,42 +660,28 @@ pub const Command = struct { // MARK: Command return out; }, .delete => { - if(data.len != 10) { - return error.Invalid; - } const out: SyncOperation = .{.delete = .{ - .inv = try InventoryAndSlot.read(data[0..8], .client, null), - .amount = std.mem.readInt(u16, data[8..10], .big), + .inv = try InventoryAndSlot.read(reader, .client, null), + .amount = try reader.readInt(u16), }}; return out; }, .useDurability => { - if(data.len != 12) { - return error.Invalid; - } const out: SyncOperation = .{.useDurability = .{ - .inv = try InventoryAndSlot.read(data[0..8], .client, null), - .durability = std.mem.readInt(u32, data[8..12], .big), + .inv = try InventoryAndSlot.read(reader, .client, null), + .durability = try reader.readInt(u32), }}; return out; }, .health => { - if(data.len != 4) { - return error.Invalid; - } - return .{.health = .{ .target = null, - .health = @bitCast(std.mem.readInt(u32, data[0..4], .big)), + .health = @bitCast(try reader.readInt(u32)), }}; }, .kill => { - if(data.len != 0) { - return error.Invalid; - } - return .{.kill = .{ .target = null, }}; @@ -705,35 +690,35 @@ pub const Command = struct { // MARK: Command } pub fn serialize(self: SyncOperation, allocator: NeverFailingAllocator) []const u8 { - var data = main.List(u8).initCapacity(allocator, 13); - data.append(@intFromEnum(self)); + var writer = utils.BinaryWriter.initCapacity(allocator, main.network.networkEndian, 13); + writer.writeEnum(SyncOperationType, self); switch(self) { .create => |create| { - create.inv.write(data.addMany(8)[0..8]); - std.mem.writeInt(u16, data.addMany(2)[0..2], create.amount, .big); + create.inv.write(&writer); + writer.writeInt(u16, create.amount); if(create.item) |item| { const zon = ZonElement.initObject(main.stackAllocator); defer zon.deinit(main.stackAllocator); item.insertIntoZon(main.stackAllocator, zon); const string = zon.toStringEfficient(main.stackAllocator, &.{}); defer main.stackAllocator.free(string); - data.appendSlice(string); + writer.writeSlice(string); } }, .delete => |delete| { - delete.inv.write(data.addMany(8)[0..8]); - std.mem.writeInt(u16, data.addMany(2)[0..2], delete.amount, .big); + delete.inv.write(&writer); + writer.writeInt(u16, delete.amount); }, .useDurability => |durability| { - durability.inv.write(data.addMany(8)[0..8]); - std.mem.writeInt(u32, data.addMany(4)[0..4], durability.durability, .big); + durability.inv.write(&writer); + writer.writeInt(u32, durability.durability); }, .health => |health| { - std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(health.health), .big); + writer.writeInt(u32, @bitCast(health.health)); }, .kill => {}, } - return data.toOwnedSlice(); + return writer.data.toOwnedSlice(); } }; @@ -742,13 +727,14 @@ pub const Command = struct { // MARK: Command syncOperations: main.ListUnmanaged(SyncOperation) = .{}, 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) { 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() @@ -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| { switch(step) { .move, .swap, .create, .addHealth => {}, @@ -835,7 +821,7 @@ pub const Command = struct { // MARK: Command switch(self.payload) { inline else => |payload| { 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 finalize(self: Open, side: Side, data: []const u8) void { + fn finalize(self: Open, side: Side, reader: *utils.BinaryReader) !void { if(side != .client) return; - if(data.len >= 4) { - const serverId = std.mem.readInt(u32, data[0..4], .big); + if(reader.remaining.len != 0) { + const serverId = try reader.readInt(u32); Sync.ClientSide.mapServerId(serverId, self.inv); } } fn confirmationData(self: Open, allocator: NeverFailingAllocator) []const u8 { - const data = allocator.alloc(u8, 4); - std.mem.writeInt(u32, data[0..4], self.inv.id, .big); - return data; + var writer = utils.BinaryWriter.initCapacity(allocator, main.network.networkEndian, 4); + writer.writeInt(u32, self.inv.id); + return writer.data.toOwnedSlice(); } - fn serialize(self: Open, data: *main.List(u8)) void { - std.mem.writeInt(u32, data.addMany(4)[0..4], self.inv.id, .big); - std.mem.writeInt(usize, data.addMany(8)[0..8], self.inv._items.len, .big); - data.append(@intFromEnum(std.meta.activeTag(self.inv.type))); - data.append(@intFromEnum(self.source)); + fn serialize(self: Open, writer: *utils.BinaryWriter) void { + writer.writeInt(u32, self.inv.id); + writer.writeInt(usize, self.inv._items.len); + writer.writeEnum(TypeEnum, self.inv.type); + writer.writeEnum(SourceType, self.source); switch(self.source) { .playerInventory, .hand => |val| { - std.mem.writeInt(u32, data.addMany(4)[0..4], val, .big); + writer.writeInt(u32, val); }, .recipe => |val| { - std.mem.writeInt(u16, data.addMany(2)[0..2], val.resultAmount, .big); - data.appendSlice(val.resultItem.id); - data.append(0); + writer.writeInt(u16, val.resultAmount); + writer.writeWithDelimiter(val.resultItem.id, 0); for(0..val.sourceItems.len) |i| { - std.mem.writeInt(u16, data.addMany(2)[0..2], val.sourceAmounts[i], .big); - data.appendSlice(val.sourceItems[i].id); - data.append(0); + writer.writeInt(u16, val.sourceAmounts[i]); + writer.writeWithDelimiter(val.sourceItems[i].id, 0); } }, .sharedTestingInventory, .other => {}, @@ -1086,35 +1070,29 @@ pub const Command = struct { // MARK: Command switch(self.inv.type) { .normal, .creative, .crafting => {}, .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 { - if(data.len < 14) return error.Invalid; + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Open { if(side != .server or user == null) return error.Invalid; - const id = std.mem.readInt(u32, data[0..4], .big); - const len = std.mem.readInt(usize, data[4..12], .big); - const typeEnum: TypeEnum = @enumFromInt(data[12]); - const sourceType: SourceType = @enumFromInt(data[13]); + const id = try reader.readInt(u32); + const len = try reader.readInt(u64); + const typeEnum = try reader.readEnum(TypeEnum); + const sourceType = try reader.readEnum(SourceType); const source: Source = switch(sourceType) { - .playerInventory => .{.playerInventory = std.mem.readInt(u32, data[14..18], .big)}, + .playerInventory => .{.playerInventory = try reader.readInt(u32)}, .sharedTestingInventory => .{.sharedTestingInventory = {}}, - .hand => .{.hand = std.mem.readInt(u32, data[14..18], .big)}, + .hand => .{.hand = try reader.readInt(u32)}, .recipe => .{ .recipe = blk: { var itemList = main.List(struct {amount: u16, item: *const main.items.BaseItem}).initCapacity(main.stackAllocator, len); defer itemList.deinit(); - var index: usize = 14; - while(index + 2 < data.len) { - const resultAmount = std.mem.readInt(u16, data[index..][0..2], .big); - index += 2; - 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; + while(reader.remaining.len >= 2) { + const resultAmount = try reader.readInt(u16); + const itemId = try reader.readUntilDelimiter(0); + const resultItem = main.items.getByID(itemId) orelse return error.Invalid; itemList.append(.{.amount = resultAmount, .item = resultItem}); } if(itemList.items.len != len) return error.Invalid; @@ -1133,15 +1111,9 @@ pub const Command = struct { // MARK: Command .other => .{.other = {}}, .alreadyFreed => unreachable, }; - const remainingLen: usize = switch(sourceType) { - .playerInventory, .hand => 18, - .sharedTestingInventory, .other => 14, - .recipe => data.len, - .alreadyFreed => unreachable, - }; const typ: Type = switch(typeEnum) { 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); 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 finalize(self: Close, side: Side, data: []const u8) void { + fn finalize(self: Close, side: Side, reader: *utils.BinaryReader) !void { if(side != .client) return; self.inv._deinit(self.allocator, .client); - if(data.len >= 4) { - const serverId = std.mem.readInt(u32, data[0..4], .big); + if(reader.remaining.len != 0) { + const serverId = try reader.readInt(u32); Sync.ClientSide.unmapServerId(serverId, self.inv.id); } } - fn serialize(self: Close, data: *main.List(u8)) void { - std.mem.writeInt(u32, data.addMany(4)[0..4], self.inv.id, .big); + fn serialize(self: Close, writer: *utils.BinaryWriter) void { + writer.writeInt(u32, self.inv.id); } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Close { - if(data.len != 4) return error.Invalid; + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Close { 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); return undefined; } @@ -1228,16 +1199,15 @@ pub const Command = struct { // MARK: Command }}, side); } - fn serialize(self: DepositOrSwap, data: *main.List(u8)) void { - self.dest.write(data.addMany(8)[0..8]); - self.source.write(data.addMany(8)[0..8]); + fn serialize(self: DepositOrSwap, writer: *utils.BinaryWriter) void { + self.dest.write(writer); + self.source.write(writer); } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !DepositOrSwap { - if(data.len != 16) return error.Invalid; + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !DepositOrSwap { return .{ - .dest = try InventoryAndSlot.read(data[0..8], side, user), - .source = try InventoryAndSlot.read(data[8..16], side, user), + .dest = try InventoryAndSlot.read(reader, 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 { - self.dest.write(data.addMany(8)[0..8]); - self.source.write(data.addMany(8)[0..8]); - std.mem.writeInt(u16, data.addMany(2)[0..2], self.amount, .big); + fn serialize(self: Deposit, writer: *utils.BinaryWriter) void { + self.dest.write(writer); + self.source.write(writer); + writer.writeInt(u16, self.amount); } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Deposit { - if(data.len != 18) return error.Invalid; + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Deposit { return .{ - .dest = try InventoryAndSlot.read(data[0..8], side, user), - .source = try InventoryAndSlot.read(data[8..16], side, user), - .amount = std.mem.readInt(u16, data[16..18], .big), + .dest = try InventoryAndSlot.read(reader, side, user), + .source = try InventoryAndSlot.read(reader, side, user), + .amount = try reader.readInt(u16), }; } }; @@ -1339,16 +1308,15 @@ pub const Command = struct { // MARK: Command } } - fn serialize(self: TakeHalf, data: *main.List(u8)) void { - self.dest.write(data.addMany(8)[0..8]); - self.source.write(data.addMany(8)[0..8]); + fn serialize(self: TakeHalf, writer: *utils.BinaryWriter) void { + self.dest.write(writer); + self.source.write(writer); } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !TakeHalf { - if(data.len != 16) return error.Invalid; + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !TakeHalf { return .{ - .dest = try InventoryAndSlot.read(data[0..8], side, user), - .source = try InventoryAndSlot.read(data[8..16], side, user), + .dest = try InventoryAndSlot.read(reader, side, user), + .source = try InventoryAndSlot.read(reader, side, user), }; } }; @@ -1393,18 +1361,17 @@ pub const Command = struct { // MARK: Command }}, side); } - fn serialize(self: Drop, data: *main.List(u8)) void { - self.source.write(data.addMany(8)[0..8]); + fn serialize(self: Drop, writer: *utils.BinaryWriter) void { + self.source.write(writer); 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 { - if(data.len != 8 and data.len != 10) return error.Invalid; + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Drop { return .{ - .source = try InventoryAndSlot.read(data[0..8], side, user), - .desiredAmount = if(data.len == 10) std.mem.readInt(u16, data[8..10], .big) else 0xffff, + .source = try InventoryAndSlot.read(reader, side, user), + .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 { - self.dest.write(data.addMany(8)[0..8]); - std.mem.writeInt(u16, data.addMany(2)[0..2], self.amount, .big); + fn serialize(self: FillFromCreative, writer: *utils.BinaryWriter) void { + self.dest.write(writer); + writer.writeInt(u16, self.amount); if(self.item) |item| { const zon = ZonElement.initObject(main.stackAllocator); defer zon.deinit(main.stackAllocator); item.insertIntoZon(main.stackAllocator, zon); const string = zon.toStringEfficient(main.stackAllocator, &.{}); defer main.stackAllocator.free(string); - data.appendSlice(string); + writer.writeSlice(string); } } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !FillFromCreative { - if(data.len < 10) return error.Invalid; - const amount = std.mem.readInt(u16, data[8..10], .big); + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !FillFromCreative { + const dest = try InventoryAndSlot.read(reader, side, user); + const amount = try reader.readInt(u16); var item: ?Item = null; - if(data.len > 10) { - const zon = ZonElement.parseFromString(main.stackAllocator, null, data[10..]); + if(reader.remaining.len != 0) { + const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining); defer zon.deinit(main.stackAllocator); item = try Item.init(zon); } return .{ - .dest = try InventoryAndSlot.read(data[0..8], side, user), + .dest = dest, .item = item, .amount = amount, }; @@ -1509,15 +1476,14 @@ pub const Command = struct { // MARK: Command } } - fn serialize(self: DepositOrDrop, data: *main.List(u8)) void { - std.mem.writeInt(u32, data.addMany(4)[0..4], self.dest.id, .big); - std.mem.writeInt(u32, data.addMany(4)[0..4], self.source.id, .big); + fn serialize(self: DepositOrDrop, writer: *utils.BinaryWriter) void { + writer.writeInt(u32, self.dest.id); + writer.writeInt(u32, self.source.id); } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !DepositOrDrop { - if(data.len != 8) return error.Invalid; - const destId = std.mem.readInt(u32, data[0..4], .big); - const sourceId = std.mem.readInt(u32, data[4..8], .big); + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !DepositOrDrop { + const destId = try reader.readInt(u32); + const sourceId = try reader.readInt(u32); return .{ .dest = Sync.getInventory(destId, 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 { - std.mem.writeInt(u32, data.addMany(4)[0..4], self.inv.id, .big); + fn serialize(self: Clear, writer: *utils.BinaryWriter) void { + writer.writeInt(u32, self.inv.id); } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !Clear { - if(data.len != 4) return error.Invalid; - const invId = std.mem.readInt(u32, data[0..4], .big); + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !Clear { + const invId = try reader.readInt(u32); return .{ .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 { - self.source.write(data.addMany(8)[0..8]); - std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[0], .big); - std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[1], .big); - std.mem.writeInt(i32, data.addMany(4)[0..4], self.pos[2], .big); - std.mem.writeInt(u32, data.addMany(4)[0..4], @as(u32, @bitCast(self.oldBlock)), .big); - std.mem.writeInt(u32, data.addMany(4)[0..4], @as(u32, @bitCast(self.newBlock)), .big); + fn serialize(self: UpdateBlock, writer: *utils.BinaryWriter) void { + self.source.write(writer); + writer.writeInt(i32, self.pos[0]); + writer.writeInt(i32, self.pos[1]); + writer.writeInt(i32, self.pos[2]); + writer.writeInt(u32, @as(u32, @bitCast(self.oldBlock))); + writer.writeInt(u32, @as(u32, @bitCast(self.newBlock))); } - fn deserialize(data: []const u8, side: Side, user: ?*main.server.User) !UpdateBlock { - if(data.len != 28) return error.Invalid; + fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !UpdateBlock { return .{ - .source = try InventoryAndSlot.read(data[0..8], side, user), + .source = try InventoryAndSlot.read(reader, side, user), .pos = .{ - std.mem.readInt(i32, data[8..12], .big), - std.mem.readInt(i32, data[12..16], .big), - std.mem.readInt(i32, data[16..20], .big), + try reader.readInt(i32), + try reader.readInt(i32), + try reader.readInt(i32), }, - .oldBlock = @bitCast(std.mem.readInt(u32, data[20..24], .big)), - .newBlock = @bitCast(std.mem.readInt(u32, data[24..28], .big)), + .oldBlock = @bitCast(try reader.readInt(u32)), + .newBlock = @bitCast(try reader.readInt(u32)), }; } }; @@ -1696,19 +1660,17 @@ pub const Command = struct { // MARK: Command }}, side); } - fn serialize(self: AddHealth, data: *main.List(u8)) void { - std.mem.writeInt(u32, data.addMany(4)[0..4], self.target, .big); - std.mem.writeInt(u32, data.addMany(4)[0..4], @bitCast(self.health), .big); - data.append(@intFromEnum(self.cause)); + fn serialize(self: AddHealth, writer: *utils.BinaryWriter) void { + writer.writeInt(u32, self.target); + writer.writeInt(u32, @bitCast(self.health)); + writer.writeEnum(main.game.DamageType, self.cause); } - fn deserialize(data: []const u8, _: Side, _: ?*main.server.User) !AddHealth { - if(data.len != 9) return error.Invalid; - + fn deserialize(reader: *utils.BinaryReader, _: Side, _: ?*main.server.User) !AddHealth { return .{ - .target = std.mem.readInt(u32, data[0..4], .big), - .health = @bitCast(std.mem.readInt(u32, data[4..8], .big)), - .cause = @enumFromInt(data[8]), + .target = try reader.readInt(u32), + .health = @bitCast(try reader.readInt(u32)), + .cause = try reader.readEnum(main.game.DamageType), }; } }; diff --git a/src/network.zig b/src/network.zig index 8714cd22..c71acbea 100644 --- a/src/network.zig +++ b/src/network.zig @@ -21,6 +21,8 @@ const Vec3f = vec.Vec3f; const Vec3i = vec.Vec3i; 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 const Socket = struct { @@ -632,7 +634,7 @@ const UnconfirmedPacket = struct { pub var bytesReceived: [256]Atomic(usize) = .{Atomic(usize).init(0)} ** 256; pub var packetsReceived: [256]Atomic(usize) = .{Atomic(usize).init(0)} ** 256; 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 const keepAlive: u8 = 0; @@ -646,12 +648,13 @@ pub const Protocols = struct { const stepServerData: u8 = 3; pub const stepComplete: u8 = 255; - fn receive(conn: *Connection, data: []const u8) !void { - if(conn.handShakeState.load(.monotonic) < data[0]) { - conn.handShakeState.store(data[0], .monotonic); - switch(data[0]) { + fn receive(conn: *Connection, reader: *utils.BinaryReader) !void { + const newState = try reader.readInt(u8); + if(conn.handShakeState.load(.monotonic) < newState) { + conn.handShakeState.store(newState, .monotonic); + switch(newState) { stepUserData => { - const zon = ZonElement.parseFromString(main.stackAllocator, null, data[1..]); + const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining); defer zon.deinit(main.stackAllocator); const name = zon.get([]const u8, "name", "unnamed"); 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. var dir = try std.fs.cwd().makeOpenPath("serverAssets", .{}); defer dir.close(); - try utils.Compression.unpack(dir, data[1..]); + try utils.Compression.unpack(dir, reader.remaining); }, stepServerData => { - const zon = ZonElement.parseFromString(main.stackAllocator, null, data[1..]); + const zon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining); defer zon.deinit(main.stackAllocator); try conn.manager.world.?.finishHandshake(zon); conn.handShakeState.store(stepComplete, .monotonic); @@ -707,7 +710,7 @@ pub const Protocols = struct { }, stepComplete => {}, else => { - std.log.err("Unknown state in HandShakeProtocol {}", .{data[0]}); + std.log.err("Unknown state in HandShakeProtocol {}", .{newState}); }, } } else { @@ -738,66 +741,63 @@ pub const Protocols = struct { pub const chunkRequest = struct { pub const id: u8 = 2; pub const asynchronous = false; - fn receive(conn: *Connection, data: []const u8) !void { - var remaining = data[0..]; + fn receive(conn: *Connection, reader: *utils.BinaryReader) !void { const basePosition = Vec3i{ - std.mem.readInt(i32, remaining[0..4], .big), - std.mem.readInt(i32, remaining[4..8], .big), - std.mem.readInt(i32, remaining[8..12], .big), + try reader.readInt(i32), + try reader.readInt(i32), + try reader.readInt(i32), }; conn.user.?.clientUpdatePos = basePosition; - conn.user.?.renderDistance = std.mem.readInt(u16, remaining[12..14], .big); - remaining = remaining[14..]; - while(remaining.len >= 4) { - const voxelSizeShift: u5 = @intCast(remaining[3]); + conn.user.?.renderDistance = try reader.readInt(u16); + while(reader.remaining.len >= 4) { + const x: i32 = try reader.readInt(i8); + 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 request = chunk.ChunkPosition{ - .wx = (@as(i32, @as(i8, @bitCast(remaining[0]))) << voxelSizeShift + chunk.chunkShift) +% (basePosition[0] & positionMask), - .wy = (@as(i32, @as(i8, @bitCast(remaining[1]))) << voxelSizeShift + chunk.chunkShift) +% (basePosition[1] & positionMask), - .wz = (@as(i32, @as(i8, @bitCast(remaining[2]))) << voxelSizeShift + chunk.chunkShift) +% (basePosition[2] & positionMask), + .wx = (x << voxelSizeShift + chunk.chunkShift) +% (basePosition[0] & positionMask), + .wy = (y << voxelSizeShift + chunk.chunkShift) +% (basePosition[1] & positionMask), + .wz = (z << voxelSizeShift + chunk.chunkShift) +% (basePosition[2] & positionMask), .voxelSize = @as(u31, 1) << voxelSizeShift, }; if(conn.user) |user| { user.increaseRefCount(); main.server.world.?.queueChunkAndDecreaseRefCount(request, user); } - remaining = remaining[4..]; } } pub fn sendRequest(conn: *Connection, requests: []chunk.ChunkPosition, basePosition: Vec3i, renderDistance: u16) void { if(requests.len == 0) return; - const data = main.stackAllocator.alloc(u8, 14 + 4*requests.len); - defer main.stackAllocator.free(data); - var remaining = data; - std.mem.writeInt(i32, remaining[0..4], basePosition[0], .big); - std.mem.writeInt(i32, remaining[4..8], basePosition[1], .big); - std.mem.writeInt(i32, remaining[8..12], basePosition[2], .big); - std.mem.writeInt(u16, remaining[12..14], renderDistance, .big); - remaining = remaining[14..]; + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 14 + 4*requests.len); + defer writer.deinit(); + writer.writeInt(i32, basePosition[0]); + writer.writeInt(i32, basePosition[1]); + writer.writeInt(i32, basePosition[2]); + writer.writeInt(u16, renderDistance); for(requests) |req| { const voxelSizeShift: u5 = std.math.log2_int(u31, req.voxelSize); const positionMask = ~((@as(i32, 1) << voxelSizeShift + chunk.chunkShift) - 1); - remaining[0] = @bitCast(@as(i8, @intCast((req.wx -% (basePosition[0] & positionMask)) >> voxelSizeShift + chunk.chunkShift))); - remaining[1] = @bitCast(@as(i8, @intCast((req.wy -% (basePosition[1] & positionMask)) >> voxelSizeShift + chunk.chunkShift))); - remaining[2] = @bitCast(@as(i8, @intCast((req.wz -% (basePosition[2] & positionMask)) >> voxelSizeShift + chunk.chunkShift))); - remaining[3] = voxelSizeShift; - remaining = remaining[4..]; + writer.writeInt(i8, @intCast((req.wx -% (basePosition[0] & positionMask)) >> voxelSizeShift + chunk.chunkShift)); + writer.writeInt(i8, @intCast((req.wy -% (basePosition[1] & positionMask)) >> voxelSizeShift + chunk.chunkShift)); + writer.writeInt(i8, @intCast((req.wz -% (basePosition[2] & positionMask)) >> voxelSizeShift + chunk.chunkShift)); + writer.writeInt(u5, voxelSizeShift); } - conn.sendImportant(id, data); + conn.sendImportant(id, writer.data.items); } }; pub const chunkTransmission = struct { pub const id: u8 = 3; pub const asynchronous = true; - fn receive(_: *Connection, data: []const u8) !void { + fn receive(_: *Connection, reader: *utils.BinaryReader) !void { const pos = chunk.ChunkPosition{ - .wx = std.mem.readInt(i32, data[0..4], .big), - .wy = std.mem.readInt(i32, data[4..8], .big), - .wz = std.mem.readInt(i32, data[8..12], .big), - .voxelSize = @intCast(std.mem.readInt(i32, data[12..16], .big)), + .wx = try reader.readInt(i32), + .wy = try reader.readInt(i32), + .wz = try reader.readInt(i32), + .voxelSize = try reader.readInt(u31), }; 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); } 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); ch.mutex.unlock(); defer main.stackAllocator.free(chunkData); - const data = main.stackAllocator.alloc(u8, chunkData.len + 16); - defer main.stackAllocator.free(data); - std.mem.writeInt(i32, data[0..4], ch.super.pos.wx, .big); - std.mem.writeInt(i32, data[4..8], ch.super.pos.wy, .big); - std.mem.writeInt(i32, data[8..12], ch.super.pos.wz, .big); - std.mem.writeInt(i32, data[12..16], ch.super.pos.voxelSize, .big); - @memcpy(data[16..], chunkData); - conn.sendImportant(id, data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, chunkData.len + 16); + defer writer.deinit(); + writer.writeInt(i32, ch.super.pos.wx); + writer.writeInt(i32, ch.super.pos.wy); + writer.writeInt(i32, ch.super.pos.wz); + writer.writeInt(u31, ch.super.pos.voxelSize); + writer.writeSlice(chunkData); + conn.sendImportant(id, writer.data.items); } fn sendChunkLocally(ch: *chunk.ServerChunk) void { const chunkCopy = chunk.Chunk.init(ch.super.pos); @@ -831,8 +831,8 @@ pub const Protocols = struct { pub const playerPosition = struct { pub const id: u8 = 4; pub const asynchronous = false; - fn receive(conn: *Connection, data: []const u8) !void { - conn.user.?.receiveData(data); + fn receive(conn: *Connection, reader: *utils.BinaryReader) !void { + conn.user.?.receiveData(reader.remaining); } var lastPositionSent: u16 = 0; 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. } lastPositionSent = time; - var data: [62]u8 = undefined; - std.mem.writeInt(u64, data[0..8], @as(u64, @bitCast(playerPos[0])), .big); - std.mem.writeInt(u64, data[8..16], @as(u64, @bitCast(playerPos[1])), .big); - std.mem.writeInt(u64, data[16..24], @as(u64, @bitCast(playerPos[2])), .big); - std.mem.writeInt(u64, data[24..32], @as(u64, @bitCast(playerVel[0])), .big); - std.mem.writeInt(u64, data[32..40], @as(u64, @bitCast(playerVel[1])), .big); - std.mem.writeInt(u64, data[40..48], @as(u64, @bitCast(playerVel[2])), .big); - std.mem.writeInt(u32, data[48..52], @as(u32, @bitCast(game.camera.rotation[0])), .big); - std.mem.writeInt(u32, data[52..56], @as(u32, @bitCast(game.camera.rotation[1])), .big); - std.mem.writeInt(u32, data[56..60], @as(u32, @bitCast(game.camera.rotation[2])), .big); - std.mem.writeInt(u16, data[60..62], time, .big); - conn.sendUnimportant(id, &data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 62); + defer writer.deinit(); + writer.writeInt(u64, @bitCast(playerPos[0])); + writer.writeInt(u64, @bitCast(playerPos[1])); + writer.writeInt(u64, @bitCast(playerPos[2])); + writer.writeInt(u64, @bitCast(playerVel[0])); + writer.writeInt(u64, @bitCast(playerVel[1])); + writer.writeInt(u64, @bitCast(playerVel[2])); + writer.writeInt(u32, @bitCast(game.camera.rotation[0])); + writer.writeInt(u32, @bitCast(game.camera.rotation[1])); + writer.writeInt(u32, @bitCast(game.camera.rotation[2])); + writer.writeInt(u16, time); + conn.sendUnimportant(id, writer.data.items); } }; pub const disconnect = struct { pub const id: u8 = 5; pub const asynchronous = false; - fn receive(conn: *Connection, _: []const u8) !void { + fn receive(conn: *Connection, _: *utils.BinaryReader) !void { conn.disconnect(); if(conn.user) |user| { main.server.disconnect(user); @@ -875,44 +876,45 @@ pub const Protocols = struct { pub const asynchronous = false; const type_entity: u8 = 0; 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| { - const time = std.mem.readInt(i16, data[1..3], .big); - if(data[0] == type_entity) { - main.entity.ClientEntityManager.serverUpdate(time, data[3..]); - } else if(data[0] == type_item) { - world.itemDrops.readPosition(data[3..], time); + const typ = try reader.readInt(u8); + const time = try reader.readInt(i16); + if(typ == type_entity) { + main.entity.ClientEntityManager.serverUpdate(time, reader.remaining); + } else if(typ == type_item) { + world.itemDrops.readPosition(reader.remaining, time); } } } pub fn send(conn: *Connection, entityData: []const u8, itemData: []const u8) void { if(entityData.len != 0) { - const fullEntityData = main.stackAllocator.alloc(u8, entityData.len + 3); - defer main.stackAllocator.free(fullEntityData); - fullEntityData[0] = type_entity; - std.mem.writeInt(i16, fullEntityData[1..3], @as(i16, @truncate(std.time.milliTimestamp())), .big); - @memcpy(fullEntityData[3..], entityData); - conn.sendUnimportant(id, fullEntityData); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, entityData.len + 3); + defer writer.deinit(); + writer.writeInt(u8, type_entity); + writer.writeInt(i16, @truncate(std.time.milliTimestamp())); + writer.writeSlice(entityData); + conn.sendUnimportant(id, writer.data.items); } if(itemData.len != 0) { - const fullItemData = main.stackAllocator.alloc(u8, itemData.len + 3); - defer main.stackAllocator.free(fullItemData); - fullItemData[0] = type_item; - std.mem.writeInt(i16, fullItemData[1..3], @as(i16, @truncate(std.time.milliTimestamp())), .big); - @memcpy(fullItemData[3..], itemData); - conn.sendUnimportant(id, fullItemData); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, itemData.len + 3); + defer writer.deinit(); + writer.writeInt(u8, type_item); + writer.writeInt(i16, @truncate(std.time.milliTimestamp())); + writer.writeSlice(itemData); + conn.sendUnimportant(id, writer.data.items); } } }; pub const blockUpdate = struct { pub const id: u8 = 7; pub const asynchronous = false; - fn receive(conn: *Connection, data: []const u8) !void { - const x = std.mem.readInt(i32, data[0..4], .big); - const y = std.mem.readInt(i32, data[4..8], .big); - const z = std.mem.readInt(i32, data[8..12], .big); - const newBlock = Block.fromInt(std.mem.readInt(u32, data[12..16], .big)); + fn receive(conn: *Connection, reader: *utils.BinaryReader) !void { + const x = try reader.readInt(i32); + const y = try reader.readInt(i32); + const z = try reader.readInt(i32); + const newBlock = Block.fromInt(try reader.readInt(u32)); if(conn.user != null) { return error.InvalidPacket; } else { @@ -920,19 +922,20 @@ pub const Protocols = struct { } } pub fn send(conn: *Connection, x: i32, y: i32, z: i32, newBlock: Block) void { - var data: [16]u8 = undefined; - std.mem.writeInt(i32, data[0..4], x, .big); - std.mem.writeInt(i32, data[4..8], y, .big); - std.mem.writeInt(i32, data[8..12], z, .big); - std.mem.writeInt(u32, data[12..16], newBlock.toInt(), .big); - conn.sendImportant(id, &data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 16); + defer writer.deinit(); + writer.writeInt(i32, x); + writer.writeInt(i32, y); + writer.writeInt(i32, z); + writer.writeInt(u32, newBlock.toInt()); + conn.sendImportant(id, writer.data.items); } }; pub const entity = struct { pub const id: u8 = 8; pub const asynchronous = false; - fn receive(conn: *Connection, data: []const u8) !void { - const zonArray = ZonElement.parseFromString(main.stackAllocator, null, data); + fn receive(conn: *Connection, reader: *utils.BinaryReader) !void { + const zonArray = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining); defer zonArray.deinit(main.stackAllocator); var i: u32 = 0; while(i < zonArray.array.items.len) : (i += 1) { @@ -949,7 +952,7 @@ pub const Protocols = struct { break; }, 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_reserved6: u8 = 7; const type_timeAndBiome: u8 = 8; - fn receive(conn: *Connection, data: []const u8) !void { - switch(data[0]) { + fn receive(conn: *Connection, reader: *utils.BinaryReader) !void { + switch(try reader.readInt(u8)) { type_gamemode => { if(conn.user != null) return error.InvalidPacket; - if(data.len != 2) return error.InvalidPacket; - main.items.Inventory.Sync.setGamemode(null, @enumFromInt(data[1])); + main.items.Inventory.Sync.setGamemode(null, try reader.readEnum(main.game.Gamemode)); }, type_teleport => { game.Player.setPosBlocking(Vec3d{ - @bitCast(std.mem.readInt(u64, data[1..9], .big)), - @bitCast(std.mem.readInt(u64, data[9..17], .big)), - @bitCast(std.mem.readInt(u64, data[17..25], .big)), + @bitCast(try reader.readInt(u64)), + @bitCast(try reader.readInt(u64)), + @bitCast(try reader.readInt(u64)), }); }, type_cure => { @@ -1004,7 +1006,7 @@ pub const Protocols = struct { type_reserved6 => {}, type_timeAndBiome => { 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); const expectedTime = zon.get(i64, "time", 0); var curTime = world.gameTime.load(.monotonic); @@ -1027,7 +1029,8 @@ pub const Protocols = struct { } }, 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 { - var data: [1 + 24]u8 = undefined; - data[0] = type_teleport; - std.mem.writeInt(u64, data[1..9], @as(u64, @bitCast(pos[0])), .big); - std.mem.writeInt(u64, data[9..17], @as(u64, @bitCast(pos[1])), .big); - std.mem.writeInt(u64, data[17..25], @as(u64, @bitCast(pos[2])), .big); - conn.sendImportant(id, &data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 25); + defer writer.deinit(); + writer.writeInt(u8, type_teleport); + writer.writeInt(u64, @bitCast(pos[0])); + writer.writeInt(u64, @bitCast(pos[1])); + writer.writeInt(u64, @bitCast(pos[2])); + conn.sendImportant(id, writer.data.items); } pub fn sendCure(conn: *Connection) void { @@ -1081,131 +1085,135 @@ pub const Protocols = struct { pub const chat = struct { pub const id: u8 = 10; 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(data.len > 10000 or main.graphics.TextBuffer.Parser.countVisibleCharacters(data) > 1000) { - std.log.err("Received too long chat message with {}/{} characters.", .{main.graphics.TextBuffer.Parser.countVisibleCharacters(data), data.len}); + 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(msg), msg.len}); return error.Invalid; } - main.server.messageFrom(data, user); + main.server.messageFrom(msg, user); } else { - main.gui.windowlist.chat.addMessage(data); + main.gui.windowlist.chat.addMessage(msg); } } - pub fn send(conn: *Connection, data: []const u8) void { - conn.sendImportant(id, data); + pub fn send(conn: *Connection, msg: []const u8) void { + conn.sendImportant(id, msg); } }; pub const lightMapRequest = struct { pub const id: u8 = 11; pub const asynchronous = false; - fn receive(conn: *Connection, data: []const u8) !void { - var remaining = data[0..]; - while(remaining.len >= 9) { + fn receive(conn: *Connection, reader: *utils.BinaryReader) !void { + while(reader.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{ - .wx = std.mem.readInt(i32, remaining[0..4], .big), - .wy = std.mem.readInt(i32, remaining[4..8], .big), - .voxelSize = @as(u31, 1) << @intCast(std.mem.readInt(u8, remaining[8..9], .big)), - .voxelSizeShift = @intCast(std.mem.readInt(u8, remaining[8..9], .big)), + .wx = wx, + .wy = wy, + .voxelSize = @as(u31, 1) << voxelSizeShift, + .voxelSizeShift = voxelSizeShift, }; if(conn.user) |user| { user.increaseRefCount(); main.server.world.?.queueLightMapAndDecreaseRefCount(request, user); } - remaining = remaining[9..]; } } pub fn sendRequest(conn: *Connection, requests: []main.server.terrain.SurfaceMap.MapFragmentPosition) void { if(requests.len == 0) return; - const data = main.stackAllocator.alloc(u8, 9*requests.len); - defer main.stackAllocator.free(data); - var remaining = data; + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 9*requests.len); + defer writer.deinit(); for(requests) |req| { - std.mem.writeInt(i32, remaining[0..4], req.wx, .big); - std.mem.writeInt(i32, remaining[4..8], req.wy, .big); - std.mem.writeInt(u8, remaining[8..9], req.voxelSizeShift, .big); - remaining = remaining[9..]; + writer.writeInt(i32, req.wx); + writer.writeInt(i32, req.wy); + writer.writeInt(u8, req.voxelSizeShift); } - conn.sendImportant(id, data); + conn.sendImportant(id, writer.data.items); } }; pub const lightMapTransmission = struct { pub const id: u8 = 12; pub const asynchronous = true; - fn receive(_: *Connection, _data: []const u8) !void { - var data = _data; + fn receive(_: *Connection, reader: *utils.BinaryReader) !void { + 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{ - .wx = std.mem.readInt(i32, data[0..4], .big), - .wy = std.mem.readInt(i32, data[4..8], .big), - .voxelSize = @as(u31, 1) << @intCast(std.mem.readInt(u8, data[8..9], .big)), - .voxelSizeShift = @intCast(std.mem.readInt(u8, data[8..9], .big)), + .wx = wx, + .wy = wy, + .voxelSize = @as(u31, 1) << voxelSizeShift, + .voxelSizeShift = voxelSizeShift, }; const _inflatedData = main.stackAllocator.alloc(u8, main.server.terrain.LightMap.LightMapFragment.mapSize*main.server.terrain.LightMap.LightMapFragment.mapSize*2); 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) { - 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); map.init(pos.wx, pos.wy, pos.voxelSize); _ = map.refCount.fetchAdd(1, .monotonic); for(&map.startHeight) |*val| { - val.* = std.mem.readInt(i16, data[0..2], .big); - data = data[2..]; + val.* = try ligthMapReader.readInt(i16); } renderer.mesh_storage.updateLightMap(map); } pub fn sendLightMap(conn: *Connection, map: *main.server.terrain.LightMap.LightMapFragment) void { - var uncompressedData: [@sizeOf(@TypeOf(map.startHeight))]u8 = undefined; // TODO: #15280 - for(&map.startHeight, 0..) |val, i| { - std.mem.writeInt(i16, uncompressedData[2*i ..][0..2], val, .big); + var ligthMapWriter = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, @sizeOf(@TypeOf(map.startHeight))); + defer ligthMapWriter.deinit(); + 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); - const data = main.stackAllocator.alloc(u8, 9 + compressedData.len); - defer main.stackAllocator.free(data); - @memcpy(data[9..], compressedData); - std.mem.writeInt(i32, data[0..4], map.pos.wx, .big); - std.mem.writeInt(i32, data[4..8], map.pos.wy, .big); - std.mem.writeInt(u8, data[8..9], map.pos.voxelSizeShift, .big); - conn.sendImportant(id, data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, 9 + compressedData.len); + defer writer.deinit(); + writer.writeInt(i32, map.pos.wx); + writer.writeInt(i32, map.pos.wy); + writer.writeInt(u8, map.pos.voxelSizeShift); + writer.writeSlice(compressedData); + conn.sendImportant(id, writer.data.items); } }; pub const inventory = struct { pub const id: u8 = 13; 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(data[0] == 0xff) return error.InvalidPacket; - try items.Inventory.Sync.ServerSide.receiveCommand(user, data); + if(reader.remaining[0] == 0xff) return error.InvalidPacket; + try items.Inventory.Sync.ServerSide.receiveCommand(user, reader); } else { - if(data[0] == 0xff) { // Confirmation - items.Inventory.Sync.ClientSide.receiveConfirmation(data[1..]); - } else if(data[0] == 0xfe) { // Failure + const typ = try reader.readInt(u8); + if(typ == 0xff) { // Confirmation + try items.Inventory.Sync.ClientSide.receiveConfirmation(reader); + } else if(typ == 0xfe) { // Failure items.Inventory.Sync.ClientSide.receiveFailure(); } 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 { std.debug.assert(conn.user == null); - var data = main.stackAllocator.alloc(u8, _data.len + 1); - defer main.stackAllocator.free(data); - data[0] = @intFromEnum(payloadType); - std.debug.assert(data[0] != 0xff); - @memcpy(data[1..], _data); - conn.sendImportant(id, data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, _data.len + 1); + defer writer.deinit(); + writer.writeEnum(items.Inventory.Command.PayloadType, payloadType); + std.debug.assert(writer.data.items[0] != 0xff); + writer.writeSlice(_data); + conn.sendImportant(id, writer.data.items); } pub fn sendConfirmation(conn: *Connection, _data: []const u8) void { std.debug.assert(conn.user != null); - var data = main.stackAllocator.alloc(u8, _data.len + 1); - defer main.stackAllocator.free(data); - data[0] = 0xff; - @memcpy(data[1..], _data); - conn.sendImportant(id, data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, _data.len + 1); + defer writer.deinit(); + writer.writeInt(u8, 0xff); + writer.writeSlice(_data); + conn.sendImportant(id, writer.data.items); } pub fn sendFailure(conn: *Connection) void { std.debug.assert(conn.user != null); @@ -1213,11 +1221,11 @@ pub const Protocols = struct { } pub fn sendSyncOperation(conn: *Connection, _data: []const u8) void { std.debug.assert(conn.user != null); - var data = main.stackAllocator.alloc(u8, _data.len + 1); - defer main.stackAllocator.free(data); - data[0] = 0; - @memcpy(data[1..], _data); - conn.sendImportant(id, data); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, networkEndian, _data.len + 1); + defer writer.deinit(); + writer.writeInt(u8, 0); + writer.writeSlice(_data); + conn.sendImportant(id, writer.data.items); } }; }; @@ -1680,7 +1688,8 @@ pub const Connection = struct { // MARK: Connection if(Protocols.isAsynchronous[protocol]) { ProtocolTask.schedule(self, protocol, data); } else { - try prot(self, data); + var reader = utils.BinaryReader.init(data, networkEndian); + try prot(self, &reader); } } else { 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) { - 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; } self.receivedPackets[0].append(id); @@ -1737,9 +1746,11 @@ pub const Connection = struct { // MARK: Connection self.receiveKeepAlive(data[1..]); } else { if(Protocols.list[protocol]) |prot| { - try prot(self, data[1..]); + var reader = utils.BinaryReader.init(data[1..], networkEndian); + try prot(self, &reader); } else { 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 { 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 }; } diff --git a/src/utils.zig b/src/utils.zig index 97a02582..2062c91c 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -1843,3 +1843,77 @@ pub const Side = enum { client, 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); + } +};