Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f86bb94
saplings
tillpp Nov 25, 2025
4f5bebb
tmp: cactus_flower as sapling
tillpp Nov 25, 2025
4e0de58
Merge branch 'PixelGuys:master' into saplings
tillpp Nov 26, 2025
9ad5b0a
Merge branch 'PixelGuys:master' into saplings
tillpp Nov 27, 2025
14d97e8
saplings
tillpp Nov 25, 2025
e216b62
tmp: cactus_flower as sapling
tillpp Nov 25, 2025
bdb0cb2
Merge branch 'saplings' of https://github.com/tillpp/Cubzio into sapl…
tillpp Feb 22, 2026
10c3836
fixed memory leak and added oak sapling
tillpp Feb 22, 2026
08ecb24
format
tillpp Feb 22, 2026
5b68a8d
fmt
tillpp Feb 22, 2026
c74e085
format
tillpp Feb 22, 2026
8896218
Only allow SBBs
tillpp Feb 25, 2026
cffc575
removed unused parameter
tillpp Feb 25, 2026
f33d3a8
Don't trigger blockUpdates when using saplings, to safe performance
tillpp Feb 25, 2026
dbb2147
removed recursive import
tillpp Feb 26, 2026
ceb16b0
Merge branch 'PixelGuys:master' into saplings
tillpp Feb 27, 2026
cc0b6de
Merge branch 'PixelGuys:master' into saplings
tillpp Mar 1, 2026
bcf99eb
use comptime
tillpp Mar 2, 2026
a1d3e42
Merge branch 'saplings' of https://github.com/tillpp/Cubzio into sapl…
tillpp Mar 2, 2026
fbd777c
Merge branch 'PixelGuys:master' into saplings
tillpp Mar 23, 2026
6293562
Merge remote-tracking branch 'origin/master' into saplings
tillpp May 4, 2026
1b61a67
fixed merge
tillpp May 4, 2026
a2ddebc
Merge branch 'master' into saplings
tillpp May 4, 2026
702b6f1
Merge remote-tracking branch 'upstream/master' into saplings
tillpp Jun 16, 2026
70bda9c
fix merge
tillpp Jun 16, 2026
bac9af6
changed import style
tillpp Jun 16, 2026
75a9972
removed noUpdate parameter, use "false" instead
tillpp Jun 16, 2026
190cd6a
remove the zon
tillpp Jun 16, 2026
b82be53
make noUpdate doable
tillpp Jun 16, 2026
f8c6724
Merge remote-tracking branch 'upstream/master' into saplings
tillpp Jun 18, 2026
a3dd604
fmt
tillpp Jun 18, 2026
0f426e4
dont replace undegradable
tillpp Jun 23, 2026
e944aa3
fmt
tillpp Jun 23, 2026
4ba14be
Merge remote-tracking branch 'upstream/master' into saplings
tillpp Jun 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/cubyz/blocks/leaves/_defaults.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
.prevention = .{.log, .branch},
.drops = .{
.{.chance = 0.01, .items = .{"cubyz:apple"}},
.{.chance = 0.001, .items = .{"cubyz:sapling/oak"}},
},
},
.drops = .{
.{.items = .{.auto}, .allowedToolTags = .{.cuttable}},
.{.chance = 0.01, .items = .{"cubyz:apple"}, .forbiddenToolTags = .{.cuttable}},
.{.chance = 0.001, .items = .{"cubyz:sapling/oak"}, .forbiddenToolTags = .{.cuttable}},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many leaf blocks are there in a tree?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably less then 1000

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have those numbers, I think for all saplings the number of saplings per tree should be consistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feel free to count them

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also the amount of leaves varies because of the generation

},
.rotation = "cubyz:decayable",
.tags = .{.cuttable, .leaf},
Expand Down
33 changes: 33 additions & 0 deletions assets/cubyz/blocks/sapling/cactus.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.{
.tags = .{.cuttable},
.blockHealth = 0.2,
.drops = .{
.{.items = .{.auto}},
},
.onTick = .{.type = .sapling, .sbb = .{
.structure = "cubyz:cactus/saguaro",
.placeMode = .degradable,
.chance = 0.01,
}},
.degradable = true,
.collide = false,
.alwaysViewThrough = true,
.absorbedLight = 0x121012,
.model = "cubyz:cactus_flower",
.rotation = "cubyz:direction",
.texture = "cubyz:cactus_flower",
.texture_top = "cubyz:cactus_flower_top",
.texture_bottom = "cubyz:cactus_flower_top",
.item = .{
.texture = "cactus_flower.png",
},
.lodReplacement = "cubyz:air",
.onTouch = .{
.type = .hurt,
.dps = 0.2,
.damageType = .spiky,
},
.onUpdate = .{
.type = .checkSupportBlocks,
},
}
36 changes: 36 additions & 0 deletions assets/cubyz/blocks/sapling/oak.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.{
.tags = .{.cuttable},
.blockHealth = 0.2,
.drops = .{
.{.items = .{.auto}},
},
.onTick = .{
.type = .sapling,
.sbb = .{
.structure = "cubyz:tree/oak/white",
.placeMode = .degradable,
.chance = 0.03,
},
},
.degradable = true,
.collide = false,
.alwaysViewThrough = true,
.absorbedLight = 0x121012,
.model = "cubyz:cactus_flower",
.rotation = "cubyz:direction",
.texture = "cubyz:cactus_flower",
.texture_top = "cubyz:cactus_flower_top",
.texture_bottom = "cubyz:cactus_flower_top",
.item = .{
.texture = "cactus_flower.png",
},
.lodReplacement = "cubyz:air",
.onTouch = .{
.type = .hurt,
.dps = 0.2,
.damageType = .spiky,
},
.onUpdate = .{
.type = .checkSupportBlocks,
},
}
2 changes: 2 additions & 0 deletions src/assets.zig
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,8 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
}
}

