From c85239307604282e5b5937f60b5cec7ea820ede7 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com> Date: Wed, 21 May 2025 20:42:17 +0200 Subject: [PATCH] Refactor how block entity unloading works and unloading things on the client side (#1475) extracted from #1446 --- src/block_entity.zig | 32 ++++++++++++++++++++------------ src/chunk.zig | 21 +++++++++++++++++++++ src/renderer/chunk_meshing.zig | 1 + 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/block_entity.zig b/src/block_entity.zig index 993acdf4..bc9b41c7 100644 --- a/src/block_entity.zig +++ b/src/block_entity.zig @@ -18,9 +18,9 @@ pub const BlockEntityType = struct { const VTable = struct { onLoadClient: *const fn(pos: Vec3i, chunk: *Chunk) void, - onUnloadClient: *const fn(pos: Vec3i, chunk: *Chunk) void, + onUnloadClient: *const fn(dataIndex: BlockEntityIndex) void, onLoadServer: *const fn(pos: Vec3i, chunk: *Chunk) void, - onUnloadServer: *const fn(pos: Vec3i, chunk: *Chunk) void, + onUnloadServer: *const fn(dataIndex: BlockEntityIndex) void, onPlaceClient: *const fn(pos: Vec3i, chunk: *Chunk) void, onBreakClient: *const fn(pos: Vec3i, chunk: *Chunk) void, onPlaceServer: *const fn(pos: Vec3i, chunk: *Chunk) void, @@ -45,14 +45,14 @@ pub const BlockEntityType = struct { pub inline fn onLoadClient(self: *BlockEntityType, pos: Vec3i, chunk: *Chunk) void { return self.vtable.onLoadClient(pos, chunk); } - pub inline fn onUnloadClient(self: *BlockEntityType, pos: Vec3i, chunk: *Chunk) void { - return self.vtable.onUnloadClient(pos, chunk); + pub inline fn onUnloadClient(self: *BlockEntityType, dataIndex: BlockEntityIndex) void { + return self.vtable.onUnloadClient(dataIndex); } pub inline fn onLoadServer(self: *BlockEntityType, pos: Vec3i, chunk: *Chunk) void { return self.vtable.onLoadServer(pos, chunk); } - pub inline fn onUnloadServer(self: *BlockEntityType, pos: Vec3i, chunk: *Chunk) void { - return self.vtable.onUnloadServer(pos, chunk); + pub inline fn onUnloadServer(self: *BlockEntityType, dataIndex: BlockEntityIndex) void { + return self.vtable.onUnloadServer(dataIndex); } pub inline fn onPlaceClient(self: *BlockEntityType, pos: Vec3i, chunk: *Chunk) void { return self.vtable.onPlaceClient(pos, chunk); @@ -117,6 +117,13 @@ fn BlockEntityDataStorage(T: type) type { chunk.blockPosToEntityDataMap.put(main.globalAllocator.allocator, blockIndex, @intCast(dataIndex)) catch unreachable; chunk.blockPosToEntityDataMapMutex.unlock(); } + pub fn removeAtIndex(dataIndex: BlockEntityIndex) void { + main.utils.assertLocked(&mutex); + freeIndexList.append(main.globalAllocator, dataIndex); + storage.remove(dataIndex) catch |err| { + std.log.err("Error while removing block entity: {s}", .{@errorName(err)}); + }; + } pub fn remove(pos: Vec3i, chunk: *Chunk) void { mutex.lock(); defer mutex.unlock(); @@ -133,10 +140,7 @@ fn BlockEntityDataStorage(T: type) type { }; const dataIndex = entry.value; - freeIndexList.append(main.globalAllocator, dataIndex); - storage.remove(dataIndex) catch |err| { - std.log.err("Error while remvoing block entity at position {}: {s}", .{pos, @errorName(err)}); - }; + removeAtIndex(dataIndex); } pub fn get(pos: Vec3i, chunk: *Chunk) ?*DataT { main.utils.assertLocked(&mutex); @@ -175,9 +179,13 @@ pub const BlockEntityTypes = struct { } pub fn onLoadClient(_: Vec3i, _: *Chunk) void {} - pub fn onUnloadClient(_: Vec3i, _: *Chunk) void {} + pub fn onUnloadClient(_: BlockEntityIndex) void {} pub fn onLoadServer(_: Vec3i, _: *Chunk) void {} - pub fn onUnloadServer(_: Vec3i, _: *Chunk) void {} + pub fn onUnloadServer(dataIndex: BlockEntityIndex) void { + StorageServer.mutex.lock(); + defer StorageServer.mutex.unlock(); + StorageServer.removeAtIndex(dataIndex); + } pub fn onPlaceClient(_: Vec3i, _: *Chunk) void {} pub fn onBreakClient(_: Vec3i, _: *Chunk) void {} pub fn onPlaceServer(_: Vec3i, _: *Chunk) void {} diff --git a/src/chunk.zig b/src/chunk.zig index aa86b689..2665e7b9 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -284,6 +284,27 @@ pub const Chunk = struct { // MARK: Chunk memoryPool.destroy(@alignCast(self)); } + pub fn unloadBlockEntities(self: *Chunk, comptime side: main.utils.Side) void { + self.blockPosToEntityDataMapMutex.lock(); + defer self.blockPosToEntityDataMapMutex.unlock(); + var iterator = self.blockPosToEntityDataMap.iterator(); + while(iterator.next()) |elem| { + const index = elem.key_ptr.*; + const entityDataIndex = elem.value_ptr.*; + const block = self.data.getValue(index); + const blockEntity = block.blockEntity() orelse unreachable; + switch(side) { + .client => { + blockEntity.onUnloadClient(entityDataIndex); + }, + .server => { + blockEntity.onUnloadServer(entityDataIndex); + }, + } + } + self.blockPosToEntityDataMap.clearRetainingCapacity(); + } + /// Updates a block if it is inside this chunk. /// Does not do any bound checks. They are expected to be done with the `liesInChunk` function. pub fn updateBlock(self: *Chunk, _x: i32, _y: i32, _z: i32, newBlock: Block) void { diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index c65b02bc..8fecfd0d 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -694,6 +694,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh chunkBuffer.free(self.chunkAllocation); self.opaqueMesh.deinit(); self.transparentMesh.deinit(); + self.chunk.unloadBlockEntities(.client); self.chunk.deinit(); main.globalAllocator.free(self.currentSorting); main.globalAllocator.free(self.sortingOutputBuffer);