diff --git a/src/Inventory.zig b/src/Inventory.zig index a9706099a6..a72bc044d8 100644 --- a/src/Inventory.zig +++ b/src/Inventory.zig @@ -37,7 +37,7 @@ pub const client = struct { // MARK: client serverToClientMap.deinit(); } - fn nextId() InventoryId { + pub fn nextId() InventoryId { main.sync.client.mutex.lock(); defer main.sync.client.mutex.unlock(); if (freeIdList.popOrNull()) |id| { @@ -88,6 +88,42 @@ pub const client = struct { // MARK: client } return null; } + + var pendingChestOpen: ?ClientInventory = null; + + fn buildFromResponse(clientId: InventoryId, source: Source, len: usize, reader: *BinaryReader) Inventory { + const inv: Inventory = .{ + ._items = main.globalAllocator.alloc(ItemStack, len), + .id = clientId, + .source = source, + .callbacks = .{}, + }; + for (inv._items) |*item| item.* = .{}; + inv.fromBytes(reader); + return inv; + } + + pub fn receiveChestOpenResponse(clientId: InventoryId, pos: Vec3i, serverId: InventoryId, reader: *BinaryReader) void { + main.sync.client.mutex.lock(); + defer main.sync.client.mutex.unlock(); + const inv = buildFromResponse(clientId, .{.blockInventory = pos}, main.block_entity.BlockEntityTypes.@"cubyz:chest".inventorySize, reader); + mapServerId(serverId, inv); + pendingChestOpen = .{.super = inv, .type = .serverShared}; + } + + pub fn cancelChestOpen(clientId: InventoryId) void { + main.sync.client.mutex.lock(); + defer main.sync.client.mutex.unlock(); + freeId(clientId); + std.log.err("Server rejected request to open chest.", .{}); + } + + pub fn takePendingChestOpen() ?ClientInventory { + main.sync.client.mutex.lock(); + defer main.sync.client.mutex.unlock(); + defer pendingChestOpen = null; + return pendingChestOpen; + } }; pub const server = struct { // MARK: server diff --git a/src/callbacks/block/client/open_chest.zig b/src/callbacks/block/client/open_chest.zig index a4846117ab..19e1ce5cbb 100644 --- a/src/callbacks/block/client/open_chest.zig +++ b/src/callbacks/block/client/open_chest.zig @@ -17,11 +17,8 @@ pub fn run(_: *anyopaque, params: main.callbacks.ClientBlockCallback.Params) mai } main.network.protocols.blockEntityUpdate.sendClientDataUpdateToServer(main.game.world.?.conn, params.blockPos); - const inventory = main.items.Inventory.ClientInventory.init(main.globalAllocator, main.block_entity.BlockEntityTypes.@"cubyz:chest".inventorySize, .serverShared, .{.blockInventory = params.blockPos}, .{}); - - main.gui.windowlist.chest.setInventory(inventory); - main.gui.openWindow("chest"); - main.Window.setMouseGrabbed(false); + const clientId = main.items.Inventory.client.nextId(); + main.network.protocols.chestOpen.sendRequest(main.game.world.?.conn, clientId, params.blockPos); return .handled; } diff --git a/src/game.zig b/src/game.zig index 89e6b9273d..565b94b78a 100644 --- a/src/game.zig +++ b/src/game.zig @@ -585,6 +585,8 @@ pub fn update(deltaTime: f64) void { // MARK: update() restart(); } + main.gui.windowlist.chest.checkPendingOpen(); + physics.calculateVolumeProperties(.client, &Player.volumeProperties, Player.super.pos, Player.outerBoundingBox, physics.playerAirTerminalVelocity); if (Player.isFlying.load(.monotonic)) { Player.friction = .{.current = 20, .mobile = 20}; diff --git a/src/gui/windows/chest.zig b/src/gui/windows/chest.zig index f41dd7cbcc..a39e2282cf 100644 --- a/src/gui/windows/chest.zig +++ b/src/gui/windows/chest.zig @@ -39,6 +39,13 @@ pub fn setInventory(selectedInventory: main.items.Inventory.ClientInventory) voi openInventory = selectedInventory; } +pub fn checkPendingOpen() void { + const inv = main.items.Inventory.client.takePendingChestOpen() orelse return; + setInventory(inv); + main.gui.openWindow("chest"); + main.Window.setMouseGrabbed(false); +} + pub fn onOpen() void { const list = VerticalList.init(.{padding, padding + 16}, 300, 0); diff --git a/src/network/protocols.zig b/src/network/protocols.zig index 6201e1f66e..04045cfc00 100644 --- a/src/network/protocols.zig +++ b/src/network/protocols.zig @@ -1105,3 +1105,69 @@ pub const EntityComponentUpdate = struct { // MARK: EntityComponentUpdate conn.send(.secure, id, writer.data.items); } }; + +pub const chestOpen = struct { + pub const id: u8 = 16; + + fn clientReceive(_: *Connection, reader: *utils.BinaryReader) !void { + const InventoryId = items.Inventory.InventoryId; + const success = try reader.readInt(u8); + const clientId = try reader.readEnum(InventoryId); + if (success == 0) { + items.Inventory.client.cancelChestOpen(clientId); + return; + } + const pos = try reader.readVec(Vec3i); + const serverId = try reader.readEnum(InventoryId); + items.Inventory.client.receiveChestOpenResponse(clientId, pos, serverId, reader); + } + fn serverReceive(conn: *Connection, reader: *utils.BinaryReader) !void { + const user = conn.user.?; + user.receiveChestOpenRequest(reader.remaining); + } + + pub fn sendRequest(conn: *Connection, clientId: items.Inventory.InventoryId, pos: Vec3i) void { + std.debug.assert(conn.user == null); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 16); + defer writer.deinit(); + writer.writeEnum(items.Inventory.InventoryId, clientId); + writer.writeVec(Vec3i, pos); + conn.send(.secure, id, writer.data.items); + } + + pub fn process(user: *main.server.User, reader: *utils.BinaryReader) void { + const InventoryId = items.Inventory.InventoryId; + const clientId = reader.readEnum(InventoryId) catch return; + const pos = reader.readVec(Vec3i) catch return; + main.items.Inventory.server.createInventory(user, clientId, main.block_entity.BlockEntityTypes.@"cubyz:chest".inventorySize, .{.blockInventory = pos}) catch { + sendFailure(user.conn, clientId); + return; + }; + const inv = main.items.Inventory.server.getInventory(user, clientId) orelse { + sendFailure(user.conn, clientId); + return; + }; + sendResponse(user.conn, clientId, pos, inv); + } + + fn sendResponse(conn: *Connection, clientId: items.Inventory.InventoryId, pos: Vec3i, inv: items.Inventory) void { + std.debug.assert(conn.isServerSide()); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 32); + defer writer.deinit(); + writer.writeInt(u8, 1); + writer.writeEnum(items.Inventory.InventoryId, clientId); + writer.writeVec(Vec3i, pos); + writer.writeEnum(items.Inventory.InventoryId, inv.id); + inv.toBytes(&writer); + conn.send(.secure, id, writer.data.items); + } + + fn sendFailure(conn: *Connection, clientId: items.Inventory.InventoryId) void { + std.debug.assert(conn.isServerSide()); + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 8); + defer writer.deinit(); + writer.writeInt(u8, 0); + writer.writeEnum(items.Inventory.InventoryId, clientId); + conn.send(.secure, id, writer.data.items); + } +}; diff --git a/src/server/server.zig b/src/server/server.zig index 0fbc82d540..7ff553c66b 100644 --- a/src/server/server.zig +++ b/src/server/server.zig @@ -147,6 +147,7 @@ pub const User = struct { // MARK: User mutex: main.utils.Mutex = .{}, inventoryCommands: main.List([]const u8) = .empty, + chestOpenRequests: main.List([]const u8) = .empty, pub const State = enum { awaitingKeyVerification, connectedVerified, awaitingReloadVerified }; @@ -229,6 +230,10 @@ pub const User = struct { // MARK: User main.globalAllocator.free(commandData); } self.inventoryCommands.deinit(main.globalAllocator); + for (self.chestOpenRequests.items) |requestData| { + main.globalAllocator.free(requestData); + } + self.chestOpenRequests.deinit(main.globalAllocator); self.jobQueue.deinit(); } @@ -496,6 +501,9 @@ pub const User = struct { // MARK: User const commands = self.inventoryCommands; defer commands.deinit(main.globalAllocator); self.inventoryCommands = .empty; + const chestOpenRequests = self.chestOpenRequests; + defer chestOpenRequests.deinit(main.globalAllocator); + self.chestOpenRequests = .empty; self.mutex.unlock(); for (commands.items) |commandData| { @@ -512,6 +520,12 @@ pub const User = struct { // MARK: User }; } + for (chestOpenRequests.items) |requestData| { + defer main.globalAllocator.free(requestData); + var reader: BinaryReader = .init(requestData); + main.network.protocols.chestOpen.process(self, &reader); + } + self.mutex.lock(); defer self.mutex.unlock(); var time = @as(i16, @truncate(main.timestamp().toMilliseconds())) -% main.settings.entityLookback; @@ -536,6 +550,12 @@ pub const User = struct { // MARK: User self.inventoryCommands.append(main.globalAllocator, main.globalAllocator.dupe(u8, commandData)); } + pub fn receiveChestOpenRequest(self: *User, requestData: []const u8) void { + self.mutex.lock(); + defer self.mutex.unlock(); + self.chestOpenRequests.append(main.globalAllocator, main.globalAllocator.dupe(u8, requestData)); + } + pub fn receiveData(self: *User, reader: *BinaryReader) !void { self.mutex.lock(); defer self.mutex.unlock();