Rollback networking extension for Godot 4 based loosely on the great https://gitlab.com/snopek-games/godot-rollback-netcode
WARNING: This extension is still in development and parts of it are likely to break or panic if not used correctly in undocumented ways. You have been warned
Make sure you have rust installed via https://rustup.rs/
Once installed, build the project with cargo build; cargo build --release
Create a .gdextension file that contains something like the following:
[configuration]
entry_symbol = "gdext_rust_init"
compatibility_minimum = 4.1
reloadable = true
[libraries]
windows.debug.x86_64 = "res://{PATH TO THIS REPO}/target/debug/gdrollback.dll"
windows.release.x86_64 = "res://{PATH TO THIS REPO}/target/release/gdrollback.dll"
This extension is a rollback networking system for Godot 4 which enables responsive peer to peer networking over udp. The core system is the SyncManager which is registered as a singleton autoload when the extension is first added.
The SyncManager has three stages of operation:
- Lobby - where peers connect to a known host and port port forwarding details are gossiped between them. Negotiation for when to start the game is also managed here.
- Play - once scheduled, the game starts moving the
SyncManager into the play state where
networked
nodes are ticked and reloaded based on received input. During play, all inputs, debug state, and other related details are logged to a sqlite database for later replay. - Replay - If instead of connecting and scheduling a start, the user selects a replay file, the SyncManager will collect inputs from the replay and play them back in a predictable manner.
The following is available from the SyncManager in Lobby mode:
Emitted when the game has been scheduled to start after all peers have declared themselves ready.
Emitted when a peer has connected either by direct connection, or by being gossiped from an already connected peer.
Emitted when the game has started. A scene with networked
nodes should be initialized at this point.
Starts listening for connections on the given port. Ideally this would be done on a machine and with a port that has been port forwarded or otherwise exposed in some way.
Note: This is not really a host per se, just an exposed user. Rollback networking via this extension is peer to peer, so no one player has more responsibility or advantage than any other.
Attempts to connect to the given address and port.
Declares that this client is ready to start the game. When
executed, the SyncManager will send a ready signal to every
connected peer. If all peers are ready, the peer with the
smallest GUID will send a message scheduling the start of
the game. When the game start message is received, and the
appropriate amount of time has been waited, the SyncManager
will raise the started
signal and move into the play
state.
If a valid replay file is passed, the SyncManager will load it and move into the replay state which operates very similarly to the play state, but where inputs are read from the replay file instead of received from the network or local machine.
The SyncManager expects a global autoload called
InputManager
with a method networked_input
on it which
returns the input for that frame. Currently this input is
required to have a specific format (check
src/play_stage/input.rs
), but this will likely be relaxed
in a future update.
During play/replay modes, any nodes that are a part of the
networked
are expected to be managed by the SyncManager
via specific methods. The following functions have special
purposes:
Called every frame by the SyncManager and is responsible for updating the state of the node by one tick and should return whatever state is necessary to reload the node back to this frame in the future.
Called by the SyncManager whenever an input from an earlier
frame is received. This is used to reload the state back to
the frame before the received input in preparation for
simulating forward to the current frame again. The state
passed to this function is the same state that was returned
from networked_process
Called by the SyncManager before networked_process
. Useful
for maintaining state at the start of a frame.
Called by the SyncManager at the end of the frame to log a dictionary of state to the replay database for debug purposes. This state is also used by hashing it to verify that a desync has not occurred.
Called by the SyncManager when a node has been despawned. Convenient for cleaning up any resources and resetting the node to a default state for reuse.
Called by the SyncManager when a node is spawned. The state
passed is an argument that was passed to the SyncManager's
spawn
method and is used to initialize the node
repeatably.
The following functions are available when the SyncManager is in play/replay state:
Returns the GUID of the local machine.
Returns an array of GUIDs for all connected peers.
Returns an array of all peer GUIDs including the local machine.
Returns a boolean indicating if this machine was elected leader.
Returns the input for the given peer (or local machine) for the currently simulated frame.
Returns the average advantage for this machine over all connected peers in terms of frames. This value is minimized automatically by dropping frames when it is determined that a given peer is significantly ahead.
Despawns the given node. This is necessary to ensure that nodes are despawned correctly across rollbacks.
Spawns a new node of the given scene under the given parent
with the given name. Data is passed to networked_spawn
.
This method is necessary to ensure that nodes are despawned
and spawned correctly across rollbacks.
Logs an event to the replay database. Useful for debugging purposes.