Netcode taxonomy: three models
The mechanism: what we put on the wire
The whole taxonomy grows out of one decision. You can send world state over the network ("player A at point X with 80 HP") — then the client needs no determinism, it just draws what it was sent, but the volume grows with the number of entities, and somebody has to be the authority, otherwise a client will lie. Or you send input/commands ("pressed forward", "units 5–40 — attack here") and replay them through an identical simulation on every machine — then traffic is tiny, but any divergence in the math wrecks everything (desync).
The cost of bandwidth
A rough traffic model per player. Broadcasting state, you pay for every visible entity in every snapshot:
With lockstep/rollback you pay only for commands or input bits, and that does not depend on the number of units:
This is exactly the thesis of Bettner and Terrano's "1500 Archers on a 28.8" (GDC 2001, Ensemble, Age of Empires): trying to send unit positions capped an RTS at ~250 objects on a 28.8k modem; broadcasting commands on top of a deterministic simulation lifted the ceiling past 1500 — on the wire it is the same handful of commands per turn, even with a thousand archers on screen.
The cost of latency
Hiding ping gives the three models different perceived input latency (how long before you see a reaction to your button). A frame at 60 fps = 16.67 ms; say RTT = 100 ms (one-way ≈ 50 ms ≈ 3 frames).
Lockstep defers your own input by the input delay — enough frames for the command to reach the peer before the shared frame is executed:
That is ~50 ms of lag — and both players feel it, always. Rollback and client prediction give a perceived latency of ≈ 0 (your own input is applied on this very frame), and pay the price elsewhere: by predicting the other side plus rolling back (rollback), or by reconciling with the server (client-server). Naive authority without prediction is the whole RTT, 100 ms — "control by mail".
The three models side by side
| Property | Client-server + lag comp | Lockstep (determ.) | Rollback |
|---|---|---|---|
| On the wire | world state (deltas) | commands | input bits |
| Authority | dedicated server | none (all equal) | none (P2P, 2 players) |
| Determinism | not required | required, strict | required |
| Traffic | ∝ number of entities | tiny | tiny |
| Hiding ping | prediction + reconcile + interp. | input delay | input prediction + rollback |
| Artifact | rubber-banding, "death around the corner" | everyone waits for the slowest | a visual "snap" |
| Players | from 2 to thousands | 2–8 (masses of units) | usually 2 |
| Genre | MMO, BR, shooters | RTS, auto-battlers | fighting games |
The numbers at 100 ms RTT / 60 fps: naive authority — 100 ms of lag; lockstep — 50 ms, but for everyone; client prediction and rollback — ~0 ms of lag, but the first catches a rubber-band whenever it diverges from the server, and the second a 1–3 frame "snap" on a misprediction. Each model gets its own lesson from here: client prediction (ready), rollback in detail.
🕹 Games to play — and what to notice
You cannot feel the three models in one game — each has its own carrier genre. Play three different ones and catch the latency signature of each: what exactly "lags" when the network is bad.
The dedicated server is truth, you predict your own movement and shooting, you see other people slightly in the past (interpolation), and the server rewinds time to resolve hits. The signature of a bad connection is rubber-banding (you get yanked back when the prediction diverges) and "I was already around the corner when I died" (favor-the-shooter).
🎮 Play: in CS type net_graph 3 and play on a high ping. Your own movement rubber-bands and other players teleport — but the world does not freeze as a whole. That is state-on-the-wire with prediction.
Only commands go over the wire, and every machine runs the simulation in sync. So hundreds of units cost nothing in traffic, but your click executes after the input delay (turn N+k), and when somebody lags the whole game freezes, not one unit.
🎮 Play: old StarCraft/AoE on a bad connection — notice that the entire world lags at once (we are waiting for everyone), while units move smoothly and identically on every screen. That is commands-on-the-wire plus determinism. In Korea's PC bangs it went unnoticed: ping was low.
P2P, 2 players, input bits on the wire. Your own input is instant; the opponent's input is predicted ("they are holding the same thing as last frame"), and on a mistake there is a rollback and a resimulation of a few frames. The signature of a bad connection is a short "teleport snap" of the character, but never input lag.
🎮 Play: in GGST/KI online, catch the micro-"jitter" of the opponent on a ping spike — that is a visible rollback plus resimulation. Compare it with delay netcode (early Smash Ultimate / old SFV): there you get flat input lag on everything instead of a snap. Deeper in the rollback lesson.
The same client-server family, pushed to its limit: a single server (Tranquility), and under overload the server slows time down instead of lagging (Time Dilation) — everything runs at 10–30% speed, but consistently. Authority is preserved at the cost of pace.
🎮 Watch: log into EVE during a major battle (or watch B-R5RB 2014 footage). The TiDi indicator will read "10%" — the world runs in slow motion but does not fall apart. That is choosing "consistency over pace". In detail in "Persistence and sharding".
Deep end · engineering: determinism, floats, and why lockstep is scarier than rollbackskippable
Determinism is the requirement that "identical input → bit-identical result on every machine". Enemy number one is floating point: the same code on different CPUs/compilers gives different results (order of operations, FMA, x87 80-bit vs SSE 64-bit, fast-math). So deterministic engines go fixed-point (integer arithmetic) or pin their floats down hard.
Why lockstep is the harshest
- Divergence is fatal and silent. In client-server the server will correct the client anyway; in lockstep one divergence in one unit out of thousands → desync, and from there the worlds drift apart irreversibly. You need bit-exact determinism across the whole simulation.
- Deterministic randomness. A PRNG with a shared seed and a synchronized call order. Any "harmless"
rand()outside the synced simulation (particles, sound) must not affect gameplay. - Command ordering. Every player's commands for frame N are sorted deterministically (by player id), otherwise A→B and B→A give different results.
- Checksums. Engines periodically send a hash of the state; if it diverges you catch the desync immediately, not ten minutes later.
Rollback vs lockstep — the shared DNA
Both are deterministic and both send input, but lockstep waits for the input before executing (input delay), while rollback predicts and executes right away, rolling back on a mistake. Rollback = "optimistic lockstep". The price of rollback is a state save every frame (for the rollback); which is why it lives where state is tiny (a fighting game, ~10–50 KB) and is almost inapplicable to an RTS, where state is enormous. See the separate lesson.
Deep end · hosting: dedicated vs P2P, NAT, and where the math happensskippable
- Who hosts. The client-server of MMOs and shooters requires dedicated servers: anti-cheat, no "host advantage", stability. Lockstep and rollback are usually P2P — there is no authoritative state, so there is nothing to host centrally; cheap, but it falls over when the "host"/peer disconnects.
- NAT and matchmaking. P2P runs into NAT traversal (STUN/TURN, hole punching); some connections end up going through a relay anyway. Which is why even "P2P" fighting games keep relay servers running.
- Authority ≠ topology. You can do P2P with one authoritative peer (a listen server, co-op like Helldivers/Deep Rock): cheap, but the host sees the world with no ping and is harder to protect against cheating.
- Regional servers. You cannot cheat the physics of ping:
RTT ≥ 2·distance/c. All three models benefit from a nearby data center — matchmaking pins you to a region to lower the base RTT that all the hiding works on top of. - Hybrids. Modern co-op games take client-server with weakened authority (fewer players, a social context, tolerance for lag) — a compromise between the cost of dedicated servers and the protection P2P lacks.
Distributed systems: lockstep = deterministic state-machine replication (Raft/Paxos: we replicate a log of commands, not snapshots; every replica applies the same commands in the same order → the same state). Client-server with snapshots = primary-replica with state shipping. "Send commands, demand determinism" vs "send state, determinism not needed" is exactly this dichotomy.
ML / AI: distributed training is the same fork in the road. Data-parallel = "send state": every worker computes gradients and all-reduce synchronizes weights/gradients (heavy traffic, but the workers are independent). Deterministic reconstruction from a seed = "send commands": store the seed plus the data order and reproduce the epoch bit-for-bit instead of storing checkpoints — cheap on disk, but it demands determinism (like lockstep). And off-policy actors (IMPALA), acting on a stale policy with a later correction, are the "optimistic" path — a relative of rollback.
Backend / databases: replication via an operation log (event sourcing, WAL shipping) vs via a snapshot (snapshot replication) — the "commands or state" choice. Determinism of the replica = reproducibility of the log.
The principle: decide what is cheaper to move — state (flexible, expensive, no determinism needed) or commands (cheap, but everything rests on bit-exact reproducibility). And decide separately who holds authority.
MultiplayerSynchronizer) and any rollback plugin (GodotSteam/SnapNet, or GGRS in Rust). In the client-server one, turn on drawing of both the server and the predicted position; in the rollback one, the rollback frames counter and the predicted input. Dial in artificial lag and compare the behavior.net_fakelag in Source), input delay and "the whole game waits" (a lockstep RTS on a bad connection), a visual "snap" (a rollback fighting game). Checklist: one artifact = one model, do not mix them up.Why not just send state always — it does not require determinism, after all?
If rollback = "optimistic lockstep", why do RTS not use rollback while fighting games do?
"Everyone waits for the slowest" in lockstep — can you get around it?
Why is determinism "scarier" in lockstep than in rollback, if both require it?
rand()) → a desync of the whole game with no chance of correction (no authority to fix it). Which is why RTS engines are obsessed with fixed-point, deterministic PRNGs, command ordering and state checksums.Four-player co-op (Helldivers, Deep Rock) — which model is that?
- Paul Bettner & Mark Terrano, "1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond" (GDC 2001) — the canonical breakdown of lockstep.
- Glenn Fiedler (Gaffer on Games), "What Every Programmer Needs to Know About Game Networking" — an overview of all three families.
- Yahn Bernier, "Latency Compensating Methods…" (Valve, 2001) — client-server lag comp.
- GGPO (Tony Cannon) + the GDC talks — rollback; covered in the next lesson.
- Module 4 (
04-online-worlds-1997-2005.md), section "Networking model taxonomy".