Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
11 changes: 10 additions & 1 deletion addons/netfox.extras/physics/godot_driver_2d.gd.off
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@ func _snapshot_space(tick: int) -> void:
func _rollback_space(tick) -> void:
if snapshots.has(tick):
var rid_states = snapshots[tick]

# Collect valid RIDs to avoid restoring state for freed bodies
var valid_rids := {}
for body in scene_collision_objects:
if is_instance_valid(body):
valid_rids[body.get_rid()] = true

for rid in rid_states.keys():
set_body_states(rid, rid_states[rid])
# Skip bodies freed (eg: despawned) since the snapshot was taken
if valid_rids.has(rid):
set_body_states(rid, rid_states[rid])

@albertok albertok Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of the looping checks maybe we can clear out the object as soon as it's going to queue free.

Instead of node_removed being called from get_tree().node_removed.connect(node_removed) signal on line 16 we directly hook into the https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-signal-tree-exiting signal from the node itself.

We do this in node_added ( second last function in this class ) as the node is being registered.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I like the idea of moving away from the loop. I changed it to a dictionary to improve lookup times.

Unfortunately though, to your point, tree_exiting() is called on any tree removal (including remove_child(), which could be a valid gameplay operation for moving RBs around a scene's tree/heirarchy). https://docs.godotengine.org/en/stable/classes/class_node.html#class-node-private-method-exit-tree And alternatively checking is_queued_for_deletion() would miss direct free() calls on RBs. So from what I see we're stuck without a proper (engine/solver-driven) event-based approach on this.


for body in scene_collision_objects:
if body is CharacterBody2D or body is AnimatableBody2D:
Expand Down
11 changes: 10 additions & 1 deletion addons/netfox.extras/physics/godot_driver_3d.gd.off
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,17 @@ func _snapshot_space(tick: int) -> void:
func _rollback_space(tick) -> void:
if snapshots.has(tick):
var rid_states = snapshots[tick]

# Collect valid RIDs to avoid restoring state for freed bodies
var valid_rids := {}
for body in scene_collision_objects:
if is_instance_valid(body):
valid_rids[body.get_rid()] = true

for rid in rid_states.keys():
set_body_states(rid, rid_states[rid])
# Skip bodies freed (eg: despawned) since the snapshot was taken
if valid_rids.has(rid):
set_body_states(rid, rid_states[rid])

for body in scene_collision_objects:
if body is CharacterBody3D or body is AnimatableBody3D:
Expand Down
11 changes: 6 additions & 5 deletions addons/netfox.extras/physics/physics_driver.gd
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func _ready() -> void:
# Emitted before a tick is run.
func before_tick(_delta: float, tick: int) -> void:
Comment thread
jburdecki marked this conversation as resolved.
Outdated
_snapshot_space(tick)
step_physics(_delta)
step_physics(_delta, tick)

func on_prepare_tick(tick: int) -> void:
if NetworkRollback._rollback_from == tick:
Expand All @@ -48,22 +48,23 @@ func on_prepare_tick(tick: int) -> void:
# Subsequent ticks are re-writing history.
_snapshot_space(tick)

func on_process_tick(_tick: int) -> void:
step_physics(NetworkTime.ticktime)
func on_process_tick(tick: int) -> void:
step_physics(NetworkTime.ticktime, tick)

func after_tick_loop() -> void:
# Remove old snapshots
for i in snapshots.keys():
if i < NetworkRollback.history_start:
snapshots.erase(i)

func step_physics(_delta: float) -> void:
# tick is either the current tick, or the tick being resimulated (if rollback is active)
func step_physics(_delta: float, tick: int) -> void:
Comment thread
jburdecki marked this conversation as resolved.
Outdated
# Break up physics into smaller steps if needed
var frac_delta = _delta / physics_factor
var rollback_participants = get_tree().get_nodes_in_group("network_rigid_body")
for i in range(physics_factor):
for net_rigid_body in rollback_participants:
net_rigid_body._physics_rollback_tick(frac_delta, NetworkTime.tick)
net_rigid_body._physics_rollback_tick(frac_delta, tick)

_physics_step(frac_delta)

Expand Down