At the adding multiplayer to a single player game talk at GodotCon 2025:
- Code may asssume all data is available, when it isn’t
- Code might assume that once a client is connected that they are ready to receive game data, when they are not yet ready.
- They decided to fix this by adding connection states to detect when a client is or is not ready to receive data
- They transitioned away from the multiplayer nodes to RPC calls in order to have more control over when data is sent.
- This resulted in them making their own synchronizer node. They still respect the
multiplayer_authority
to decide who is allowed to send data (they can do this because they don’t care about preventing cheating since it is a friendly multiplayer game instead of competitive) - They use the single player save serialization code to initialize the state of all the clients
- Two ways to keep the state synchronized: either constantly update the state or send diffs
- Can use
PackedByteArray
to create smaller packets - Sometimes you need to do bit packing instead of byte packing (like in the case of booleans)
- If game state only changes in deterministic ways, RPC is better than constant state synchronization. However, this requires actions to be done in the same order and with the same seeds for RNG
- They wanted to do LAN, Cross-play and Steam. They decided to write their own peer which handles all three cases
- They don’t bother trying to synchronizing everything perfectly. For example, small differences in visuals or sounds are fine.
- if you write with
MultiplayerPeer
, you can have more flexibility with testing (You won’t need to run Steam to test multiplayer and you can use tools to simulate different network conditions for example). - They use higher-level nodes to deal with packet ordering, to decide if packets should be applied to the game state or not.
- Gafferon Games has a bunch of resources on multiplayer code