Adding online multiplayer to a game is not simply a matter of sending positions from one computer to another. You have to deal with a physical constraint: data takes time to travel, and that time varies. All of so-called netcode exists to hide this delay from the player while keeping the match consistent and hard to tamper with.
Latency, packet loss and jitter
Three quantities describe the quality of a connection:
- latency: the time a message takes to arrive. It is often measured as round-trip time (RTT);
- packet loss: the percentage of messages that never arrive;
- jitter: the variation in latency from one packet to the next.
Even with an excellent connection, geographical distance imposes a minimum delay: signals cannot travel faster than light, and in fibre they travel noticeably slower. Netcode must therefore work well with delays of tens of milliseconds and survive larger ones.
Who decides: the authoritative server
In the most widespread architecture for action games, an authoritative server holds the official state of the match. Clients send only their inputs, such as «I am pressing forward» or «I am firing in this direction», and the server simulates the game and reports the outcome.
| Approach | Advantages | Disadvantages |
|---|---|---|
| Authoritative server | Consistent state, hard to cheat | Needs a server, the client sees things late |
| Peer-to-peer | No dedicated server | Easier to cheat, problems with many players |
| Client-authoritative | Simple, no local delay | The client can claim anything |
The underlying principle is never to trust the client: if the client could say «I am at this position» or «I hit that player», modifying it to cheat would be trivial.
Tick rate and state
The server updates the simulation at a fixed frequency, the tick rate, for example 30 or 60 times per second, and sends snapshots of the state to clients. A higher rate reduces delay and improves precision, but increases server load and required bandwidth. To reduce data, often only the differences from the last state the client acknowledged are sent.
The local delay problem
With a pure authoritative server, when the player presses forward the input goes to the server, the server simulates and sends back the result, and only then does the character move on screen. With a 100 ms round trip, every command has a noticeable delay and the game feels «mushy».
Client-side prediction
The solution is client-side prediction. The client immediately applies the input to its own character, using the same movement logic as the server, without waiting for the response. The player sees an immediate reaction.
Meanwhile the client keeps a list of inputs sent but not yet acknowledged, each with a sequence number.
Reconciliation
When state arrives from the server, it represents the past: it accounts for inputs only up to a certain sequence number. The client performs reconciliation:
- it replaces its own character's state with the authoritative state received;
- it discards inputs already acknowledged;
- it reapplies, in order, the inputs not yet acknowledged.
state = state_from_server
pending_inputs.remove_up_to(last_acknowledged)
for each input in pending_inputs:
state = simulate(state, input)
If the prediction was correct, the result matches what the player was already seeing and nothing is noticeable. If it diverged, for example because another player pushed the character, the position is corrected. To avoid obvious jumps, small corrections are applied gradually.
For all of this to work, the simulation must be as deterministic as possible and identical on client and server: same calculations, same fixed step.
Interpolating other entities
For other players and enemies, prediction is hard, because their future inputs are unknown. The common approach is entity interpolation: the client shows others with a small deliberate delay, for example 100 ms, and interpolates between two received snapshots. Movement looks smooth even though updates arrive at intervals and with jitter.
The price is that what you see of your opponents is slightly in the past. That is why many shooters use lag compensation: when a shot arrives, the server temporarily rewinds other players' positions to the moment the shooter saw on their own screen and checks the hit at that moment. This produces a well-known trade-off: sometimes the person who is hit feels they were already behind cover.
Advice for getting started
- Design the game with multiplayer in mind from the beginning: adding it later almost always means rewriting the logic.
- Keep input, simulation and rendering clearly separated.
- Use a networking library or the engine's features rather than writing the protocol from scratch.
- Test with simulated latency and packet loss, not just on a local network, where everything looks perfect.
In short
- Latency, packet loss and jitter are constraints to manage, not ignore.
- The authoritative server receives inputs and decides the official state, making cheating hard.
- Client-side prediction removes the perceived delay on your own commands.
- Reconciliation corrects divergences by reapplying unacknowledged inputs.
- Interpolation and lag compensation make other players smooth and consistent.