blocks.finishBlockCallbacks(worldAssets.blocks);

worldAssets.log(.world);
}

Expand Down
7 changes: 6 additions & 1 deletion src/blocks.zig
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,16 @@ pub fn finishBlocks(zonElements: Assets.ZonHashMap) void {
registerBlockDrop(i, zonElements.get(_id[i]) orelse continue);
registerLodReplacement(i, zonElements.get(_id[i]) orelse continue);
registerOpaqueVariant(i, zonElements.get(_id[i]) orelse continue);
registerCallbacks(i, zonElements.get(_id[i]) orelse continue);
}
blueprint.registerVoidBlock(parseBlock("cubyz:void"));
meshes.finishTextureLoading();
}
pub fn finishBlockCallbacks(zonElements: Assets.ZonHashMap) void {
var i: u16 = 0;
while (i < size) : (i += 1) {
registerCallbacks(i, zonElements.get(_id[i]) orelse continue);
}
}

pub fn reset() void {
size = 0;
Expand Down
12 changes: 10 additions & 2 deletions src/blueprint.zig
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ pub const Blueprint = struct {

pub const PasteFlags = struct {
preserveVoid: bool = false,
replaceUndegradable: bool = true,
};

pub fn paste(self: Blueprint, pos: Vec3i, flags: PasteFlags) void {
Expand All @@ -183,8 +184,15 @@ pub const Blueprint = struct {
const worldZ = startZ +% @as(i32, @intCast(z));

const block = self.blocks.get(x, y, z);
if (block.typ != voidType or flags.preserveVoid) {
_ = main.server.world.?.updateBlock(worldX, worldY, worldZ, block);
if (block.typ == voidType and !flags.preserveVoid) continue;

while (true) {
const oldBlock = main.server.world.?.getBlock(@intCast(worldX), @intCast(worldY), @intCast(worldZ));
if (oldBlock) |_oldBlock| {
if (!_oldBlock.degradable() and !flags.replaceUndegradable) break;
}
if (main.server.world.?.cmpxchgBlock(worldX, worldY, worldZ, oldBlock, block, true) == null)
break;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/callbacks/block/server/_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub const check_support_blocks = @import("check_support_blocks.zig");
pub const decay = @import("decay.zig");
pub const vine_decay = @import("vine_decay.zig");
pub const replace_block = @import("replace_block.zig");
pub const sapling = @import("sapling.zig");
2 changes: 1 addition & 1 deletion src/callbacks/block/server/check_support_blocks.zig
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn run(_: *@This(), params: main.callbacks.ServerBlockCallback.Params) main.

if (newBlock == params.block) return .ignored;

if (main.server.world.?.cmpxchgBlock(wx, wy, wz, params.block, newBlock) == null) {
if (main.server.world.?.cmpxchgBlock(wx, wy, wz, params.block, newBlock, false) == null) {
const dropAmount = params.block.mode().itemDropsOnChange(params.block, newBlock);
const drops = params.block.blockDrops();
for (0..dropAmount) |_| {
Expand Down
2 changes: 1 addition & 1 deletion src/callbacks/block/server/decay.zig
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ pub fn run(self: *@This(), params: main.callbacks.ServerBlockCallback.Params) ma
if (self.foundWayToLog(world, leaf, wx, wy, wz)) return .ignored;

// no, there is no log in proximity
if (world.cmpxchgBlock(wx, wy, wz, leaf, self.decayReplacement) == null) {
if (world.cmpxchgBlock(wx, wy, wz, leaf, self.decayReplacement, false) == null) {
for (self.blockDrops) |drop| {
if (drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) {
for (drop.items) |stack| {
Expand Down
2 changes: 1 addition & 1 deletion src/callbacks/block/server/replace_block.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ pub fn run(self: *@This(), params: main.callbacks.ServerBlockCallback.Params) ma
const wy = params.chunk.super.pos.wy + params.blockPos.y;
const wz = params.chunk.super.pos.wz + params.blockPos.z;

_ = main.server.world.?.cmpxchgBlock(wx, wy, wz, params.block, self.block);
_ = main.server.world.?.cmpxchgBlock(wx, wy, wz, params.block, self.block, false);
return .handled;
}
32 changes: 32 additions & 0 deletions src/callbacks/block/server/sapling.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const std = @import("std");

const main = @import("main");
const Vec3i = main.vec.Vec3i;
const sbbGen = main.server.terrain.structures.simple_structures.SbbGen;
const SimpleStructureModel = main.server.terrain.biomes.SimpleStructureModel;

sbb: *sbbGen.SbbGen,
chance: f32,

pub fn init(zon: main.ZonElement, _: main.callbacks.Creator) ?*@This() {
const result = main.worldArena.create(@This());

const vtableModel = sbbGen.loadModel(zon.getChild("sbb")) orelse {
std.log.err("Error occurred while loading structure for saplings", .{});
return null;
};
result.sbb = vtableModel;
result.chance = zon.get(f32, "chance", 0.1);
return result;
}

pub fn run(self: *@This(), params: main.callbacks.ServerBlockCallback.Params) main.callbacks.Result {

// copied from SimpleStructureGen.generate.
const randomValue = main.random.nextFloat(&main.seed);
if (randomValue < self.chance) {
self.sbb.placeSbb(self.sbb.structureRef, Vec3i{params.blockPos.x, params.blockPos.y, params.blockPos.z}, null, self.sbb.rotation.getInitialRotation(&main.seed), params.chunk, &main.seed, false);
}

return .handled;
}
2 changes: 1 addition & 1 deletion src/callbacks/block/server/vine_decay.zig
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ pub fn run(_: *@This(), params: main.callbacks.ServerBlockCallback.Params) main.
}

fn decay(x: i32, y: i32, z: i32, current: Block) main.callbacks.Result {
if (server.world.?.cmpxchgBlock(x, y, z, current, blocks.Block.air) == null) return .handled;
if (server.world.?.cmpxchgBlock(x, y, z, current, blocks.Block.air, false) == null) return .handled;
return .ignored;
}
18 changes: 13 additions & 5 deletions src/server/terrain/simple_structures/SbbGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const NeverFailingAllocator = main.heap.NeverFailingAllocator;
pub const id = "cubyz:sbb";
pub const generationMode = .floor;

const SbbGen = @This();
pub const SbbGen = @This();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes no sense to make this public


structureRef: *const sbb.StructureBuildingBlock,
placeMode: Blueprint.PasteMode,
Expand Down Expand Up @@ -53,10 +53,10 @@ pub fn loadModel(parameters: ZonElement) ?*SbbGen {
}

pub fn generate(self: *SbbGen, _: GenerationMode, x: i32, y: i32, z: i32, chunk: *ServerChunk, _: CaveMapView, _: CaveBiomeMapView, seed: *u64, _: bool) void {
placeSbb(self, self.structureRef, Vec3i{x, y, z}, null, self.rotation.getInitialRotation(seed), chunk, seed);
placeSbb(self, self.structureRef, Vec3i{x, y, z}, null, self.rotation.getInitialRotation(seed), chunk, seed, true);
}

fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placementPosition: Vec3i, placementDirection: ?Neighbor, rotation: sbb.Rotation, chunk: *ServerChunk, seed: *u64) void {
pub fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placementPosition: Vec3i, placementDirection: ?Neighbor, rotation: sbb.Rotation, chunk: *ServerChunk, seed: *u64, comptime duringWorldGeneration: bool) void {
const blueprints = &(structure.getBlueprints(seed).* orelse return);

const origin = blueprints[0].originBlock;
Expand All @@ -68,12 +68,20 @@ fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placeme
const rotatedOrigin = rotated.originBlock.pos();
const pastePosition = placementPosition - rotatedOrigin - (placementDirection orelse origin.direction()).relPos();

rotated.blueprint.pasteInGeneration(pastePosition, chunk, self.placeMode);
if (duringWorldGeneration) {
rotated.blueprint.pasteInGeneration(pastePosition, chunk, self.placeMode);
} else {
var pos = Vec3i{0, 0, 0};
pos[0] = chunk.super.pos.wx + pastePosition[0];
pos[1] = chunk.super.pos.wy + pastePosition[1];
pos[2] = chunk.super.pos.wz + pastePosition[2];

rotated.blueprint.paste(pos, .{.replaceUndegradable = false});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be configurable via self.placeMode, like when placed in generation.

}
for (rotated.childBlocks) |childBlock| {
const child = structure.getChildStructure(childBlock) orelse continue;
const childRotation = blueprintRotation.getChildRotation(seed, child.rotation, childBlock.direction());
placeSbb(self, child, pastePosition + childBlock.pos(), childBlock.direction(), childRotation, chunk, seed);
placeSbb(self, child, pastePosition + childBlock.pos(), childBlock.direction(), childRotation, chunk, seed, duringWorldGeneration);
}
}

Expand Down
11 changes: 7 additions & 4 deletions src/server/world.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
}

/// Returns the actual block on failure
pub fn cmpxchgBlock(self: *ServerWorld, wx: i32, wy: i32, wz: i32, oldBlock: ?Block, _newBlock: Block) ?Block {
pub fn cmpxchgBlock(self: *ServerWorld, wx: i32, wy: i32, wz: i32, oldBlock: ?Block, _newBlock: Block, comptime noUpdate: bool) ?Block {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use an enum to improve reasability at the callsite

main.sync.threadContext.assertCorrectContext(.server);
const baseChunk = ChunkManager.getOrGenerateChunkAndIncreaseRefCount(.{.wx = wx & ~@as(i32, chunk.chunkMask), .wy = wy & ~@as(i32, chunk.chunkMask), .wz = wz & ~@as(i32, chunk.chunkMask), .voxelSize = 1});
defer baseChunk.decreaseRefCount();
Expand Down Expand Up @@ -1291,6 +1291,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
for (userList) |user| {
main.network.protocols.blockUpdate.send(user.conn, &.{.{.pos = .{wx, wy, wz}, .newBlock = newBlock, .blockEntityData = &.{}}});
}

// onBreak event
Comment thread
tillpp marked this conversation as resolved.
if (oldBlock) |block| {
if (block.typ != newBlock.typ) {
Expand All @@ -1301,7 +1302,9 @@ pub const ServerWorld = struct { // MARK: ServerWorld
});
}
}
self.triggerNeighborBlockUpdates(wx, wy, wz);
if (!noUpdate) {
self.triggerNeighborBlockUpdates(wx, wy, wz);
}

return null;
}
Expand All @@ -1324,8 +1327,8 @@ pub const ServerWorld = struct { // MARK: ServerWorld
}
}

pub fn updateBlock(self: *ServerWorld, wx: i32, wy: i32, wz: i32, newBlock: Block) void {
_ = self.cmpxchgBlock(wx, wy, wz, null, newBlock);
pub fn updateBlock(self: *ServerWorld, wx: i32, wy: i32, wz: i32, newBlock: Block, comptime noUpdate: bool) void {
_ = self.cmpxchgBlock(wx, wy, wz, null, newBlock, noUpdate);
}

pub fn queueChunkUpdateAndDecreaseRefCount(self: *ServerWorld, ch: *ServerChunk) void {
Expand Down
2 changes: 1 addition & 1 deletion src/sync.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1612,7 +1612,7 @@ pub const Command = struct { // MARK: Command
}

if (ctx.side == .server) {
if (main.server.world.?.cmpxchgBlock(self.pos[0], self.pos[1], self.pos[2], self.oldBlock, self.newBlock) != null) {
if (main.server.world.?.cmpxchgBlock(self.pos[0], self.pos[1], self.pos[2], self.oldBlock, self.newBlock, false) != null) {
// Inform the client of the actual block:
var writer = BinaryWriter.init(main.stackAllocator);
defer writer.deinit();
Expand Down
Loading