Skip to content

call RBs' _physics_rollback_tick(...) with tick rather than NetworkTi…#633

Open
jburdecki wants to merge 3 commits into
foxssake:mainfrom
jburdecki:fix-issue-632
Open

call RBs' _physics_rollback_tick(...) with tick rather than NetworkTi…#633
jburdecki wants to merge 3 commits into
foxssake:mainfrom
jburdecki:fix-issue-632

Conversation

@jburdecki

Copy link
Copy Markdown

Closes #632

Fixes

  1. The issue of rollback_space(tick) trying to apply state to freed bodies
  2. Issues caused by not having the correct tick available during rollback on NetworkRigidbodys. Now pass the tick being resimulated to RBs, rather than passing the current NetworkTime.tick

…me.tick, so as to sim/resim with historically-accurate tick info. Separately, only apply state to valid (unfreed) RBs during rollback.

@albertok albertok left a comment

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.

Nice work!

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.

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.

Interesting. Are those really valid scenarios?

Calling free will definitely break netfox as will re-parenting.

@elementbound I think you've dealt with these life cycles a lot, what are your thoughts?

@jburdecki jburdecki Jul 18, 2026

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.

Maybe they're not valid, if so, let's try the tree_exiting() proposal 👍 .
Curious on elementbound's thoughts here for (1.)

  1. I was thinking re-parenting could be a valid (or desired) gameplay operation (haven't tested/needed to in my current project though).
  2. I've never tried to manually free() a body, have no need to, and personally don't think it's essential to support.

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.

So from what I see we're stuck without a proper (engine/solver-driven) event-based approach on this.

Exactly my experience. Due to lack of a better alternative, netfox considers exiting the tree as being destroyed. Unfortunately I had to make a choice, because after the rewrite, netfox needs to know when things are getting removed. Well either that, or every major system iterating its lists on every tick loop to filter out invalid nodes, which did not seem realistic nor performant to do.

So my suggestion is to keep that pattern, and treat the node exiting the scene tree as the node being destroyed. We can also consider re-adding it to whatever systems we need on _enter_tree().

  1. I was thinking re-parenting could be a valid (or desired) gameplay operation (haven't tested/needed to in my current project though).

I don't disagree, reparenting could be a legitimate use. Though, do objects need to exit the scene tree for that? Is it possible to travel directly from one parent to another?

  1. I've never tried to manually free() a body, have no need to, and personally don't think it's essential to support.

Agreed. If someone really needs to immediately free() something, they can also call the servers directly to deregister. There's also no way to catch that from the outside.

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.

Due to lack of a better alternative, netfox considers exiting the tree as being destroyed.

Makes sense, thanks for clarifying. I will update to the tree_exiting() approach on next pass 👍

Though, do objects need to exit the scene tree for that? Is it possible to travel directly from one parent to another?

No (unless I can be shown how!).
Node::remove_child(...) always calls p_child->propogate_after_exit_tree(); which always calls emit_signal(SceneStringName(tree_exited));.

and Node::reparent(...) always calls Node::remove_child(...).

  1. remove_child(...)
  2. emit_signal(...tree_exited)
  3. reparent(...)

@albertok albertok left a comment

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.

Nice work!

Comment thread addons/netfox.extras/physics/physics_driver.gd Outdated
Comment thread addons/netfox.extras/physics/physics_driver.gd Outdated

# RIDs of tracked bodies currently in the tree. Snapshot entries whose
# body has been freed are skipped for state updates in rollback
var live_rids: Dictionary = {}

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.

Sorry, only noticing this on second read. You can use netfox's _Set type for tracking a set of keys without (meaningful) values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PhysicsDriver passes wrong tick to _physics_rollback_tick(), and _rollback_space can restore state onto freed bodies

3 participants