From 4e9a1b20393455f2e5a8770e5a0456bc1d596a2a Mon Sep 17 00:00:00 2001 From: Wunka <56411012+Wunka@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:07:24 +0200 Subject: [PATCH 1/6] add swing_arm component --- src/entityComponent/_list.zig | 1 + src/entityComponent/swing_arm.zig | 99 +++++++++++++++++++++++++++++++ src/renderer.zig | 16 ++--- src/server/server.zig | 1 + 4 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 src/entityComponent/swing_arm.zig diff --git a/src/entityComponent/_list.zig b/src/entityComponent/_list.zig index d162bf66e0..289cebbb74 100644 --- a/src/entityComponent/_list.zig +++ b/src/entityComponent/_list.zig @@ -3,3 +3,4 @@ pub const @"cubyz:bag" = @import("bag.zig"); pub const @"cubyz:model" = @import("model.zig"); pub const @"cubyz:permissions" = @import("permissions.zig"); pub const @"cubyz:player" = @import("player.zig"); +pub const @"cubyz:swing_arm" = @import("swing_arm.zig"); diff --git a/src/entityComponent/swing_arm.zig b/src/entityComponent/swing_arm.zig new file mode 100644 index 0000000000..31c9b7ca0b --- /dev/null +++ b/src/entityComponent/swing_arm.zig @@ -0,0 +1,99 @@ +const std = @import("std"); + +const main = @import("main"); +const chunk = main.chunk; +const Entity = main.entity.Entity; +const ServerChunk = chunk.ServerChunk; +const game = main.game; +const graphics = main.graphics; +const ZonElement = main.ZonElement; +const renderer = main.renderer; +const settings = main.settings; +const utils = main.utils; +const BinaryReader = utils.BinaryReader; +const BinaryWriter = utils.BinaryWriter; +const vec = main.vec; +const Mat4f = vec.Mat4f; +const Vec3d = vec.Vec3d; +const Vec3f = vec.Vec3f; +const Vec4f = vec.Vec4f; +const Vec3i = vec.Vec3i; +const NeverFailingAllocator = main.heap.NeverFailingAllocator; +const blocks = main.blocks; +const World = game.World; +const ServerWorld = main.server.ServerWorld; +const items = main.items; +const ItemStack = items.ItemStack; +const random = main.random; + +const c = @import("c"); + +pub var entityComponentID: main.entity.EntityComponentId = undefined; +pub const entityComponentVersion = 0; + +// ############################# Client only stuff ################################ +pub const client = struct { + const Component = struct { + currentSwingProgress: f32, + }; + pub var components: main.utils.SparseSet(Component, Entity) = .{}; + + pub fn init() void {} + pub fn deinit() void { + components.deinit(main.globalAllocator); + } + pub fn clear() void { + components.clear(); + } + pub fn load(entity: Entity, reader: *utils.BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { + if (version != 0) return error.InvalidComponentVersion; + const currentSwingProgress = reader.readFloat(f32) catch return error.UnreadableComponentData; + const ptr = components.get(entity) orelse components.add(main.globalAllocator, entity); + ptr.* = Component{ + .currentSwingProgress = currentSwingProgress, + }; + } + pub fn unload(entity: Entity) void { + components.remove(entity) catch {}; + } + pub fn get(entity: Entity) ?*Component { + return components.get(entity); + } +}; +// ############################# Server only stuff ################################ +pub const server = struct { + pub const Component = struct { + currentSwingProgress: f32, + pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { + writer.writeFloat(f32, self.currentSwingProgress); + if (audience == .disk) return .discard; + return .save; + } + }; + var components: main.utils.SparseSet(Component, Entity) = undefined; + pub fn init() void { + components = .{}; + } + pub fn deinit() void { + components.deinit(main.globalAllocator); + } + pub fn loadFromData(entity: Entity, _: *utils.BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { + if (version != 0) return error.InvalidComponentVersion; + load(entity); + } + pub fn load(entity: Entity) void { + put(entity, Component{ + .currentSwingProgress = 0, + }); + } + pub fn unload(entity: Entity) void { + components.remove(entity) catch {}; + } + pub fn put(entity: Entity, renderComponent: Component) void { + const ptr = components.get(entity) orelse components.add(main.globalAllocator, entity); + ptr.* = renderComponent; + } + pub fn get(entity: Entity) ?*Component { + return components.get(entity); + } +}; diff --git a/src/renderer.zig b/src/renderer.zig index b250021882..66b6d63b36 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -924,7 +924,6 @@ pub const MeshSelection = struct { // MARK: MeshSelection pub var selectedBlockPos: ?Vec3i = null; var lastSelectedBlockPos: Vec3i = undefined; var currentBlockProgress: f32 = 0; - var currentSwingProgress: f32 = 0; var currentSwingTime: f32 = 0; var selectionMin: Vec3f = undefined; var selectionMax: Vec3f = undefined; @@ -1067,6 +1066,8 @@ pub const MeshSelection = struct { // MARK: MeshSelection } pub fn breakBlock(inventory: main.items.Inventory.ClientInventory, slot: u32, deltaTime: f64) void { + const swing_arm = main.entity.components.@"cubyz:swing_arm".client.get(main.game.Player.id) orelse return; // player can't swing... + if (selectedBlockPos) |selectedPos| { const stack = inventory.getStack(slot); const isSelectionWand = stack.item == .baseItem and std.mem.eql(u8, stack.item.baseItem.id(), "cubyz:selection_wand"); @@ -1078,8 +1079,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection if (@reduce(.Or, lastSelectedBlockPos != selectedPos)) { mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); - currentSwingProgress = 0; - currentSwingTime = 0; + swing_arm.currentSwingProgress = 0; lastSelectedBlockPos = selectedPos; currentBlockProgress = 0; } @@ -1100,7 +1100,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection if (damage > 0) { const swingTime = if (isProceduralItem and stack.item.proceduralItem.isEffectiveOn(block)) 1.0/stack.item.proceduralItem.getProperty(.swingSpeed) else 0.5; if (currentSwingTime > swingTime) { - currentSwingProgress = 0; + swing_arm.currentSwingProgress = 0; currentSwingTime = 0; } if (currentSwingTime == 0) { @@ -1108,9 +1108,9 @@ pub const MeshSelection = struct { // MARK: MeshSelection const damagePerSwing = block.blockHealth()/swings; currentSwingTime = damagePerSwing/damage*swingTime; } - currentSwingProgress += @floatCast(deltaTime); - while (currentSwingProgress > currentSwingTime) { - currentSwingProgress -= currentSwingTime; + swing_arm.currentSwingProgress += @floatCast(deltaTime); + while (swing_arm.currentSwingProgress > currentSwingTime) { + swing_arm.currentSwingProgress -= currentSwingTime; currentBlockProgress += damage*currentSwingTime/swingTime/block.blockHealth(); if (currentBlockProgress > 0.9999) break; const swings = @ceil(block.blockHealth()/damage); @@ -1126,7 +1126,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection return; } else { - currentSwingProgress = 0; + swing_arm.currentSwingProgress = 0; mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); currentBlockProgress = 0; currentSwingTime = 0; diff --git a/src/server/server.zig b/src/server/server.zig index 03830290d8..6684813aa9 100644 --- a/src/server/server.zig +++ b/src/server/server.zig @@ -324,6 +324,7 @@ pub const User = struct { // MARK: User self.loadUnloadChunks(); main.entity.components.@"cubyz:player".server.load(self.id, @truncate(self.playerIndex)); + main.entity.components.@"cubyz:swing_arm".server.load(self.id); } fn simArrIndex(x: i32) usize { From fef53322ebc9b2e2296f2a37599ac16094c63def Mon Sep 17 00:00:00 2001 From: Wunka <56411012+Wunka@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:13:38 +0200 Subject: [PATCH 2/6] move currentSwingTime over --- src/entityComponent/swing_arm.zig | 6 ++++++ src/renderer.zig | 29 ++++++++++++++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/entityComponent/swing_arm.zig b/src/entityComponent/swing_arm.zig index 31c9b7ca0b..e1975663be 100644 --- a/src/entityComponent/swing_arm.zig +++ b/src/entityComponent/swing_arm.zig @@ -35,6 +35,7 @@ pub const entityComponentVersion = 0; pub const client = struct { const Component = struct { currentSwingProgress: f32, + currentSwingTime: f32, }; pub var components: main.utils.SparseSet(Component, Entity) = .{}; @@ -48,9 +49,11 @@ pub const client = struct { pub fn load(entity: Entity, reader: *utils.BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { if (version != 0) return error.InvalidComponentVersion; const currentSwingProgress = reader.readFloat(f32) catch return error.UnreadableComponentData; + const currentSwingTime = reader.readFloat(f32) catch return error.UnreadableComponentData; const ptr = components.get(entity) orelse components.add(main.globalAllocator, entity); ptr.* = Component{ .currentSwingProgress = currentSwingProgress, + .currentSwingTime = currentSwingTime, }; } pub fn unload(entity: Entity) void { @@ -64,8 +67,10 @@ pub const client = struct { pub const server = struct { pub const Component = struct { currentSwingProgress: f32, + currentSwingTime: f32, pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { writer.writeFloat(f32, self.currentSwingProgress); + writer.writeFloat(f32, self.currentSwingTime); if (audience == .disk) return .discard; return .save; } @@ -84,6 +89,7 @@ pub const server = struct { pub fn load(entity: Entity) void { put(entity, Component{ .currentSwingProgress = 0, + .currentSwingTime = 0, }); } pub fn unload(entity: Entity) void { diff --git a/src/renderer.zig b/src/renderer.zig index 66b6d63b36..770bf4f521 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -924,7 +924,6 @@ pub const MeshSelection = struct { // MARK: MeshSelection pub var selectedBlockPos: ?Vec3i = null; var lastSelectedBlockPos: Vec3i = undefined; var currentBlockProgress: f32 = 0; - var currentSwingTime: f32 = 0; var selectionMin: Vec3f = undefined; var selectionMax: Vec3f = undefined; var selectionNormal: Vec3f = undefined; @@ -1066,7 +1065,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection } pub fn breakBlock(inventory: main.items.Inventory.ClientInventory, slot: u32, deltaTime: f64) void { - const swing_arm = main.entity.components.@"cubyz:swing_arm".client.get(main.game.Player.id) orelse return; // player can't swing... + const swingArm = main.entity.components.@"cubyz:swing_arm".client.get(main.game.Player.id) orelse return; // player can't swing... if (selectedBlockPos) |selectedPos| { const stack = inventory.getStack(slot); @@ -1079,7 +1078,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection if (@reduce(.Or, lastSelectedBlockPos != selectedPos)) { mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); - swing_arm.currentSwingProgress = 0; + swingArm.currentSwingProgress = 0; lastSelectedBlockPos = selectedPos; currentBlockProgress = 0; } @@ -1099,23 +1098,23 @@ pub const MeshSelection = struct { // MARK: MeshSelection damage -= block.blockResistance(); if (damage > 0) { const swingTime = if (isProceduralItem and stack.item.proceduralItem.isEffectiveOn(block)) 1.0/stack.item.proceduralItem.getProperty(.swingSpeed) else 0.5; - if (currentSwingTime > swingTime) { - swing_arm.currentSwingProgress = 0; - currentSwingTime = 0; + if (swingArm.currentSwingTime > swingTime) { + swingArm.currentSwingProgress = 0; + swingArm.currentSwingTime = 0; } - if (currentSwingTime == 0) { + if (swingArm.currentSwingTime == 0) { const swings = @ceil(block.blockHealth()/damage); const damagePerSwing = block.blockHealth()/swings; - currentSwingTime = damagePerSwing/damage*swingTime; + swingArm.currentSwingTime = damagePerSwing/damage*swingTime; } - swing_arm.currentSwingProgress += @floatCast(deltaTime); - while (swing_arm.currentSwingProgress > currentSwingTime) { - swing_arm.currentSwingProgress -= currentSwingTime; - currentBlockProgress += damage*currentSwingTime/swingTime/block.blockHealth(); + swingArm.currentSwingProgress += @floatCast(deltaTime); + while (swingArm.currentSwingProgress > swingArm.currentSwingTime) { + swingArm.currentSwingProgress -= swingArm.currentSwingTime; + currentBlockProgress += damage*swingArm.currentSwingTime/swingTime/block.blockHealth(); if (currentBlockProgress > 0.9999) break; const swings = @ceil(block.blockHealth()/damage); const damagePerSwing = block.blockHealth()/swings; - currentSwingTime = damagePerSwing/damage*swingTime; + swingArm.currentSwingTime = damagePerSwing/damage*swingTime; } if (currentBlockProgress < 0.9999) { mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); @@ -1126,10 +1125,10 @@ pub const MeshSelection = struct { // MARK: MeshSelection return; } else { - swing_arm.currentSwingProgress = 0; + swingArm.currentSwingProgress = 0; mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); currentBlockProgress = 0; - currentSwingTime = 0; + swingArm.currentSwingTime = 0; } } else { main.sync.client.mutex.unlock(); From 617627e91b7499e8a6ee1eb9173ce422599e5a8d Mon Sep 17 00:00:00 2001 From: Wunka <56411012+Wunka@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:35:04 +0200 Subject: [PATCH 3/6] add broken component --- src/entityComponent/_list.zig | 1 + src/entityComponent/broken.zig | 99 ++++++++++++++++++++++++++++++++++ src/renderer.zig | 16 +++--- src/server/server.zig | 1 + 4 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 src/entityComponent/broken.zig diff --git a/src/entityComponent/_list.zig b/src/entityComponent/_list.zig index 289cebbb74..f223f9c70e 100644 --- a/src/entityComponent/_list.zig +++ b/src/entityComponent/_list.zig @@ -1,5 +1,6 @@ pub const @"cubyz:_template" = @import("_template.zig"); pub const @"cubyz:bag" = @import("bag.zig"); +pub const @"cubyz:broken" = @import("broken.zig"); pub const @"cubyz:model" = @import("model.zig"); pub const @"cubyz:permissions" = @import("permissions.zig"); pub const @"cubyz:player" = @import("player.zig"); diff --git a/src/entityComponent/broken.zig b/src/entityComponent/broken.zig new file mode 100644 index 0000000000..6bc1b8bf00 --- /dev/null +++ b/src/entityComponent/broken.zig @@ -0,0 +1,99 @@ +const std = @import("std"); + +const main = @import("main"); +const chunk = main.chunk; +const Entity = main.entity.Entity; +const ServerChunk = chunk.ServerChunk; +const game = main.game; +const graphics = main.graphics; +const ZonElement = main.ZonElement; +const renderer = main.renderer; +const settings = main.settings; +const utils = main.utils; +const BinaryReader = utils.BinaryReader; +const BinaryWriter = utils.BinaryWriter; +const vec = main.vec; +const Mat4f = vec.Mat4f; +const Vec3d = vec.Vec3d; +const Vec3f = vec.Vec3f; +const Vec4f = vec.Vec4f; +const Vec3i = vec.Vec3i; +const NeverFailingAllocator = main.heap.NeverFailingAllocator; +const blocks = main.blocks; +const World = game.World; +const ServerWorld = main.server.ServerWorld; +const items = main.items; +const ItemStack = items.ItemStack; +const random = main.random; + +const c = @import("c"); + +pub var entityComponentID: main.entity.EntityComponentId = undefined; +pub const entityComponentVersion = 0; + +// ############################# Client only stuff ################################ +pub const client = struct { + const Component = struct { + progress: f32, + }; + pub var components: main.utils.SparseSet(Component, Entity) = .{}; + + pub fn init() void {} + pub fn deinit() void { + components.deinit(main.globalAllocator); + } + pub fn clear() void { + components.clear(); + } + pub fn load(entity: Entity, reader: *utils.BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { + if (version != 0) return error.InvalidComponentVersion; + const progress = reader.readFloat(f32) catch return error.UnreadableComponentData; + const ptr = components.get(entity) orelse components.add(main.globalAllocator, entity); + ptr.* = Component{ + .progress = progress, + }; + } + pub fn unload(entity: Entity) void { + components.remove(entity) catch {}; + } + pub fn get(entity: Entity) ?*Component { + return components.get(entity); + } +}; +// ############################# Server only stuff ################################ +pub const server = struct { + pub const Component = struct { + progress: f32, + pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { + writer.writeFloat(f32, self.progress); + if (audience == .disk) return .discard; + return .save; + } + }; + var components: main.utils.SparseSet(Component, Entity) = undefined; + pub fn init() void { + components = .{}; + } + pub fn deinit() void { + components.deinit(main.globalAllocator); + } + pub fn loadFromData(entity: Entity, _: *utils.BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { + if (version != 0) return error.InvalidComponentVersion; + load(entity); + } + pub fn load(entity: Entity) void { + put(entity, Component{ + .progress = 0, + }); + } + pub fn unload(entity: Entity) void { + components.remove(entity) catch {}; + } + pub fn put(entity: Entity, renderComponent: Component) void { + const ptr = components.get(entity) orelse components.add(main.globalAllocator, entity); + ptr.* = renderComponent; + } + pub fn get(entity: Entity) ?*Component { + return components.get(entity); + } +}; diff --git a/src/renderer.zig b/src/renderer.zig index 770bf4f521..2a6361ae4c 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -923,7 +923,6 @@ pub const MeshSelection = struct { // MARK: MeshSelection var neighborOfSelection: chunk.Neighbor = undefined; pub var selectedBlockPos: ?Vec3i = null; var lastSelectedBlockPos: Vec3i = undefined; - var currentBlockProgress: f32 = 0; var selectionMin: Vec3f = undefined; var selectionMax: Vec3f = undefined; var selectionNormal: Vec3f = undefined; @@ -1066,6 +1065,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection pub fn breakBlock(inventory: main.items.Inventory.ClientInventory, slot: u32, deltaTime: f64) void { const swingArm = main.entity.components.@"cubyz:swing_arm".client.get(main.game.Player.id) orelse return; // player can't swing... + const brokenBlock = main.entity.components.@"cubyz:broken".client.get(main.game.Player.id) orelse return; // player can't break blocks... if (selectedBlockPos) |selectedPos| { const stack = inventory.getStack(slot); @@ -1080,7 +1080,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); swingArm.currentSwingProgress = 0; lastSelectedBlockPos = selectedPos; - currentBlockProgress = 0; + brokenBlock.progress = 0; } const block = mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; const holdingTargetedBlock = stack.item == .baseItem and stack.item.baseItem.block() == block.typ; @@ -1110,16 +1110,16 @@ pub const MeshSelection = struct { // MARK: MeshSelection swingArm.currentSwingProgress += @floatCast(deltaTime); while (swingArm.currentSwingProgress > swingArm.currentSwingTime) { swingArm.currentSwingProgress -= swingArm.currentSwingTime; - currentBlockProgress += damage*swingArm.currentSwingTime/swingTime/block.blockHealth(); - if (currentBlockProgress > 0.9999) break; + brokenBlock.progress += damage*swingArm.currentSwingTime/swingTime/block.blockHealth(); + if (brokenBlock.progress > 0.9999) break; const swings = @ceil(block.blockHealth()/damage); const damagePerSwing = block.blockHealth()/swings; swingArm.currentSwingTime = damagePerSwing/damage*swingTime; } - if (currentBlockProgress < 0.9999) { + if (brokenBlock.progress < 0.9999) { mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); - if (currentBlockProgress != 0) { - mesh_storage.addBreakingAnimation(lastSelectedBlockPos, currentBlockProgress); + if (brokenBlock.progress != 0) { + mesh_storage.addBreakingAnimation(lastSelectedBlockPos, brokenBlock.progress); } main.sync.client.mutex.unlock(); @@ -1127,7 +1127,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection } else { swingArm.currentSwingProgress = 0; mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); - currentBlockProgress = 0; + brokenBlock.progress = 0; swingArm.currentSwingTime = 0; } } else { diff --git a/src/server/server.zig b/src/server/server.zig index 6684813aa9..db48dd5d1f 100644 --- a/src/server/server.zig +++ b/src/server/server.zig @@ -325,6 +325,7 @@ pub const User = struct { // MARK: User main.entity.components.@"cubyz:player".server.load(self.id, @truncate(self.playerIndex)); main.entity.components.@"cubyz:swing_arm".server.load(self.id); + main.entity.components.@"cubyz:broken".server.load(self.id); } fn simArrIndex(x: i32) usize { From 1d9dfbb911b7ca8a4008b2ba53444d08e3ad1157 Mon Sep 17 00:00:00 2001 From: Wunka <56411012+Wunka@users.noreply.github.com> Date: Tue, 29 Sep 2026 14:21:56 +0200 Subject: [PATCH 4/6] current progress --- src/game.zig | 12 ++--- src/renderer.zig | 100 +++++++++++++++++++++++------------------- src/server/Entity.zig | 11 +++++ 3 files changed, 73 insertions(+), 50 deletions(-) diff --git a/src/game.zig b/src/game.zig index cf679ee869..2867013795 100644 --- a/src/game.zig +++ b/src/game.zig @@ -188,12 +188,12 @@ pub const Player = struct { // MARK: Player } pub fn placeBlock(mods: main.Window.Key.Modifiers) void { - if (main.renderer.MeshSelection.selectedBlockPos) |blockPos| blk: { - const mesh = main.renderer.mesh_storage.getMesh(.initFromWorldPos(blockPos, 1)) orelse break :blk; - const block = mesh.chunk.getBlock(blockPos[0] - mesh.pos.wx, blockPos[1] - mesh.pos.wy, blockPos[2] - mesh.pos.wz); + if (main.game.Player.super.getSelected(inventory.getItem(selectedSlot), .client)) |selected| blk: { + const mesh = main.renderer.mesh_storage.getMesh(.initFromWorldPos(selected.blockPos, 1)) orelse break :blk; + const block = mesh.chunk.getBlock(selected.blockPos[0] - mesh.pos.wx, selected.blockPos[1] - mesh.pos.wy, selected.blockPos[2] - mesh.pos.wz); const onInteract = block.onInteract(); if (!mods.shift) { - if (onInteract.run(.{.blockPos = blockPos, .block = block, .chunk = mesh.chunk}) == .handled) return; + if (onInteract.run(.{.blockPos = selected.blockPos, .block = block, .chunk = mesh.chunk}) == .handled) return; } } @@ -224,8 +224,8 @@ pub const Player = struct { // MARK: Player } pub fn acquireSelectedBlock() void { - if (main.renderer.MeshSelection.selectedBlockPos) |selectedPos| { - const block = main.renderer.mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; + if (main.game.Player.super.getSelected(inventory.getItem(selectedSlot), .client)) |selected| { + const block = main.renderer.mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; const item: items.Item = for (0..items.itemListSize) |idx| { const baseItem: main.items.BaseItemIndex = @enumFromInt(idx); diff --git a/src/renderer.zig b/src/renderer.zig index 2a6361ae4c..a13985df8c 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -137,7 +137,7 @@ var worldFrameBuffer: graphics.FrameBuffer = undefined; pub var lastWidth: u31 = 0; pub var lastHeight: u31 = 0; -var lastFov: f32 = 0; +pub var lastFov: f32 = 0; pub fn updateFov(fov: f32) void { if (lastFov != fov) { lastFov = fov; @@ -232,8 +232,6 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const meshes = mesh_storage.updateAndGetRenderChunks(world.conn, &frustum, playerPos, settings.renderDistance); gpu_performance_measuring.startQuery(.chunk_rendering_preparation); - const direction = crosshairDirection(game.camera.viewMatrix, lastFov, lastWidth, lastHeight); - MeshSelection.select(playerPos, direction, game.Player.inventory.getItem(game.Player.selectedSlot)); chunk_meshing.beginRender(); @@ -921,14 +919,18 @@ pub const MeshSelection = struct { // MARK: MeshSelection var posBeforeBlock: Vec3i = undefined; var neighborOfSelection: chunk.Neighbor = undefined; - pub var selectedBlockPos: ?Vec3i = null; var lastSelectedBlockPos: Vec3i = undefined; - var selectionMin: Vec3f = undefined; - var selectionMax: Vec3f = undefined; - var selectionNormal: Vec3f = undefined; var lastPos: Vec3d = undefined; var lastDir: Vec3f = undefined; - pub fn select(pos: Vec3d, _dir: Vec3f, item: main.items.Item) void { + + pub const Selection = struct { + blockPos: Vec3i, + min: Vec3f, + max: Vec3f, + normal: Vec3f, + }; + + pub fn select(pos: Vec3d, _dir: Vec3f, item: main.items.Item) ?Selection { lastPos = pos; const dir: Vec3d = @floatCast(_dir); lastDir = _dir; @@ -946,8 +948,6 @@ pub const MeshSelection = struct { // MARK: MeshSelection var total_tMax: f64 = 0; - selectedBlockPos = null; - while (total_tMax < closestDistance) { const block = mesh_storage.getBlockFromRenderThread(voxelPos[0], voxelPos[1], voxelPos[2]) orelse break; if (block.typ != 0) blk: { @@ -956,11 +956,12 @@ pub const MeshSelection = struct { // MARK: MeshSelection const relativePlayerPos: Vec3f = @floatCast(pos - @as(Vec3d, @floatFromInt(voxelPos))); if (block.mode().rayIntersection(block, item, relativePlayerPos, _dir)) |intersection| { if (intersection.distance <= closestDistance) { - selectedBlockPos = voxelPos; - selectionMin = intersection.min; - selectionMax = intersection.max; - selectionNormal = intersection.normal; - break; + return .{ + .blockPos = voxelPos, + .min = intersection.min, + .max = intersection.max, + .normal = intersection.normal, + }; } } } @@ -992,6 +993,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection } } // TODO: Test entities + return null; } fn canPlaceBlock(pos: Vec3i, block: main.blocks.Block) bool { @@ -1002,8 +1004,8 @@ pub const MeshSelection = struct { // MARK: MeshSelection } pub fn placeBlock(inventory: main.items.Inventory.ClientInventory, slot: u32) void { - if (selectedBlockPos) |selectedPos| { - var oldBlock = mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; + if (main.game.Player.super.getSelected(inventory.getItem(slot), .client)) |selected| { + var oldBlock = mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; var block = oldBlock; switch (inventory.getItem(slot)) { .baseItem => |baseItem| { @@ -1012,22 +1014,22 @@ pub const MeshSelection = struct { // MARK: MeshSelection var neighborDir = Vec3i{0, 0, 0}; // Check if stuff can be added to the block itself: if (itemBlock == block.typ) { - const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selectedPos))); - if (rotationMode.generateData(main.game.world.?, selectedPos, relPos, lastDir, neighborDir, null, &block, .{.typ = 0, .data = 0}, false)) { - if (!canPlaceBlock(selectedPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, selectedPos, oldBlock, block); + const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selected.blockPos))); + if (rotationMode.generateData(main.game.world.?, selected.blockPos, relPos, lastDir, neighborDir, null, &block, .{.typ = 0, .data = 0}, false)) { + if (!canPlaceBlock(selected.blockPos, block)) return; + updateBlockAndSendUpdate(inventory, slot, selected, oldBlock, block); return; } } else { if (rotationMode.modifyBlock(&block, itemBlock)) { - if (!canPlaceBlock(selectedPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, selectedPos, oldBlock, block); + if (!canPlaceBlock(selected.blockPos, block)) return; + updateBlockAndSendUpdate(inventory, slot, selected, oldBlock, block); return; } } // Check the block in front of it: const neighborPos = posBeforeBlock; - neighborDir = selectedPos - posBeforeBlock; + neighborDir = selected.blockPos - posBeforeBlock; const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos))); const neighborBlock = block; oldBlock = mesh_storage.getBlockFromRenderThread(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return; @@ -1035,7 +1037,12 @@ pub const MeshSelection = struct { // MARK: MeshSelection if (block.typ == itemBlock) { if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, false)) { if (!canPlaceBlock(neighborPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, neighborPos, oldBlock, block); + updateBlockAndSendUpdate(inventory, slot, .{ + .blockPos = neighborPos, + .normal = selected.normal, + .min = selected.min, + .max = selected.max, + }, oldBlock, block); return; } } else { @@ -1044,14 +1051,19 @@ pub const MeshSelection = struct { // MARK: MeshSelection block.data = 0; if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, true)) { if (!canPlaceBlock(neighborPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, neighborPos, oldBlock, block); + updateBlockAndSendUpdate(inventory, slot, .{ + .blockPos = neighborPos, + .normal = selected.normal, + .min = selected.min, + .max = selected.max, + }, oldBlock, block); return; } } } if (std.mem.eql(u8, baseItem.id(), "cubyz:selection_wand")) { - game.Player.selectionPosition2 = selectedPos; - main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos2, selectedPos); + game.Player.selectionPosition2 = selected.blockPos; + main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos2, selected.blockPos); return; } }, @@ -1067,26 +1079,26 @@ pub const MeshSelection = struct { // MARK: MeshSelection const swingArm = main.entity.components.@"cubyz:swing_arm".client.get(main.game.Player.id) orelse return; // player can't swing... const brokenBlock = main.entity.components.@"cubyz:broken".client.get(main.game.Player.id) orelse return; // player can't break blocks... - if (selectedBlockPos) |selectedPos| { + if (main.game.Player.super.getSelected(inventory.getItem(slot), .client)) |selected| { const stack = inventory.getStack(slot); const isSelectionWand = stack.item == .baseItem and std.mem.eql(u8, stack.item.baseItem.id(), "cubyz:selection_wand"); if (isSelectionWand) { - game.Player.selectionPosition1 = selectedPos; - main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos1, selectedPos); + game.Player.selectionPosition1 = selected.blockPos; + main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos1, selected.blockPos); return; } - if (@reduce(.Or, lastSelectedBlockPos != selectedPos)) { + if (@reduce(.Or, lastSelectedBlockPos != selected.blockPos)) { mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); swingArm.currentSwingProgress = 0; - lastSelectedBlockPos = selectedPos; + lastSelectedBlockPos = selected.blockPos; brokenBlock.progress = 0; } - const block = mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; + const block = mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; const holdingTargetedBlock = stack.item == .baseItem and stack.item.baseItem.block() == block.typ; if ((block.hasTag(.fluid) or block.hasTag(.air)) and !holdingTargetedBlock) return; - const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selectedPos))); + const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selected.blockPos))); main.sync.client.mutex.lock(); if (!game.Player.isCreative()) { @@ -1143,26 +1155,26 @@ pub const MeshSelection = struct { // MARK: MeshSelection main.sync.client.mutex.unlock(); if (newBlock != block) { - updateBlockAndSendUpdate(inventory, slot, selectedPos, block, newBlock); + updateBlockAndSendUpdate(inventory, slot, selected, block, newBlock); } } } - fn updateBlockAndSendUpdate(source: main.items.Inventory.ClientInventory, slot: u32, pos: Vec3i, oldBlock: blocks.Block, newBlock: blocks.Block) void { + fn updateBlockAndSendUpdate(source: main.items.Inventory.ClientInventory, slot: u32, selected: Selection, oldBlock: blocks.Block, newBlock: blocks.Block) void { main.sync.client.executeCommand(.{ .updateBlock = .{ .source = .{.inv = source.super, .slot = slot}, .dropLocation = .{ - .worldPos = pos, - .normalDir = selectionNormal, - .min = selectionMin, - .max = selectionMax, + .worldPos = selected.blockPos, + .normalDir = selected.normal, + .min = selected.min, + .max = selected.max, }, .oldBlock = oldBlock, .newBlock = newBlock, }, }); - mesh_storage.updateBlock(.{.pos = pos, .newBlock = newBlock, .blockEntityData = &.{}}); + mesh_storage.updateBlock(.{.pos = selected.blockPos, .newBlock = newBlock, .blockEntityData = &.{}}); } pub fn drawCube(relativePositionToPlayer: Vec3d, min: Vec3f, max: Vec3f) void { @@ -1184,8 +1196,8 @@ pub const MeshSelection = struct { // MARK: MeshSelection pub fn render(playerPos: Vec3d) void { if (main.gui.hideGui) return; - if (selectedBlockPos) |_selectedBlockPos| { - drawCube(@as(Vec3d, @floatFromInt(_selectedBlockPos)) - playerPos, selectionMin, selectionMax); + if (main.game.Player.super.getSelected(main.game.Player.inventory.getItem(main.game.Player.selectedSlot), .client)) |selected| { + drawCube(@as(Vec3d, @floatFromInt(selected.blockPos)) - playerPos, selected.min, selected.max); } if (game.Player.selectionPosition1) |pos1| { if (game.Player.selectionPosition2) |pos2| { diff --git a/src/server/Entity.zig b/src/server/Entity.zig index 169412a84f..5479d28194 100644 --- a/src/server/Entity.zig +++ b/src/server/Entity.zig @@ -73,3 +73,14 @@ pub fn deinit(self: *@This(), comptime side: main.sync.Side) void { main.entity.client.removeAllComponents(self.id); } } + +fn getDirection(self: *const @This()) Vec3f { + return vec.rotateZ(vec.rotateX(Vec3f{0, 1, 0}, -self.rot[0]), -self.rot[2]); +} + +pub fn getSelected(self: *const @This(), item: main.items.Item, comptime side: main.sync.Side) ?main.renderer.MeshSelection.Selection { + switch (side) { + .client => return main.renderer.MeshSelection.select(main.game.Player.getEyePosBlocking(), main.renderer.crosshairDirection(main.game.camera.viewMatrix, main.renderer.lastFov, main.renderer.lastWidth, main.renderer.lastHeight), item), + .server => return main.renderer.MeshSelection.select(self.pos, self.getDirection(), item), + } +} From 82940536a6906c25c92da67c71084f7a6886f869 Mon Sep 17 00:00:00 2001 From: Wunka <56411012+Wunka@users.noreply.github.com> Date: Thu, 1 Oct 2026 10:14:12 +0200 Subject: [PATCH 5/6] remove lastPos, lastDir --- src/renderer.zig | 270 +++++++++++++++++++++--------------------- src/server/Entity.zig | 12 +- 2 files changed, 141 insertions(+), 141 deletions(-) diff --git a/src/renderer.zig b/src/renderer.zig index a13985df8c..3e9509eef1 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -920,8 +920,6 @@ pub const MeshSelection = struct { // MARK: MeshSelection var posBeforeBlock: Vec3i = undefined; var neighborOfSelection: chunk.Neighbor = undefined; var lastSelectedBlockPos: Vec3i = undefined; - var lastPos: Vec3d = undefined; - var lastDir: Vec3f = undefined; pub const Selection = struct { blockPos: Vec3i, @@ -931,9 +929,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection }; pub fn select(pos: Vec3d, _dir: Vec3f, item: main.items.Item) ?Selection { - lastPos = pos; const dir: Vec3d = @floatCast(_dir); - lastDir = _dir; // Test blocks: const closestDistance: f64 = 6.0; // selection now limited @@ -1004,74 +1000,76 @@ pub const MeshSelection = struct { // MARK: MeshSelection } pub fn placeBlock(inventory: main.items.Inventory.ClientInventory, slot: u32) void { - if (main.game.Player.super.getSelected(inventory.getItem(slot), .client)) |selected| { - var oldBlock = mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; - var block = oldBlock; - switch (inventory.getItem(slot)) { - .baseItem => |baseItem| { - if (baseItem.block()) |itemBlock| { - const rotationMode = blocks.Block.mode(.{.typ = itemBlock, .data = 0}); - var neighborDir = Vec3i{0, 0, 0}; - // Check if stuff can be added to the block itself: - if (itemBlock == block.typ) { - const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selected.blockPos))); - if (rotationMode.generateData(main.game.world.?, selected.blockPos, relPos, lastDir, neighborDir, null, &block, .{.typ = 0, .data = 0}, false)) { - if (!canPlaceBlock(selected.blockPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, selected, oldBlock, block); - return; - } - } else { - if (rotationMode.modifyBlock(&block, itemBlock)) { - if (!canPlaceBlock(selected.blockPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, selected, oldBlock, block); - return; - } + const selected = main.game.Player.super.getSelected(inventory.getItem(slot), .client) orelse return; + const lastPos = main.game.Player.getEyePosBlocking(); + const lastDir = main.game.Player.super.getDirection(.client); + + var oldBlock = mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; + var block = oldBlock; + switch (inventory.getItem(slot)) { + .baseItem => |baseItem| { + if (baseItem.block()) |itemBlock| { + const rotationMode = blocks.Block.mode(.{.typ = itemBlock, .data = 0}); + var neighborDir = Vec3i{0, 0, 0}; + // Check if stuff can be added to the block itself: + if (itemBlock == block.typ) { + const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selected.blockPos))); + if (rotationMode.generateData(main.game.world.?, selected.blockPos, relPos, lastDir, neighborDir, null, &block, .{.typ = 0, .data = 0}, false)) { + if (!canPlaceBlock(selected.blockPos, block)) return; + updateBlockAndSendUpdate(inventory, slot, selected, oldBlock, block); + return; } - // Check the block in front of it: - const neighborPos = posBeforeBlock; - neighborDir = selected.blockPos - posBeforeBlock; - const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos))); - const neighborBlock = block; - oldBlock = mesh_storage.getBlockFromRenderThread(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return; - block = oldBlock; - if (block.typ == itemBlock) { - if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, false)) { - if (!canPlaceBlock(neighborPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, .{ - .blockPos = neighborPos, - .normal = selected.normal, - .min = selected.min, - .max = selected.max, - }, oldBlock, block); - return; - } - } else { - if (!block.replaceable()) return; - block.typ = itemBlock; - block.data = 0; - if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, true)) { - if (!canPlaceBlock(neighborPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, .{ - .blockPos = neighborPos, - .normal = selected.normal, - .min = selected.min, - .max = selected.max, - }, oldBlock, block); - return; - } + } else { + if (rotationMode.modifyBlock(&block, itemBlock)) { + if (!canPlaceBlock(selected.blockPos, block)) return; + updateBlockAndSendUpdate(inventory, slot, selected, oldBlock, block); + return; } } - if (std.mem.eql(u8, baseItem.id(), "cubyz:selection_wand")) { - game.Player.selectionPosition2 = selected.blockPos; - main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos2, selected.blockPos); - return; + // Check the block in front of it: + const neighborPos = posBeforeBlock; + neighborDir = selected.blockPos - posBeforeBlock; + const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos))); + const neighborBlock = block; + oldBlock = mesh_storage.getBlockFromRenderThread(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return; + block = oldBlock; + if (block.typ == itemBlock) { + if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, false)) { + if (!canPlaceBlock(neighborPos, block)) return; + updateBlockAndSendUpdate(inventory, slot, .{ + .blockPos = neighborPos, + .normal = selected.normal, + .min = selected.min, + .max = selected.max, + }, oldBlock, block); + return; + } + } else { + if (!block.replaceable()) return; + block.typ = itemBlock; + block.data = 0; + if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, true)) { + if (!canPlaceBlock(neighborPos, block)) return; + updateBlockAndSendUpdate(inventory, slot, .{ + .blockPos = neighborPos, + .normal = selected.normal, + .min = selected.min, + .max = selected.max, + }, oldBlock, block); + return; + } } - }, - .proceduralItem => |proceduralItem| { - _ = proceduralItem; // TODO: Tools might change existing blocks. - }, - .null => {}, - } + } + if (std.mem.eql(u8, baseItem.id(), "cubyz:selection_wand")) { + game.Player.selectionPosition2 = selected.blockPos; + main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos2, selected.blockPos); + return; + } + }, + .proceduralItem => |proceduralItem| { + _ = proceduralItem; // TODO: Tools might change existing blocks. + }, + .null => {}, } } @@ -1079,84 +1077,86 @@ pub const MeshSelection = struct { // MARK: MeshSelection const swingArm = main.entity.components.@"cubyz:swing_arm".client.get(main.game.Player.id) orelse return; // player can't swing... const brokenBlock = main.entity.components.@"cubyz:broken".client.get(main.game.Player.id) orelse return; // player can't break blocks... - if (main.game.Player.super.getSelected(inventory.getItem(slot), .client)) |selected| { - const stack = inventory.getStack(slot); - const isSelectionWand = stack.item == .baseItem and std.mem.eql(u8, stack.item.baseItem.id(), "cubyz:selection_wand"); - if (isSelectionWand) { - game.Player.selectionPosition1 = selected.blockPos; - main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos1, selected.blockPos); - return; - } + const selected = main.game.Player.super.getSelected(inventory.getItem(slot), .client) orelse return; + const lastPos = main.game.Player.getEyePosBlocking(); + const lastDir = main.game.Player.super.getDirection(.client); + + const stack = inventory.getStack(slot); + const isSelectionWand = stack.item == .baseItem and std.mem.eql(u8, stack.item.baseItem.id(), "cubyz:selection_wand"); + if (isSelectionWand) { + game.Player.selectionPosition1 = selected.blockPos; + main.network.protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos1, selected.blockPos); + return; + } - if (@reduce(.Or, lastSelectedBlockPos != selected.blockPos)) { - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); - swingArm.currentSwingProgress = 0; - lastSelectedBlockPos = selected.blockPos; - brokenBlock.progress = 0; + if (@reduce(.Or, lastSelectedBlockPos != selected.blockPos)) { + mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + swingArm.currentSwingProgress = 0; + lastSelectedBlockPos = selected.blockPos; + brokenBlock.progress = 0; + } + const block = mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; + const holdingTargetedBlock = stack.item == .baseItem and stack.item.baseItem.block() == block.typ; + if ((block.hasTag(.fluid) or block.hasTag(.air)) and !holdingTargetedBlock) return; + + const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selected.blockPos))); + + main.sync.client.mutex.lock(); + if (!game.Player.isCreative()) { + var damage: f32 = main.game.Player.defaultBlockDamage; + const isProceduralItem = stack.item == .proceduralItem; + if (isProceduralItem) { + damage = stack.item.proceduralItem.getBlockDamage(block); } - const block = mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; - const holdingTargetedBlock = stack.item == .baseItem and stack.item.baseItem.block() == block.typ; - if ((block.hasTag(.fluid) or block.hasTag(.air)) and !holdingTargetedBlock) return; - - const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(selected.blockPos))); - - main.sync.client.mutex.lock(); - if (!game.Player.isCreative()) { - var damage: f32 = main.game.Player.defaultBlockDamage; - const isProceduralItem = stack.item == .proceduralItem; - if (isProceduralItem) { - damage = stack.item.proceduralItem.getBlockDamage(block); + damage -= block.blockResistance(); + if (damage > 0) { + const swingTime = if (isProceduralItem and stack.item.proceduralItem.isEffectiveOn(block)) 1.0/stack.item.proceduralItem.getProperty(.swingSpeed) else 0.5; + if (swingArm.currentSwingTime > swingTime) { + swingArm.currentSwingProgress = 0; + swingArm.currentSwingTime = 0; } - damage -= block.blockResistance(); - if (damage > 0) { - const swingTime = if (isProceduralItem and stack.item.proceduralItem.isEffectiveOn(block)) 1.0/stack.item.proceduralItem.getProperty(.swingSpeed) else 0.5; - if (swingArm.currentSwingTime > swingTime) { - swingArm.currentSwingProgress = 0; - swingArm.currentSwingTime = 0; - } - if (swingArm.currentSwingTime == 0) { - const swings = @ceil(block.blockHealth()/damage); - const damagePerSwing = block.blockHealth()/swings; - swingArm.currentSwingTime = damagePerSwing/damage*swingTime; - } - swingArm.currentSwingProgress += @floatCast(deltaTime); - while (swingArm.currentSwingProgress > swingArm.currentSwingTime) { - swingArm.currentSwingProgress -= swingArm.currentSwingTime; - brokenBlock.progress += damage*swingArm.currentSwingTime/swingTime/block.blockHealth(); - if (brokenBlock.progress > 0.9999) break; - const swings = @ceil(block.blockHealth()/damage); - const damagePerSwing = block.blockHealth()/swings; - swingArm.currentSwingTime = damagePerSwing/damage*swingTime; - } - if (brokenBlock.progress < 0.9999) { - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); - if (brokenBlock.progress != 0) { - mesh_storage.addBreakingAnimation(lastSelectedBlockPos, brokenBlock.progress); - } - main.sync.client.mutex.unlock(); - - return; - } else { - swingArm.currentSwingProgress = 0; - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); - brokenBlock.progress = 0; - swingArm.currentSwingTime = 0; + if (swingArm.currentSwingTime == 0) { + const swings = @ceil(block.blockHealth()/damage); + const damagePerSwing = block.blockHealth()/swings; + swingArm.currentSwingTime = damagePerSwing/damage*swingTime; + } + swingArm.currentSwingProgress += @floatCast(deltaTime); + while (swingArm.currentSwingProgress > swingArm.currentSwingTime) { + swingArm.currentSwingProgress -= swingArm.currentSwingTime; + brokenBlock.progress += damage*swingArm.currentSwingTime/swingTime/block.blockHealth(); + if (brokenBlock.progress > 0.9999) break; + const swings = @ceil(block.blockHealth()/damage); + const damagePerSwing = block.blockHealth()/swings; + swingArm.currentSwingTime = damagePerSwing/damage*swingTime; + } + if (brokenBlock.progress < 0.9999) { + mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + if (brokenBlock.progress != 0) { + mesh_storage.addBreakingAnimation(lastSelectedBlockPos, brokenBlock.progress); } - } else { main.sync.client.mutex.unlock(); + return; + } else { + swingArm.currentSwingProgress = 0; + mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + brokenBlock.progress = 0; + swingArm.currentSwingTime = 0; } } else { - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + main.sync.client.mutex.unlock(); + return; } + } else { + mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + } - var newBlock = block; - block.mode().onBlockBreaking(inventory.getStack(slot).item, relPos, lastDir, &newBlock); - main.sync.client.mutex.unlock(); + var newBlock = block; + block.mode().onBlockBreaking(inventory.getStack(slot).item, relPos, lastDir, &newBlock); + main.sync.client.mutex.unlock(); - if (newBlock != block) { - updateBlockAndSendUpdate(inventory, slot, selected, block, newBlock); - } + if (newBlock != block) { + updateBlockAndSendUpdate(inventory, slot, selected, block, newBlock); } } diff --git a/src/server/Entity.zig b/src/server/Entity.zig index 5479d28194..5c9d217e82 100644 --- a/src/server/Entity.zig +++ b/src/server/Entity.zig @@ -74,13 +74,13 @@ pub fn deinit(self: *@This(), comptime side: main.sync.Side) void { } } -fn getDirection(self: *const @This()) Vec3f { - return vec.rotateZ(vec.rotateX(Vec3f{0, 1, 0}, -self.rot[0]), -self.rot[2]); +pub fn getDirection(_: *const @This(), comptime side: main.sync.Side) Vec3f { + return switch (side) { + .client => main.renderer.crosshairDirection(main.game.camera.viewMatrix, main.renderer.lastFov, main.renderer.lastWidth, main.renderer.lastHeight), + .server => unreachable, + }; } pub fn getSelected(self: *const @This(), item: main.items.Item, comptime side: main.sync.Side) ?main.renderer.MeshSelection.Selection { - switch (side) { - .client => return main.renderer.MeshSelection.select(main.game.Player.getEyePosBlocking(), main.renderer.crosshairDirection(main.game.camera.viewMatrix, main.renderer.lastFov, main.renderer.lastWidth, main.renderer.lastHeight), item), - .server => return main.renderer.MeshSelection.select(self.pos, self.getDirection(), item), - } + return main.renderer.MeshSelection.select(main.game.Player.getEyePosBlocking(), self.getDirection(side), item); } From a0f05e63cb86fc41ff4545626b4c2e5f199e64be Mon Sep 17 00:00:00 2001 From: Wunka <56411012+Wunka@users.noreply.github.com> Date: Thu, 1 Oct 2026 10:38:51 +0200 Subject: [PATCH 6/6] remove the rest --- src/entityComponent/broken.zig | 6 ++++ src/renderer.zig | 58 ++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/entityComponent/broken.zig b/src/entityComponent/broken.zig index 6bc1b8bf00..bde9e5792c 100644 --- a/src/entityComponent/broken.zig +++ b/src/entityComponent/broken.zig @@ -35,6 +35,7 @@ pub const entityComponentVersion = 0; pub const client = struct { const Component = struct { progress: f32, + blockPos: Vec3i, }; pub var components: main.utils.SparseSet(Component, Entity) = .{}; @@ -48,9 +49,11 @@ pub const client = struct { pub fn load(entity: Entity, reader: *utils.BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { if (version != 0) return error.InvalidComponentVersion; const progress = reader.readFloat(f32) catch return error.UnreadableComponentData; + const blockPos = reader.readVec(Vec3i) catch return error.UnreadableComponentData; const ptr = components.get(entity) orelse components.add(main.globalAllocator, entity); ptr.* = Component{ .progress = progress, + .blockPos = blockPos, }; } pub fn unload(entity: Entity) void { @@ -64,8 +67,10 @@ pub const client = struct { pub const server = struct { pub const Component = struct { progress: f32, + blockPos: Vec3i, pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { writer.writeFloat(f32, self.progress); + writer.writeVec(Vec3i, self.blockPos); if (audience == .disk) return .discard; return .save; } @@ -84,6 +89,7 @@ pub const server = struct { pub fn load(entity: Entity) void { put(entity, Component{ .progress = 0, + .blockPos = .{0, 0, 0}, }); } pub fn unload(entity: Entity) void { diff --git a/src/renderer.zig b/src/renderer.zig index 3e9509eef1..36a2f164b9 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -917,15 +917,24 @@ pub const MeshSelection = struct { // MARK: MeshSelection pipeline.deinit(); } - var posBeforeBlock: Vec3i = undefined; - var neighborOfSelection: chunk.Neighbor = undefined; - var lastSelectedBlockPos: Vec3i = undefined; - pub const Selection = struct { blockPos: Vec3i, min: Vec3f, max: Vec3f, normal: Vec3f, + posBeforeBlock: Vec3i, + neighborOfSelection: chunk.Neighbor, + + fn copyWithNewBlockPos(self: Selection, newBlockPos: Vec3i) Selection { + return .{ + .blockPos = newBlockPos, + .min = self.min, + .max = self.max, + .normal = self.normal, + .posBeforeBlock = self.posBeforeBlock, + .neighborOfSelection = self.neighborOfSelection, + }; + } }; pub fn select(pos: Vec3d, _dir: Vec3f, item: main.items.Item) ?Selection { @@ -944,6 +953,9 @@ pub const MeshSelection = struct { // MARK: MeshSelection var total_tMax: f64 = 0; + var posBeforeBlock: Vec3i = undefined; + var neighborOfSelection: chunk.Neighbor = undefined; + while (total_tMax < closestDistance) { const block = mesh_storage.getBlockFromRenderThread(voxelPos[0], voxelPos[1], voxelPos[2]) orelse break; if (block.typ != 0) blk: { @@ -957,6 +969,8 @@ pub const MeshSelection = struct { // MARK: MeshSelection .min = intersection.min, .max = intersection.max, .normal = intersection.normal, + .posBeforeBlock = posBeforeBlock, + .neighborOfSelection = neighborOfSelection, }; } } @@ -1027,35 +1041,25 @@ pub const MeshSelection = struct { // MARK: MeshSelection } } // Check the block in front of it: - const neighborPos = posBeforeBlock; - neighborDir = selected.blockPos - posBeforeBlock; + const neighborPos = selected.posBeforeBlock; + neighborDir = selected.blockPos - selected.posBeforeBlock; const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos))); const neighborBlock = block; oldBlock = mesh_storage.getBlockFromRenderThread(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return; block = oldBlock; if (block.typ == itemBlock) { - if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, false)) { + if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, selected.neighborOfSelection, &block, neighborBlock, false)) { if (!canPlaceBlock(neighborPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, .{ - .blockPos = neighborPos, - .normal = selected.normal, - .min = selected.min, - .max = selected.max, - }, oldBlock, block); + updateBlockAndSendUpdate(inventory, slot, selected.copyWithNewBlockPos(neighborPos), oldBlock, block); return; } } else { if (!block.replaceable()) return; block.typ = itemBlock; block.data = 0; - if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, true)) { + if (rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, selected.neighborOfSelection, &block, neighborBlock, true)) { if (!canPlaceBlock(neighborPos, block)) return; - updateBlockAndSendUpdate(inventory, slot, .{ - .blockPos = neighborPos, - .normal = selected.normal, - .min = selected.min, - .max = selected.max, - }, oldBlock, block); + updateBlockAndSendUpdate(inventory, slot, selected.copyWithNewBlockPos(neighborPos), oldBlock, block); return; } } @@ -1089,10 +1093,10 @@ pub const MeshSelection = struct { // MARK: MeshSelection return; } - if (@reduce(.Or, lastSelectedBlockPos != selected.blockPos)) { - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + if (@reduce(.Or, brokenBlock.blockPos != selected.blockPos)) { + mesh_storage.removeBreakingAnimation(brokenBlock.blockPos); swingArm.currentSwingProgress = 0; - lastSelectedBlockPos = selected.blockPos; + brokenBlock.blockPos = selected.blockPos; brokenBlock.progress = 0; } const block = mesh_storage.getBlockFromRenderThread(selected.blockPos[0], selected.blockPos[1], selected.blockPos[2]) orelse return; @@ -1130,16 +1134,16 @@ pub const MeshSelection = struct { // MARK: MeshSelection swingArm.currentSwingTime = damagePerSwing/damage*swingTime; } if (brokenBlock.progress < 0.9999) { - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + mesh_storage.removeBreakingAnimation(brokenBlock.blockPos); if (brokenBlock.progress != 0) { - mesh_storage.addBreakingAnimation(lastSelectedBlockPos, brokenBlock.progress); + mesh_storage.addBreakingAnimation(brokenBlock.blockPos, brokenBlock.progress); } main.sync.client.mutex.unlock(); return; } else { swingArm.currentSwingProgress = 0; - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + mesh_storage.removeBreakingAnimation(brokenBlock.blockPos); brokenBlock.progress = 0; swingArm.currentSwingTime = 0; } @@ -1148,7 +1152,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection return; } } else { - mesh_storage.removeBreakingAnimation(lastSelectedBlockPos); + mesh_storage.removeBreakingAnimation(brokenBlock.blockPos); } var newBlock = block;