hopefully save the hand and also when the player leaves or server closes (#943)

* hopefully save the hand and also when the player leaves or server closes

* fixes

* change it to check the user id

* fix

* check pointers

* fix compilation error

* fix other bugs

* put player id in source

* fix error

* remove one line of code

* fix serialization

* revert < 22 to < 14

* change

* fix error where inventory not saved on alt f4

* change connected to a regular bool

* remove isConnected function
This commit is contained in:
OneAvargeCoder193 2025-01-26 15:30:23 -05:00 committed by GitHub
parent fabc5c1aef
commit 1de5efbf6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 44 additions and 15 deletions

View File

@ -310,14 +310,14 @@ pub const Sync = struct { // MARK: Sync
} }
} }
}, },
.playerInventory, .other => {}, .playerInventory, .hand, .other => {},
.alreadyFreed => unreachable, .alreadyFreed => unreachable,
} }
const inventory = ServerInventory.init(len, typ, source); const inventory = ServerInventory.init(len, typ, source);
switch (source) { switch (source) {
.sharedTestingInventory => {}, .sharedTestingInventory => {},
.playerInventory => { .playerInventory, .hand => {
const dest: []u8 = main.stackAllocator.alloc(u8, std.base64.url_safe.Encoder.calcSize(user.name.len)); const dest: []u8 = main.stackAllocator.alloc(u8, std.base64.url_safe.Encoder.calcSize(user.name.len));
defer main.stackAllocator.free(dest); defer main.stackAllocator.free(dest);
const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name); const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name);
@ -328,7 +328,7 @@ pub const Sync = struct { // MARK: Sync
const playerData = main.files.readToZon(main.stackAllocator, path) catch .null; const playerData = main.files.readToZon(main.stackAllocator, path) catch .null;
defer playerData.deinit(main.stackAllocator); defer playerData.deinit(main.stackAllocator);
const inventoryZon = playerData.getChild("inventory"); const inventoryZon = playerData.getChild(@tagName(source));
inventory.inv.loadFromZon(inventoryZon); inventory.inv.loadFromZon(inventoryZon);
}, },
@ -352,10 +352,10 @@ pub const Sync = struct { // MARK: Sync
return inventories.items[serverId].inv; return inventories.items[serverId].inv;
} }
pub fn getInventoryFromSource(source: SourceType) ?Inventory { pub fn getInventoryFromSource(source: Source) ?Inventory {
main.utils.assertLocked(&mutex); main.utils.assertLocked(&mutex);
for(inventories.items) |inv| { for(inventories.items) |inv| {
if (inv.source == source) { if (std.meta.eql(inv.source, source)) {
return inv.inv; return inv.inv;
} }
} }
@ -1059,8 +1059,14 @@ pub const Command = struct { // MARK: Command
std.mem.writeInt(usize, data.addMany(8)[0..8], self.inv._items.len, .big); std.mem.writeInt(usize, data.addMany(8)[0..8], self.inv._items.len, .big);
data.append(@intFromEnum(self.inv.type)); data.append(@intFromEnum(self.inv.type));
data.append(@intFromEnum(self.source)); data.append(@intFromEnum(self.source));
switch (self.source) {
.playerInventory, .hand => |val| {
std.mem.writeInt(u32, data.addMany(4)[0..4], val, .big);
},
else => {}
}
switch(self.source) { switch(self.source) {
.playerInventory, .sharedTestingInventory, .other => {}, .playerInventory, .sharedTestingInventory, .hand, .other => {},
.alreadyFreed => unreachable, .alreadyFreed => unreachable,
} }
} }
@ -1073,8 +1079,9 @@ pub const Command = struct { // MARK: Command
const typ: Inventory.Type = @enumFromInt(data[12]); const typ: Inventory.Type = @enumFromInt(data[12]);
const sourceType: SourceType = @enumFromInt(data[13]); const sourceType: SourceType = @enumFromInt(data[13]);
const source: Source = switch(sourceType) { const source: Source = switch(sourceType) {
.playerInventory => .{.playerInventory = {}}, .playerInventory => .{.playerInventory = std.mem.readInt(u32, data[14..18], .big)},
.sharedTestingInventory => .{.sharedTestingInventory = {}}, .sharedTestingInventory => .{.sharedTestingInventory = {}},
.hand => .{.hand = std.mem.readInt(u32, data[14..18], .big)},
.other => .{.other = {}}, .other => .{.other = {}},
.alreadyFreed => unreachable, .alreadyFreed => unreachable,
}; };
@ -1647,12 +1654,14 @@ const SourceType = enum(u8) {
alreadyFreed = 0, alreadyFreed = 0,
playerInventory = 1, playerInventory = 1,
sharedTestingInventory = 2, sharedTestingInventory = 2,
hand = 3,
other = 0xff, // TODO: List every type separately here. other = 0xff, // TODO: List every type separately here.
}; };
const Source = union(SourceType) { const Source = union(SourceType) {
alreadyFreed: void, alreadyFreed: void,
playerInventory: void, playerInventory: u32,
sharedTestingInventory: void, sharedTestingInventory: void,
hand: u32,
other: void, other: void,
}; };
@ -1691,7 +1700,13 @@ fn _init(allocator: NeverFailingAllocator, _size: usize, _type: Type, side: Side
} }
pub fn deinit(self: Inventory, allocator: NeverFailingAllocator) void { pub fn deinit(self: Inventory, allocator: NeverFailingAllocator) void {
Sync.ClientSide.executeCommand(.{.close = .{.inv = self, .allocator = allocator}}); if (main.game.world.?.connected) {
Sync.ClientSide.executeCommand(.{.close = .{.inv = self, .allocator = allocator}});
} else {
Sync.ClientSide.mutex.lock();
defer Sync.ClientSide.mutex.unlock();
self._deinit(allocator, .client);
}
} }
fn _deinit(self: Inventory, allocator: NeverFailingAllocator, side: Side) void { fn _deinit(self: Inventory, allocator: NeverFailingAllocator, side: Side) void {

View File

@ -583,6 +583,7 @@ pub const World = struct { // MARK: World
milliTime: i64, milliTime: i64,
gameTime: Atomic(i64) = .init(0), gameTime: Atomic(i64) = .init(0),
spawn: Vec3f = undefined, spawn: Vec3f = undefined,
connected: bool = true,
blockPalette: *assets.Palette = undefined, blockPalette: *assets.Palette = undefined,
biomePalette: *assets.Palette = undefined, biomePalette: *assets.Palette = undefined,
itemDrops: ClientItemDropManager = undefined, itemDrops: ClientItemDropManager = undefined,
@ -595,6 +596,7 @@ pub const World = struct { // MARK: World
.name = "client", .name = "client",
.milliTime = std.time.milliTimestamp(), .milliTime = std.time.milliTimestamp(),
}; };
self.itemDrops.init(main.globalAllocator, self); self.itemDrops.init(main.globalAllocator, self);
network.Protocols.handShake.clientSide(self.conn, settings.playerName); network.Protocols.handShake.clientSide(self.conn, settings.playerName);
@ -607,15 +609,17 @@ pub const World = struct { // MARK: World
} }
pub fn deinit(self: *World) void { pub fn deinit(self: *World) void {
self.conn.deinit();
self.connected = false;
// TODO: Close all world related guis. // TODO: Close all world related guis.
main.gui.inventory.deinit(); main.gui.inventory.deinit();
main.gui.deinit(); main.gui.deinit();
main.gui.init(); main.gui.init();
Player.inventory.deinit(main.globalAllocator); Player.inventory.deinit(main.globalAllocator);
main.threadPool.clear(); main.threadPool.clear();
self.conn.deinit();
self.itemDrops.deinit(); self.itemDrops.deinit();
self.blockPalette.deinit(); self.blockPalette.deinit();
self.biomePalette.deinit(); self.biomePalette.deinit();
@ -642,7 +646,7 @@ pub const World = struct { // MARK: World
try assets.loadWorldAssets("serverAssets", self.blockPalette, self.biomePalette); try assets.loadWorldAssets("serverAssets", self.blockPalette, self.biomePalette);
Player.loadFrom(zon.getChild("player")); Player.loadFrom(zon.getChild("player"));
Player.id = zon.get(u32, "player_id", std.math.maxInt(u32)); Player.id = zon.get(u32, "player_id", std.math.maxInt(u32));
Player.inventory = Inventory.init(main.globalAllocator, 32, .normal, .playerInventory); Player.inventory = Inventory.init(main.globalAllocator, 32, .normal, .{.playerInventory = Player.id});
} }
pub fn update(self: *World) void { pub fn update(self: *World) void {

View File

@ -593,7 +593,7 @@ pub const inventory = struct { // MARK: inventory
var initialized: bool = false; var initialized: bool = false;
pub fn init() void { pub fn init() void {
carried = Inventory.init(main.globalAllocator, 1, .normal, .other); carried = Inventory.init(main.globalAllocator, 1, .normal, .{.hand = main.game.Player.id});
leftClickSlots = .init(main.globalAllocator); leftClickSlots = .init(main.globalAllocator);
rightClickSlots = .init(main.globalAllocator); rightClickSlots = .init(main.globalAllocator);
carriedItemSlot = ItemSlot.init(.{0, 0}, carried, 0, .default, .normal); carriedItemSlot = ItemSlot.init(.{0, 0}, carried, 0, .default, .normal);

View File

@ -70,6 +70,12 @@ pub const User = struct { // MARK: User
pub fn deinit(self: *User) void { pub fn deinit(self: *User) void {
std.debug.assert(self.refCount.load(.monotonic) == 0); std.debug.assert(self.refCount.load(.monotonic) == 0);
world.?.savePlayer(self) catch |err| {
std.log.err("Failed to save player: {s}", .{@errorName(err)});
return;
};
main.items.Inventory.Sync.ServerSide.disconnectUser(self); main.items.Inventory.Sync.ServerSide.disconnectUser(self);
std.debug.assert(self.inventoryClientToServerIdMap.count() == 0); // leak std.debug.assert(self.inventoryClientToServerIdMap.count() == 0); // leak
self.inventoryClientToServerIdMap.deinit(); self.inventoryClientToServerIdMap.deinit();

View File

@ -815,8 +815,12 @@ pub const ServerWorld = struct { // MARK: ServerWorld
{ {
main.items.Inventory.Sync.ServerSide.mutex.lock(); main.items.Inventory.Sync.ServerSide.mutex.lock();
defer main.items.Inventory.Sync.ServerSide.mutex.unlock(); defer main.items.Inventory.Sync.ServerSide.mutex.unlock();
if (main.items.Inventory.Sync.ServerSide.getInventoryFromSource(.playerInventory)) |inv| { if (main.items.Inventory.Sync.ServerSide.getInventoryFromSource(.{.playerInventory = user.id})) |inv| {
playerZon.put("inventory", inv.save(main.stackAllocator)); playerZon.put("playerInventory", inv.save(main.stackAllocator));
}
if (main.items.Inventory.Sync.ServerSide.getInventoryFromSource(.{.hand = user.id})) |inv| {
playerZon.put("hand", inv.save(main.stackAllocator));
} }
} }