Skip to content

refactor: Server pattern#549

Draft
elementbound wants to merge 76 commits intomainfrom
exp/server-pattern
Draft

refactor: Server pattern#549
elementbound wants to merge 76 commits intomainfrom
exp/server-pattern

Conversation

@elementbound
Copy link
Contributor

@elementbound elementbound commented Jan 6, 2026

This is an experimental PR, that refactors much of netfox's internals, inspired by Godot's server pattern.

While Godot had its own requirements, the interpretation here is that:

  • Servers are singletons that provide access to a group of related functionalities
  • Servers act as an interface, similar to a facade, as an outer boundary to internal details
  • Nodes act as convenient wrappers to servers, meaning they expose some config and then do some server calls based on said config

Hopefully this allows:

  • Simplified history tracking
  • Greatly simplified RollbackSynchronizer, StateSynchronizer, and PredictiveSynchronizer
  • Easier testing
  • Batching - sending state in one RPC per tick, instead of one RPC per RBS per tick
  • Nested RBS's
  • An easier fix for RollbackSynchronizer removes mutated states on rollback #383
  • Buffering incoming data in case the target node doesn't exist yet, instead of printing errors
  • Easier implementation of Rollback liveness #468

Also lays the foundations for some potentially wild features like:

  • RPCs on objects
  • Rollback on objects

TODO:

  • Diff states
  • Redundant inputs
  • Binary state serialization
  • Binary input serialization
  • Support schemas
  • Support visibility filtering
  • Networked IDs
  • Command bus
  • StateSynchronizer
  • PredictiveSynchronizer
  • Update metrics
  • Toggleable input broadcast
  • Mutations?
  • Separate into multiple PRs

Pending fixes:

  • Input prediction
  • Diff states

Sanity tasks:

  • Implement tests
  • Implement / postpone TODOs
  • Test with large states, e.g. larger than the MTU of 1500 bytes
    • Large state ( 2k ) doesn't arrive consistently using noray + relay
    • Checking the same scene with 1.35.3 works well
  • Test physics stepping
  • Test display offset and input delay
  • Test Track and expose latest confirmed tick #470
  • Test all examples

Introduces #556

@Archists-dev
Copy link

Great stuff!!

func record_state(tick: int) -> void:
var input_snapshot := get_rollback_input_snapshot(tick - 1)
_record(tick, _rb_state_snapshots, _rb_state_properties, false, func(subject: Node):
if not subject.is_multiplayer_authority():
Copy link
Contributor

Choose a reason for hiding this comment

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

Server always has authority to broadcast states

Suggested change
if not subject.is_multiplayer_authority():
if subject.multiplayer.is_server():
return true
if not subject.is_multiplayer_authority():

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Might have to investigate why is_predicting() was returning true in this case. Otherwise we risk overwriting a client's prediction with ticks that the server had no input for.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without the suggested change, RollbackSimulationServer.is_predicting() would return true for the other player's car on the server, because of this clause:

	if is_owned and not has_input:
		# We own the node, node depends on input, we don't have data for input - predict
		return true

So as long as we haven't received input from the client, we consider the recorded simulation predicted, which - in theory - makes sense. I guess next thing to find out is why we need to forego the above.

On clients, is_predicting() returns false for the player's car with this clause:

	if not is_owned and has_input:
		# We don't own the node, but we own input for it - not (input) predicting
		return false

@elementbound elementbound mentioned this pull request Feb 4, 2026
15 tasks
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.

3 participants