Ensures that all chunks are stored within 5 seconds of an update.

Closes #348
This commit is contained in:
IntegratedQuantum 2024-05-14 20:44:28 +02:00
parent cca1c14242
commit 814bcedcba
5 changed files with 68 additions and 3 deletions

View File

@ -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 {

View File

@ -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);

View File

@ -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 {

View File

@ -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();
}
};

View File

@ -37,6 +37,9 @@ pub var guiScale: ?f32 = null;
pub var musicVolume: f32 = 1;
pub var storageTime: i64 = 5000;
pub var developerAutoEnterWorld: []const u8 = "";