Rollback netcode (GGPO)
The mechanism: predict, play, roll back
Rollback is speculative execution of a deterministic simulation. Both peers know: identical input on an identical frame → bit-identical state. On the wire there are only input bits (see the taxonomy). There is one problem: the opponent's input physically arrives after the one-way delay. Delay netcode solves it head-on — it waits for the opponent's input before executing the frame (input delay, for everyone, always). Rollback does not wait.
The frame cycle
Every frame F the client:
- reads its own input and applies it immediately;
- predicts the opponent's input — usually "repeat the last known one" (in a fighting game a player more often holds something or presses nothing than changes input every frame — the prediction is right on most frames);
- advances the simulation by one frame;
- saves the state of frame
Finto a ring buffer.
When the opponent's real input for an earlier frame F′ arrives over the network:
- it matches the prediction → the guess was right, the simulation is already correct, nothing to do;
- it does not match → rollback: load the saved state of frame
F′, substitute the correct input and resimulate every frameF′…Fwithin the current frame. The player sees a short 1–3 frame "snap".
The rollback window and its cost
How deep you may have to roll back is the number of frames "in flight" while the opponent's input travels to you. With a one-way delay and a frame time :
(RTT 100 ms → one-way ≈ 50 ms ≈ 3 frames at 60 fps.) So every frame the client must be able to save the state and, on a misprediction, recompute up to frames inside a single frame budget:
At =3 that is "run the simulation ~4× per frame" (a rollback of frames plus the current one = +1 steps) — fine, as long as one step is cheap. Hence two hard requirements: (1) the per-frame save state has to be cheap (serializing the entire game state), (2) the simulation step has to be cheap and deterministic. In a fighting game the state is tiny (~10–50 KB: two fighters, projectiles, timers) → saving and rewinding costs pennies. In an RTS the state is megabytes of thousands of units → a save every frame plus resimulation would kill both memory and CPU, which is why RTS take delay (lockstep), not rollback.
Delay + rollback: one knob
Pure rollback predicts the whole window . You can add a little input delay of frames (your own input is applied after rather than immediately) — which shortens the prediction horizon:
A shorter horizon → rarer and shorter rollbacks → fewer visual "snaps", but you add a constant input lag of frames for everyone. It is a spectrum between "delay" ( large, no rollbacks, flat lag — as in lockstep) and "pure rollback" (=0, zero lag on your own input, the most rollbacks). Good implementations use 1–3 frames of delay as a jitter buffer. Compare: SFV became infamous for "eight frames of delay" — that is pure delay, no rollback, and that is why it felt sludgy.
🕹 Games to play — and what to notice
Rollback is best "heard" against delay netcode: one signature is a short jerk (a snap), the other is flat input lag and freezes. Play through these in order and catch the difference.
The first major console fighting game with rollback (Double Helix → Iron Galaxy, GGPO-style, with the author of GGPO involved). It proved rollback works on console in production — and set the fashion for the genre.
🎮 Play: KI on Xbox/PC online at a mid ping — notice that your combos run without a hitch even when the opponent's connection is spiking; what "lags" is not your controls but short twitches in the opponent's picture. That is rollback instead of input lag.
An indie fighting game built directly on GGPO; for years the showcase of how rollback should feel. Small state, clean determinism, excellent online on a "bad" connection.
🎮 Play: Skullgirls online and offline back to back — the difference in response is almost nil (that is the whole point). On a ping spike, catch the opponent's micro-"teleport" of 1–3 frames: that is the resimulation after a misprediction.
All three ship with rollback out of the box (2021–2023). By the early 2020s the community refused to buy fighting games without rollback; that pressure plus open-source GGPO made it an industry standard.
🎮 Play: in GGST/SF6 turn on the connection indicator (bars / the delay frame count) and play at different pings. Notice: as ping grows, the frequency of small snaps grows, but your own response never gets sludgy. Compare with offline — nearly indistinguishable.
The opposite pole. Smash Ultimate is delay netcode (Sakurai: "the side effects are too great" — rollback was rejected; in 2020 the community erupted with the hashtag #FixUltimateOnline). SFV is remembered for "8 frames of delay". The signature is not snaps but floating input lag and freezes whenever the opponent's input does not arrive in time.
🎮 Play: Smash Ultimate online on a less than perfect connection — feel how the controls themselves get sludgy and stutter through the whole match. Then GGST — response is instant, the artifact is different (a snap). And for a counter-example: Melee via Slippi (community rollback bolted onto a 2001 game) plays better than Ultimate's "official" delay.
Deep end · engineering: determinism, save states and the GGPO APIskippable
Rollback rests on bit-exact determinism: identical input on an identical frame must produce bit-identical state on both machines — otherwise the resimulation drifts apart (desync) and there is no such thing as a "confirmed" frame.
What breaks determinism
- Floats. The same code on different CPUs/compilers gives different results (order of operations, FMA, x87 80-bit vs SSE,
fast-math). Almost every rollback engine takes fixed-point (integer arithmetic) for the entire gameplay simulation. - Non-deterministic randomness. A PRNG with a shared seed and a synchronized call order; no "harmless"
rand()in gameplay. - Uninitialized memory / hash iteration order / pointers inside the state. Serializable state has to be flat and reproducible.
The API in three callbacks (the GGPO shape)
GGPO abstracts the network away and asks the game for exactly three things:
save_game_state()— serialize all gameplay state into a buffer (+ a checksum);load_game_state(buf)— restore state from a buffer (for the rollback);advance_frame(inputs)— advance the simulation by one frame with the given input.
From there GGPO predicts input itself, accumulates saves in a ring buffer, and when the real input arrives calls load plus repeated advance. The main debugging tool is the sync test: a mode where the engine rolls back every frame and compares checksums; any source of non-determinism surfaces immediately instead of "three minutes into a match".
How much to keep
A ring buffer of the last +slack states (usually 7–8 frames with room for jitter). The save state has to be fast: in a fighting game it is a memcpy of a compact struct. If you cannot make the state cheap and flat, rollback is not your tool.
Deep end · theory: why "repeat the last input" is a good predictorskippable
The quality of rollback is set by how often and how deep the rollbacks are, and that is set by the accuracy of the predictor of the opponent's input. The naive strategy "on frame the opponent did the same as on " works surprisingly well — here is why.
The statistics of fighting game input
Gameplay input is strongly autocorrelated: the player holds a direction, holds or releases a button, and changes the input only on rare frames (starting an attack, changing direction). If the share of "change frames" is , then over a horizon of frames the probability of at least one misprediction is ≈ . At ≈0.1 and =3 that is ~27% of frames with a rollback — but each rollback is short (≤3 frames) and almost always corrects something small, so visually it is tolerable.
The worst-case bound
Rollback depth is bounded above by the window (you cannot roll back further than the unconfirmed input), so the cost of resimulation is bounded by — the algorithm has a hard ceiling on work per frame. The higher the ping, the larger : both the recompute cost and the length of the "snaps" grow. So rollback does not "fix" an arbitrarily large ping — it makes a small ping indistinguishable from offline, and a large one playable but visibly jerky.
Why rollbacks converge
Because the simulation is deterministic, recomputing with the correct input yields exactly the state the opponent will arrive at later too. Rollbacks do not accumulate error: every "confirmed" frame is shared truth for both peers, and the next prediction is measured from it.
ML / AI: speculative decoding in LLM inference is rollback, literally. A small draft model predicts the next tokens; the large model verifies them in one parallel forward pass; the matching prefix is accepted, and at the first divergence there is a "rollback" to that position and a continuation from there. Predict cheaply → execute speculatively → verify → discard and recompute the tail. The gain is the same as rollback's: hide latency behind a prediction, as long as it is often right and verification/rollback are cheap.
Processors: branch prediction plus speculative execution of the pipeline; a misprediction → a pipeline flush (rolling back the speculative stages) and a restart on the correct branch. Exactly the "predict / execute / verify / roll back" cycle.
Databases: optimistic concurrency control (OCC) and MVCC — a transaction runs on the assumption "there is no conflict" and is validated at commit; a conflict → abort + retry (= rollback). It pays off exactly when conflicts are rare.
Frontend / UX: optimistic UI — draw the result of an action immediately (a like, a send), reconcile with the server, roll back on an error. The same visual "snap" as rollback's.
The principle: if the round trip is your ceiling, and prediction is cheap and usually right, and rollback is cheap — act now and correct yourself. It pays off the more, the rarer the misprediction and the cheaper the rollback.
Why does "repeat the last input" work at all — the player is mashing buttons constantly?
If rollback is so good, why do RTS use delay instead?
memcpy and a recompute of a few frames cost pennies. An RTS is thousands of units, megabytes of state: a save every frame plus resimulating several frames on every misprediction would kill memory and CPU. So RTS put up with input delay (deterministic lockstep — nothing to save, nothing to roll back), while fighting games, where state is tiny and every frame matters, go rollback for zero input lag on their own actions.A rollback "snaps" the picture — why is that considered better than flat input lag?
Determinism is "easier" in rollback than in RTS lockstep — why, if both require it?
Rollback "removes" ping — so at 250 ms it will be perfect?
- GGPO — the sources (
github.com/pond3r/ggpo, Tony Cannon, MIT since 9 Oct 2019) +ggpo.net. The three callbacks and the sync test live there. - Infil, "Fighting Game Netcode" guide — the best popular breakdown of rollback vs delay (with animations).
- Mike Z (Lab Zero) — talks and posts on rollback in Skullgirls on GGPO.
- GGRS (
github.com/gschup/ggrs) — the Rust port; Photon Quantum — a commercial deterministic rollback engine. - Module 4 (
04-online-worlds-1997-2005.md), section "Networking model taxonomy → Rollback".