-
Notifications
You must be signed in to change notification settings - Fork 69
Persistent and synced block damage #1261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Persistent and synced block damage #1261
Conversation
Cubyz.2025-04-01.01-14-31.mp4 |
I think it's safe to assume that the timers are fairly synchronized, so we can just simulate the entire decay on the client side and only send updates when another player increases the breaking state.
Ideally you'd only update those that actually changed their breaking animation frame. |
Alr, I guess I should open this PR for reviews, even though it will hurt, it is inevitable C: |
src/blocks.zig
Outdated
@@ -362,6 +365,10 @@ pub const Block = packed struct { // MARK: Block | |||
return _touchFunction[self.typ]; | |||
} | |||
|
|||
pub inline fn healingRatio(_: Block) f32 { | |||
return 0.05; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's a constant, then it should be a constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it makes it necessary to use Block.healingRatio
instead of a member function which is less syntacticaly pleasing (and that call which would be optimized away anyway) and makes it more complicated in case we would ever customize healing ration per-block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But having it as a member function implies that it already is customizable, which is misleading.
Also this is supposed to be an optimization and there should be no way to opt out of it.
src/renderer.zig
Outdated
game.Player.selectionPosition1 = selectedPos; | ||
main.network.Protocols.genericUpdate.sendWorldEditPos(main.game.world.?.conn, .selectedPos1, selectedPos); | ||
return; | ||
const selectedPos = selectedBlockPos orelse return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move all the cosmetic changes to another PR.
|
||
self.secondsSinceLastUpdate += deltaTime; | ||
while(self.secondsSinceLastUpdate > updateIntervalSeconds) { | ||
self._update(updateIntervalSeconds); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of updating everything in a big burst every second, maybe a random-tick technique could be used to distribute the load.
@@ -1019,11 +1171,29 @@ fn addBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, textur | |||
}); | |||
} | |||
|
|||
fn removeBreakingAnimationFaces(pos: Vec3i) void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please reorder things in a separate PR
fn removeBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, neighbor: ?chunk.Neighbor) void { | ||
const worldPos = pos +% if(neighbor) |n| n.relPos() else Vec3i{0, 0, 0}; | ||
const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask)); | ||
const mesh = getMeshAndIncreaseRefCount(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return; | ||
defer mesh.decreaseRefCount(); | ||
|
||
mesh.mutex.lock(); | ||
defer mesh.mutex.unlock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed now?
src/server/server.zig
Outdated
@@ -512,6 +511,8 @@ pub fn connectInternal(user: *User) void { | |||
users.append(user); | |||
userMutex.unlock(); | |||
user.conn.handShakeState.store(main.network.Protocols.handShake.stepComplete, .monotonic); | |||
|
|||
// main.network.Protocols.genericUpdate.sendDamageBlock(user.conn, .sync, null, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.
@@ -411,6 +411,120 @@ const WorldIO = struct { // MARK: WorldIO | |||
} | |||
}; | |||
|
|||
pub const BlockDamage = struct { // MARK: BlockDamage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you merge the two versions in a generic way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, but why do you want to merge them? Literally everything else that has client and server side is coded separate if both sides are not identical, and here they are not. Where should this merged version be placed? How should it be merged?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to merge them, so I don't have to review how these two versions are different.
I just noticed they are using the same data structures, but if you say their implementation is sufficiently distinct (i.e. they are different beyond the fact that the client needs to add it to the mesh), then we can keep it like this.
This pull request is a proof of concept for persistent block damage synchronized between client and server.
I see at least few problems:
Current implementation syncs all block damage between server and all clients every tick (to implement decaying damage). Damage done by player mining is synced per block though. Possible fix: heal damaged blocks randomly and send only those healed this tick.Every synchronization (so every game tick) causes all breaking animation faces to be removed and created again (again to guarantee decaying damage works).Current network stack may not be prepared for updates happening so often. Since every time you hit a block you have to send a request to server this would make mining with high latency exponentially more annoying.Related to: #1137