diff --git a/src/blueprint.zig b/src/blueprint.zig index 4d8a66eec0..df92b91a80 100644 --- a/src/blueprint.zig +++ b/src/blueprint.zig @@ -110,15 +110,18 @@ pub const Blueprint = struct { return .{.success = self}; } - pub const PasteMode = enum {all, degradable}; - - pub fn pasteInGeneration(self: Blueprint, pos: Vec3i, chunk: *ServerChunk, mode: PasteMode) void { - switch(mode) { - inline else => |comptimeMode| _pasteInGeneration(self, pos, chunk, comptimeMode), + pub const PasteMode = enum { + all, + degradable, + fn asUpdateMode(self: PasteMode) ServerChunk.UpdateMode { + return switch(self) { + .all => .replace, + .degradable => .replaceIfDegradable, + }; } - } + }; - fn _pasteInGeneration(self: Blueprint, pos: Vec3i, chunk: *ServerChunk, comptime mode: PasteMode) void { + pub fn pasteInGeneration(self: Blueprint, pos: Vec3i, chunk: *ServerChunk, mode: PasteMode) void { const indexEndX: i32 = @min(@as(i32, chunk.super.width) - pos[0], @as(i32, @intCast(self.blocks.width))); const indexEndY: i32 = @min(@as(i32, chunk.super.width) - pos[1], @as(i32, @intCast(self.blocks.depth))); const indexEndZ: i32 = @min(@as(i32, chunk.super.width) - pos[2], @as(i32, @intCast(self.blocks.height))); @@ -136,10 +139,7 @@ pub const Blueprint = struct { const chunkX = indexX + pos[0]; const chunkY = indexY + pos[1]; const chunkZ = indexZ + pos[2]; - switch(mode) { - .all => chunk.updateBlockInGeneration(chunkX, chunkY, chunkZ, block), - .degradable => chunk.updateBlockIfDegradable(chunkX, chunkY, chunkZ, block), - } + chunk.updateBlock(mode.asUpdateMode(), .noSetChanged, chunkX, chunkY, chunkZ, block); } } } diff --git a/src/chunk.zig b/src/chunk.zig index 2b00ac7d52..5d3f8b8ac4 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -463,35 +463,41 @@ pub const ServerChunk = struct { // MARK: ServerChunk return self.super.data.getValue(pos.toIndex()); } - /// 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 updateBlockAndSetChanged(self: *ServerChunk, x: i32, y: i32, z: i32, newBlock: Block) void { - main.utils.assertLocked(&self.mutex); - const pos = BlockPos.fromLodCoords(x, y, z, self.super.voxelSizeShift); - self.super.data.setValue(pos.toIndex(), newBlock); - self.shouldStoreNeighbors = true; - self.setChanged(); - } + pub const UpdateMode = enum { + /// Always update the block. + replace, + /// Updates if current value is air or the current block is degradable. + replaceIfDegradable, + }; + + pub const SetChanged = enum { + /// Mark the chunk as changed. + setChanged, + /// Should be used in generation to prevent accidently storing the update as changes. + noSetChanged, + }; - /// Updates a block if current value is air or the current block is degradable. + /// 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 updateBlockIfDegradable(self: *ServerChunk, x: i32, y: i32, z: i32, newBlock: Block) void { + pub fn updateBlock(self: *ServerChunk, mode: UpdateMode, shouldSetChanged: SetChanged, x: i32, y: i32, z: i32, newBlock: Block) void { main.utils.assertLocked(&self.mutex); const pos = BlockPos.fromLodCoords(x, y, z, self.super.voxelSizeShift); - const oldBlock = self.super.data.getValue(pos.toIndex()); - if(oldBlock.typ == 0 or oldBlock.degradable()) { + const shouldUpdate = switch(mode) { + .replace => true, + .replaceIfDegradable => blk: { + const oldBlock = self.super.data.getValue(pos.toIndex()); + break :blk oldBlock.typ == 0 or oldBlock.degradable(); + }, + }; + if(shouldUpdate) { self.super.data.setValue(pos.toIndex(), newBlock); + if(shouldSetChanged == .setChanged) { + self.shouldStoreNeighbors = true; + self.setChanged(); + } } } - /// Updates a block if it is inside this chunk. Should be used in generation to prevent accidently storing these as changes. - /// Does not do any bound checks. They are expected to be done with the `liesInChunk` function. - pub fn updateBlockInGeneration(self: *ServerChunk, x: i32, y: i32, z: i32, newBlock: Block) void { - main.utils.assertLocked(&self.mutex); - const pos = BlockPos.fromLodCoords(x, y, z, self.super.voxelSizeShift); - self.super.data.setValue(pos.toIndex(), newBlock); - } - /// Updates a block if it is inside this chunk. Should be used in generation to prevent accidently storing these as changes. /// Does not do any bound checks. They are expected to be done with the `liesInChunk` function. pub fn updateBlockColumnInGeneration(self: *ServerChunk, x: i32, y: i32, zStartInclusive: i32, zEndInclusive: i32, newBlock: Block) void { diff --git a/src/server/terrain/biomes.zig b/src/server/terrain/biomes.zig index 2d2acdce50..1fad337f6b 100644 --- a/src/server/terrain/biomes.zig +++ b/src/server/terrain/biomes.zig @@ -481,7 +481,7 @@ pub const BlockStructure = struct { // MARK: BlockStructure continue; } if(chunk.liesInChunk(x, y, depth)) { - chunk.updateBlockInGeneration(x, y, depth, blockStack.block); + chunk.updateBlock(.replace, .noSetChanged, x, y, depth, blockStack.block); } depth -%= chunk.super.pos.voxelSize; if(depth -% minDepth <= 0) diff --git a/src/server/terrain/chunkgen/CrystalGenerator.zig b/src/server/terrain/chunkgen/CrystalGenerator.zig index 6624da467a..ff59fedb2c 100644 --- a/src/server/terrain/chunkgen/CrystalGenerator.zig +++ b/src/server/terrain/chunkgen/CrystalGenerator.zig @@ -99,10 +99,7 @@ fn considerCrystal(x: i32, y: i32, z: i32, chunk: *main.chunk.ServerChunk, seed: const dist = distSqr(@as(f32, @floatFromInt(x3)) - x2, @as(f32, @floatFromInt(y3)) - y2, @as(f32, @floatFromInt(z3)) - z2); if(dist < size*size) { if(x3 >= 0 and x3 < chunk.super.width and y3 >= 0 and y3 < chunk.super.width and z3 >= 0 and z3 < chunk.super.width) { - const block: main.blocks.Block = chunk.getBlock(x3, y3, z3); - if(block.typ == 0 or block.degradable()) { - chunk.updateBlockInGeneration(x3, y3, z3, .{.typ = typ, .data = 0}); - } + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x3, y3, z3, .{.typ = typ, .data = 0}); } } } diff --git a/src/server/terrain/chunkgen/OreGenerator.zig b/src/server/terrain/chunkgen/OreGenerator.zig index fe39e82ed2..0f3677ee63 100644 --- a/src/server/terrain/chunkgen/OreGenerator.zig +++ b/src/server/terrain/chunkgen/OreGenerator.zig @@ -103,7 +103,7 @@ fn considerCoordinates(ore: *const main.blocks.Ore, relX: f32, relY: f32, relZ: if((1 - distSqr)*ore.density >= random.nextFloat(&veinSeed)) { const stoneBlock = chunk.getBlock(curX, curY, curZ); if(chunk.getBlock(curX, curY, curZ).allowOres()) { - chunk.updateBlockInGeneration(curX, curY, curZ, .{.typ = ore.blockType, .data = stoneBlock.typ}); + chunk.updateBlock(.replace, .noSetChanged, curX, curY, curZ, .{.typ = ore.blockType, .data = stoneBlock.typ}); } } } diff --git a/src/server/terrain/chunkgen/TerrainGenerator.zig b/src/server/terrain/chunkgen/TerrainGenerator.zig index a5e53cb1ea..3180f678d3 100644 --- a/src/server/terrain/chunkgen/TerrainGenerator.zig +++ b/src/server/terrain/chunkgen/TerrainGenerator.zig @@ -133,7 +133,7 @@ pub fn generate(worldSeed: u64, chunk: *main.chunk.ServerChunk, caveMap: CaveMap break; } } - chunk.updateBlockInGeneration(x, y, z, block); + chunk.updateBlock(.replace, .noSetChanged, x, y, z, block); } z += chunk.super.pos.voxelSize; } diff --git a/src/server/terrain/simple_structures/Boulder.zig b/src/server/terrain/simple_structures/Boulder.zig index 0c745c800b..9ade31b101 100644 --- a/src/server/terrain/simple_structures/Boulder.zig +++ b/src/server/terrain/simple_structures/Boulder.zig @@ -64,7 +64,7 @@ pub fn generate(self: *Boulder, _: GenerationMode, x: i32, y: i32, z: i32, chunk } potential *= radius*radius/4/numberOfPoints; if(potential >= 1) { - chunk.updateBlockInGeneration(px, py, pz, self.block); + chunk.updateBlock(.replace, .noSetChanged, px, py, pz, self.block); } } } diff --git a/src/server/terrain/simple_structures/FallenTree.zig b/src/server/terrain/simple_structures/FallenTree.zig index 680539d98b..5bc95a44cb 100644 --- a/src/server/terrain/simple_structures/FallenTree.zig +++ b/src/server/terrain/simple_structures/FallenTree.zig @@ -37,7 +37,7 @@ pub fn loadModel(parameters: ZonElement) ?*FallenTree { pub fn generateStump(self: *FallenTree, x: i32, y: i32, z: i32, chunk: *main.chunk.ServerChunk) void { if(chunk.liesInChunk(x, y, z)) - chunk.updateBlockIfDegradable(x, y, z, .{.typ = self.woodBlock, .data = 0}); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y, z, .{.typ = self.woodBlock, .data = 0}); } pub fn generateFallen(self: *FallenTree, x: i32, y: i32, z: i32, length: u32, chunk: *main.chunk.ServerChunk, caveMap: CaveMapView, seed: *u64) void { @@ -96,7 +96,7 @@ pub fn generateFallen(self: *FallenTree, x: i32, y: i32, z: i32, length: u32, ch const v: i32 = @intCast(val); if(chunk.liesInChunk(x + dx*(v + 2), y + dy*(v + 2), z)) { const typ = if(v == (length - 1)) self.topWoodBlock else self.woodBlock; - chunk.updateBlockIfDegradable(x + dx*(v + 2), y + dy*(v + 2), z, .{.typ = typ, .data = @intCast(d.? + 2)}); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x + dx*(v + 2), y + dy*(v + 2), z, .{.typ = typ, .data = @intCast(d.? + 2)}); } } } diff --git a/src/server/terrain/simple_structures/FlowerPatch.zig b/src/server/terrain/simple_structures/FlowerPatch.zig index b51bba9ebd..f362fb44b6 100644 --- a/src/server/terrain/simple_structures/FlowerPatch.zig +++ b/src/server/terrain/simple_structures/FlowerPatch.zig @@ -86,7 +86,7 @@ pub fn generate(self: *FlowerPatch, mode: GenerationMode, x: i32, y: i32, z: i32 startHeight = chunk.startIndex(startHeight + chunk.super.pos.voxelSize); if(@abs(startHeight -% baseHeight) > 5) continue; if(chunk.liesInChunk(px, py, startHeight)) { - chunk.updateBlockInGeneration(px, py, startHeight, self.block); + chunk.updateBlock(.replace, .noSetChanged, px, py, startHeight, self.block); } } } diff --git a/src/server/terrain/simple_structures/GroundPatch.zig b/src/server/terrain/simple_structures/GroundPatch.zig index 73471d9fee..6523e4b019 100644 --- a/src/server/terrain/simple_structures/GroundPatch.zig +++ b/src/server/terrain/simple_structures/GroundPatch.zig @@ -92,7 +92,7 @@ pub fn generate(self: *GroundPatch, mode: GenerationMode, x: i32, y: i32, z: i32 while(pz <= startHeight) : (pz += chunk.super.pos.voxelSize) { if(dist <= self.smoothness or (dist - self.smoothness)/(1 - self.smoothness) < random.nextFloat(seed)) { if(chunk.liesInChunk(px, py, pz)) { - chunk.updateBlockInGeneration(px, py, pz, self.block); + chunk.updateBlock(.replace, .noSetChanged, px, py, pz, self.block); } } } diff --git a/src/server/terrain/simple_structures/SimpleTreeModel.zig b/src/server/terrain/simple_structures/SimpleTreeModel.zig index 30c7a85730..8f23c023d4 100644 --- a/src/server/terrain/simple_structures/SimpleTreeModel.zig +++ b/src/server/terrain/simple_structures/SimpleTreeModel.zig @@ -62,7 +62,7 @@ pub fn generateStem(self: *SimpleTreeModel, x: i32, y: i32, z: i32, height: i32, var pz: i32 = chunk.startIndex(z); while(pz < z + height) : (pz += chunk.super.pos.voxelSize) { if(chunk.liesInChunk(x, y, pz)) { - chunk.updateBlockIfDegradable(x, y, pz, if(pz == z + height - 1) self.topWoodBlock else self.woodBlock); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y, pz, if(pz == z + height - 1) self.topWoodBlock else self.woodBlock); if(self.branched) { const chance = @sqrt(@as(f32, @floatFromInt(pz - z))/@as(f32, @floatFromInt(height*2))); @@ -80,13 +80,13 @@ pub fn generateBranch(self: *SimpleTreeModel, x: i32, y: i32, z: i32, d: u32, ch _ = seed; if(d == 0 and chunk.liesInChunk(x + 1, y, z)) { - chunk.updateBlockIfDegradable(x + 1, y, z, .{.typ = self.topWoodBlock.typ, .data = 2}); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x + 1, y, z, .{.typ = self.topWoodBlock.typ, .data = 2}); } else if(d == 1 and chunk.liesInChunk(x - 1, y, z)) { - chunk.updateBlockIfDegradable(x - 1, y, z, .{.typ = self.topWoodBlock.typ, .data = 3}); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x - 1, y, z, .{.typ = self.topWoodBlock.typ, .data = 3}); } else if(d == 2 and chunk.liesInChunk(x, y + 1, z)) { - chunk.updateBlockIfDegradable(x, y + 1, z, .{.typ = self.topWoodBlock.typ, .data = 4}); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y + 1, z, .{.typ = self.topWoodBlock.typ, .data = 4}); } else if(d == 3 and chunk.liesInChunk(x, y - 1, z)) { - chunk.updateBlockIfDegradable(x, y - 1, z, .{.typ = self.topWoodBlock.typ, .data = 5}); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y - 1, z, .{.typ = self.topWoodBlock.typ, .data = 5}); } } @@ -104,10 +104,10 @@ pub fn generate(self: *SimpleTreeModel, _: GenerationMode, x: i32, y: i32, z: i3 if(chunk.super.pos.voxelSize >= 16) { // Ensures that even at lowest resolution some leaves are rendered for smaller trees. if(chunk.liesInChunk(x, y, z)) { - chunk.updateBlockIfDegradable(x, y, z, self.leavesBlock); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y, z, self.leavesBlock); } if(chunk.liesInChunk(x, y, z + chunk.super.pos.voxelSize)) { - chunk.updateBlockIfDegradable(x, y, z + chunk.super.pos.voxelSize, self.leavesBlock); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y, z + chunk.super.pos.voxelSize, self.leavesBlock); } } @@ -124,7 +124,7 @@ pub fn generate(self: *SimpleTreeModel, _: GenerationMode, x: i32, y: i32, z: i3 var py = chunk.startIndex(y + 1 - j); while(py < y + j) : (py += chunk.super.pos.voxelSize) { if(chunk.liesInChunk(px, py, pz)) - chunk.updateBlockIfDegradable(px, py, pz, self.leavesBlock); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, px, py, pz, self.leavesBlock); } } } @@ -146,7 +146,7 @@ pub fn generate(self: *SimpleTreeModel, _: GenerationMode, x: i32, y: i32, z: i3 while(py < y + ceilRadius) : (py += chunk.super.pos.voxelSize) { const distSqr = @as(f32, @floatFromInt((pz - center)*(pz - center)))*invLeafElongationSqr + @as(f32, @floatFromInt((px - x)*(px - x) + (py - y)*(py - y))); if(chunk.liesInChunk(px, py, pz) and distSqr < radiusSqr and (distSqr < randomRadiusSqr or random.nextInt(u1, seed) != 0)) { // TODO: Use another seed to make this more reliable! - chunk.updateBlockIfDegradable(px, py, pz, self.leavesBlock); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, px, py, pz, self.leavesBlock); } } } diff --git a/src/server/terrain/simple_structures/SimpleVegetation.zig b/src/server/terrain/simple_structures/SimpleVegetation.zig index ea5d96156e..68130a309a 100644 --- a/src/server/terrain/simple_structures/SimpleVegetation.zig +++ b/src/server/terrain/simple_structures/SimpleVegetation.zig @@ -41,13 +41,13 @@ pub fn generate(self: *SimpleVegetation, _: GenerationMode, x: i32, y: i32, z: i if(isCeiling) { while(pz >= z - height) : (pz -= chunk.super.pos.voxelSize) { if(chunk.liesInChunk(x, y, pz)) { - chunk.updateBlockIfDegradable(x, y, pz, self.block); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y, pz, self.block); } } } else { while(pz < z + height) : (pz += chunk.super.pos.voxelSize) { if(chunk.liesInChunk(x, y, pz)) { - chunk.updateBlockIfDegradable(x, y, pz, self.block); + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x, y, pz, self.block); } } } diff --git a/src/server/terrain/simple_structures/Stalagmite.zig b/src/server/terrain/simple_structures/Stalagmite.zig index a2fa5d6914..f815065aa9 100644 --- a/src/server/terrain/simple_structures/Stalagmite.zig +++ b/src/server/terrain/simple_structures/Stalagmite.zig @@ -64,10 +64,7 @@ pub fn generate(self: *Stalagmite, _: GenerationMode, x: i32, y: i32, z: i32, ch const dist = vec.lengthSquare(Vec3f{@as(f32, @floatFromInt(x3)) - relX, @as(f32, @floatFromInt(y3)) - relY, @as(f32, @floatFromInt(z3)) - z2}); if(dist < size*size) { if(x3 >= 0 and x3 < chunk.super.width and y3 >= 0 and y3 < chunk.super.width and z3 >= 0 and z3 < chunk.super.width) { - const block: main.blocks.Block = chunk.getBlock(x3, y3, z3); - if(block.typ == 0 or block.degradable()) { - chunk.updateBlockInGeneration(x3, y3, z3, self.block); - } + chunk.updateBlock(.replaceIfDegradable, .noSetChanged, x3, y3, z3, self.block); } } } diff --git a/src/server/world.zig b/src/server/world.zig index 18e3190404..7ac97b2f81 100644 --- a/src/server/world.zig +++ b/src/server/world.zig @@ -1152,7 +1152,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld var neighborBlock = ch.getBlock(neighborPos.x, neighborPos.y, neighborPos.z); if(neighborBlock.mode().dependsOnNeighbors and neighborBlock.mode().updateData(&neighborBlock, neighbor.reverse(), newBlock)) { - ch.updateBlockAndSetChanged(neighborPos.x, neighborPos.y, neighborPos.z, neighborBlock); + ch.updateBlock(.replace, .setChanged, neighborPos.x, neighborPos.y, neighborPos.z, neighborBlock); } if(newBlock.mode().dependsOnNeighbors) { _ = newBlock.mode().updateData(&newBlock, neighbor, neighborBlock); @@ -1166,7 +1166,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld std.log.err("Got error {s} while trying to remove entity data in position {} for block {s}", .{@errorName(err), Vec3i{wx, wy, wz}, currentBlock.id()}); }; } - baseChunk.updateBlockAndSetChanged(pos.x, pos.y, pos.z, newBlock); + baseChunk.updateBlock(.replace, .setChanged, pos.x, pos.y, pos.z, newBlock); const userList = server.getUserListAndIncreaseRefCount(main.stackAllocator); defer server.freeUserListAndDecreaseRefCount(main.stackAllocator, userList);