From 814bcedcba150c3a11ce5b7c1ef874b8d8580fd3 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Tue, 14 May 2024 20:44:28 +0200 Subject: [PATCH] Ensures that all chunks are stored within 5 seconds of an update. Closes #348 --- src/chunk.zig | 12 +++++++++-- src/network.zig | 2 ++ src/server/storage.zig | 5 ++++- src/server/world.zig | 49 ++++++++++++++++++++++++++++++++++++++++++ src/settings.zig | 3 +++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/chunk.zig b/src/chunk.zig index 6054ba43..b1885944 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -211,6 +211,14 @@ pub const Chunk = struct { memoryPoolMutex.unlock(); } + fn setChanged(self: *Chunk) void { + main.utils.assertLocked(&self.mutex); + if(!self.wasChanged) { + self.wasChanged = true; + main.server.world.?.queueChunkUpdate(self); + } + } + /// Checks if the given relative coordinates lie within the bounds of this chunk. pub fn liesInChunk(self: *const Chunk, x: i32, y: i32, z: i32) bool { return x >= 0 and x < self.width @@ -258,7 +266,7 @@ pub const Chunk = struct { const z = _z >> self.voxelSizeShift; const index = getIndex(x, y, z); self.data.setValue(index, newBlock); - self.wasChanged = true; + self.setChanged(); } /// Updates a block if it is inside this chunk. Should be used in generation to prevent accidently storing these as changes. @@ -348,7 +356,7 @@ pub const Chunk = struct { } } - self.wasChanged = true; + self.setChanged(); } pub fn save(self: *Chunk, world: *main.server.ServerWorld) void { diff --git a/src/network.zig b/src/network.zig index 5c0a5736..a171c87a 100644 --- a/src/network.zig +++ b/src/network.zig @@ -820,6 +820,8 @@ pub const Protocols = struct { if(conn.user != null) { // TODO: Send update event to other players. const mask = ~@as(i32, chunk.chunkMask); const ch = main.server.world.?.getOrGenerateChunk(.{.wx = x & mask, .wy = y & mask, .wz = z & mask, .voxelSize = 1}); + ch.mutex.lock(); + defer ch.mutex.unlock(); ch.updateBlockAndSetChanged(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask, newBlock); } else { renderer.mesh_storage.updateBlock(x, y, z, newBlock); diff --git a/src/server/storage.zig b/src/server/storage.zig index a4779fc6..ab440130 100644 --- a/src/server/storage.zig +++ b/src/server/storage.zig @@ -155,10 +155,13 @@ pub const RegionFile = struct { pub fn storeChunk(self: *RegionFile, ch: []const u8, relX: usize, relY: usize, relZ: usize) void { self.mutex.lock(); defer self.mutex.unlock(); - self.modified = true; const index = getIndex(relX, relY, relZ); self.chunks[index] = main.globalAllocator.realloc(self.chunks[index], ch.len); @memcpy(self.chunks[index], ch); + if(!self.modified) { + self.modified = true; + main.server.world.?.queueRegionFileUpdate(self); + } } pub fn getChunk(self: *RegionFile, allocator: main.utils.NeverFailingAllocator, relX: usize, relY: usize, relZ: usize) ?[]const u8 { diff --git a/src/server/world.zig b/src/server/world.zig index ec8064d6..72cc6a26 100644 --- a/src/server/world.zig +++ b/src/server/world.zig @@ -320,6 +320,21 @@ pub const ServerWorld = struct { wio: WorldIO = undefined, + mutex: std.Thread.Mutex = .{}, + + chunkUpdateQueue: main.utils.CircularBufferQueue(ChunkUpdateRequest), + regionUpdateQueue: main.utils.CircularBufferQueue(RegionUpdateRequest), + + const ChunkUpdateRequest = struct { + ch: *Chunk, + milliTimeStamp: i64, + }; + + const RegionUpdateRequest = struct { + region: *storage.RegionFile, + milliTimeStamp: i64, + }; + pub fn init(name: []const u8, nullGeneratorSettings: ?JsonElement) !*ServerWorld { const self = main.globalAllocator.create(ServerWorld); errdefer main.globalAllocator.destroy(self); @@ -329,6 +344,8 @@ pub const ServerWorld = struct { .lastUnimportantDataSent = std.time.milliTimestamp(), .seed = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp()))), .name = main.globalAllocator.dupe(u8, name), + .chunkUpdateQueue = main.utils.CircularBufferQueue(ChunkUpdateRequest).init(main.globalAllocator, 256), + .regionUpdateQueue = main.utils.CircularBufferQueue(RegionUpdateRequest).init(main.globalAllocator, 256), }; self.itemDropManager.init(main.globalAllocator, self, self.gravity); errdefer self.itemDropManager.deinit(); @@ -372,6 +389,14 @@ pub const ServerWorld = struct { } pub fn deinit(self: *ServerWorld) void { + while(self.chunkUpdateQueue.dequeue()) |updateRequest| { + updateRequest.ch.save(self); + } + self.chunkUpdateQueue.deinit(); + while(self.regionUpdateQueue.dequeue()) |updateRequest| { + updateRequest.region.store(); + } + self.regionUpdateQueue.deinit(); self.chunkManager.deinit(); self.itemDropManager.deinit(); self.blockPalette.deinit(); @@ -462,6 +487,19 @@ pub const ServerWorld = struct { // Item Entities self.itemDropManager.update(deltaTime); + + // Store chunks and regions. + // Stores at least one chunk and one region per iteration. + // All chunks and regions will be stored within the storage time. + const insertionTime = newTime -% main.settings.storageTime; + while(self.chunkUpdateQueue.dequeue()) |updateRequest| { + updateRequest.ch.save(self); + if(updateRequest.milliTimeStamp -% insertionTime <= 0) break; + } + while(self.regionUpdateQueue.dequeue()) |updateRequest| { + updateRequest.region.store(); + if(updateRequest.milliTimeStamp -% insertionTime <= 0) break; + } } pub fn queueChunks(self: *ServerWorld, positions: []ChunkPosition, source: ?*User) void { @@ -504,4 +542,15 @@ pub const ServerWorld = struct { return Block {.typ = 0, .data = 0}; } + pub fn queueChunkUpdate(self: *ServerWorld, ch: *Chunk) void { + self.mutex.lock(); + self.chunkUpdateQueue.enqueue(.{.ch = ch, .milliTimeStamp = std.time.milliTimestamp()}); + self.mutex.unlock(); + } + + pub fn queueRegionFileUpdate(self: *ServerWorld, region: *storage.RegionFile) void { + self.mutex.lock(); + self.regionUpdateQueue.enqueue(.{.region = region, .milliTimeStamp = std.time.milliTimestamp()}); + self.mutex.unlock(); + } }; diff --git a/src/settings.zig b/src/settings.zig index 9307ca2c..1cff1c7c 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -37,6 +37,9 @@ pub var guiScale: ?f32 = null; pub var musicVolume: f32 = 1; +pub var storageTime: i64 = 5000; + + pub var developerAutoEnterWorld: []const u8 = "";