Skip to content

Rollback No More#588

Draft
TuysuzTavsan wants to merge 9 commits intofoxssake:mainfrom
TuysuzTavsan:rollback-no-more
Draft

Rollback No More#588
TuysuzTavsan wants to merge 9 commits intofoxssake:mainfrom
TuysuzTavsan:rollback-no-more

Conversation

@TuysuzTavsan
Copy link
Copy Markdown
Contributor

This branch aims to add cs-style net coding and quality of life updates by adding functionality that is not coupled to rollback in general.

To add cs-style net code, we first need to have alternative for rollback in netfox
We will call it "Simulation".
To achieve simulation, we will code 2 seperate nodes.
1- Input-sender (handles input sending to host)
2- Simulator (handles client side prediction and server reconciliation upon receiving truth).

Simulator node will have buffers for their properties, this will be useful later on if we want to add projectile or ray cast checking in the past (just like cs style netcode)
Input-sender nodes can work alone or together with simulation nodes.
For example you could use input-sender to control a vehicle which is coded entirely server-sided.
You can add simulator in scene and select which input-sender to listen locally.

New Concept -> Simulation
Clients will be able to simulate their nodes based on their local inputs.
Whenever server broadcasts truth, clients will reapply their stored inputs from the correction point forward to reach the current predicted state.
For remote clients, local client will interpolate them with tick-interpolator, so no need to change that.
Easier to code than rollback because game state will not be broken. Server runs one authoritative tick per frame using received inputs, then broadcasts the result. It does not predict, rewind, or re simulate.

Key differences
Runs only on clients who own input. (e.g. only for your character)
Simulation doesn't aim to have exact game state for all objects, only for simulated node itself.
Since now input is separated, we have the ability to combine this with state-syncronizer/multiplayer-syncronizer or pure rpcs.
Ability to implement cs style projectile/hit scans later on.

@TuysuzTavsan TuysuzTavsan marked this pull request as draft April 27, 2026 16:23
@TuysuzTavsan
Copy link
Copy Markdown
Contributor Author

  1. Working order of input_sender
  2. Usage of input_sender
  3. Future plans
  4. Naming of Input_sender

1- Working order of input_sender

At the moment input_sender related history and syncing operations are done within NetworkTime to avoid creating a new singleton. NetworkTime records input_sender inputs and syncronize them on its signal before_tick. Input_sender handles his signals and logic by reacting NetworkTime.on_tick. If input_sender node is_multiplayer_authority, it checks if any new inputs received on_tick. If there are new inputs received it will internally apply its input_properties and emits signal new_input_received(tick : int). Server side logic can be coded by using this signal. If there is no input available for current tick, input_sender will apply latest received input_properties and emits input_missing(current_tick : int, latest_known_input_tick : int). Input missing signal is emitted to let users know if they want to code any additional logic to react in certain situations.

Note

Recording / syncronizing input_properties on before_tick and running input_received logic on tick results in glitching when input_sender is used together with TickInterpolator.
We may need to redefine order of it to avoid having issues with existing nodes.

2- Usage of input_sender

Input sender provides new_input_received signal for coding host side logic, but that introduces additional checks to code logic when server is also a player.
Client side logic can be coded with godots built in _input or _unhandled_input virtual functions easily, but providing client side signal for improved workflow may be desired.

3- Future plans

This branch aims to provide:
1- Quality of life features that is not coupled to rollback in general.
2- Simulation nodes to handle client side prediction and server reconciliation
3- Additional logic to handle cs style projectile/ray-cast checking in history.

Because of point number "1", input_sender should be able to work alone or later with simulation nodes.
When simulation nodes arrive order of the logic will be more important because we will most likely want tick_interpolators to co-operate. Else we will have to code tick_interpolator logic within simulation nodes which will result in redundant code.
This is why Working order of input_sender should be defined wisely.

4- Naming of Input_sender

I am happy with the name input_sender since all it does is sending input to host. Naming of networked nodes is very important so feel free to recommend a name for it.

_last_emitted_tick = i

# Helper function to apply given snapshot for only this node.
# TODO Applying whole snapshot and iterating over ticks would be nicer
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.

That could be done with NetworkHistory. You can add the restore() call in NetworkTime.

@@ -0,0 +1,31 @@
@tool
extends Node
class_name Simulator
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.

Let's brainstorm some names before merge - Simulator is too generic imo, it could be simulating many things based on name, not just running netcode.


# Make sure to not send input to ourselves
# Maybe: Only erase ourselves if this is not host, because listen servers could benefit
# TODO does above comment this make sense?
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.

I don't really see this erase() as something costly here, plus at this level we don't have the notion of a listen server

# Get the latest input data available
# Known issue: If input sender is configured with multiple input nodes,
# Any fresh input from one node will trigger re-emitting of other node's inputs?
# TODO: look at above issue.
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.

Good shout, I think there's two cases to this.

One is where all of the nodes are owned by the same peer, but the same update arrives in two different batches. That would mean that an input is larger than the MTU ( usually ~1400 bytes ), which probably means that something's off. Let's assume that inputs are atomic in this sense, will add an extra check as part of #556

The other case is where nodes have different owners. My understanding is that that does not matter, only the Input Sender's authority is checked. Which I agree with!

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.

2 participants