A discussion came up on the Discord channel about how to manage state in multiplayer games. I described how it was solved “back in the day” during my time developing ArmyMen RTS.
Yes, its old (2002) but the multiplayer Peer2Peer networking methods we used were a moving window state buffer. I’ll describe how it works, and over the coming weeks I’ll add a little Defold sample people can use.
State Machines
To be able to properly “know” what a game is doing at any point in time it is good to use a state machine. This is a broad term and can come in many implementations, but fundamentally a state machine allows developers to know what state the game is in at any specific time or step.
We will ignore the machine part of this discussion, and simplify to talk about pure state within a game frame.
An example is collecting the input value for all possible key presses and record any changed state. This might be encoded into a bitstream or bytestream ie:
First byte: Id of the event that caused it
Second byte: State info for the event - 1 bit for key up/down, 1 bit for shift held
and a series of these for all the events that occurred during a single frame.
Additionally this “state” would be usually preceded with a header with properties like timestamp, frame id, and others.
Together this all represents the “state” of input over the frame.
State Buffer Management
The states are sent over the network and other peers collate them. They put them into a buffer.
This is usually a time ordered or frame ordered buffer.
The buffer is often large enough to have a second (in time) or more of updates within it.
The game with the incoming states applies them to the game if the game is at the same timestep and the actions the player applies will look correct.
A little trick with this, is all game states run a little “behind”. So that the states that the player sees can often be slightly behind what the latest state that is in the buffer. The reason for this is to cater for slower network connections allowing states in the buffer to not be too far infront of faster players.
To ensure state is consistent, one of the peers is usually nominated the “master”. The master does validation checking on the states. Usually some form of encryption or crc hashing is used to align the masters own gamestate with the state buffer. If a specific state comes in, and it doesnt match for a specific timestep, then the master can drop that peer from the game.
Also, we had peer promotion. Which allows the master to leave the game, and another peer will be promoted to master and start validating the state buffer.
Implementation
So far I have really only been talking about simple state - like keyboard input. For more complex state and state machines it is best to use a FSM - Finite State Machine (there are others, but I like FSMs ).
Im not going into detail about FSM’s here, there is alot on google - although very few good articles or Id link them :). I have some samples already with FSM’s in them (Cairo and a couple of others). I’ll make a specific example for this thread.
Put simply, a finite state machine allows you to record state of a property and easily change it via events or methods. A hierarchical one, can contain an entire gamestate for interactive objects which can then be serialized into a bitstream or bytestream and you have a “known state” for a game in a frame.
That is the general description of how you can use state machines and be able to share state between peers in a P2P networked game. This is quite an old technique games like Quake and earlier used similar mechanisms along with prediction methods to minimize the amount of data needed to be sent.
I’ll expand on this over the coming weeks. If you have questions, ideas or would like to see specific examples let me know and I’ll try to integrate it into the example.
Cheers,
Dave