← Module 4/Rollback netcode
RU
Module 4 · Online worlds (1997–2005)

Rollback netcode (GGPO)

A fighting game needs a response measured in single frames — an authoritative server cannot deliver that. Rollback solves it differently: predict the opponent's input, play immediately, and if you were wrong, roll back and resimulate. Your own attack is always instant; the price is determinism, a state save every frame and a short visual "snap".
~18 min🏠 lab in the map lesson
The gist in 30 seconds
In a fighting game the latency budget is a handful of frames (~6 frames = 100 ms at 60 fps), and input lag destroys reads. Authoritative client-server cannot do that (it adds the whole RTT). Rollback (the "input on the wire" model plus determinism): both clients run an identical simulation; every frame you apply your own input immediately and predict the opponent's input ("they are holding the same thing as last frame"), saving the state of every frame. When the opponent's real input arrives (with the network delay) and matches the prediction — nothing to do. If it does not match — roll back to the last confirmed frame, replay it with the correct input and resimulate several frames within one — the player sees a short jerk. Your own controls never lag. The reference implementation is GGPO (Tony Cannon, 2006; open-source MIT since 2019). The price: strict determinism (fixed-point) and a cheap save state — which is why rollback lives in fighting games (state ~10–50 KB) and not in RTS.

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:

  1. reads its own input and applies it immediately;
  2. 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);
  3. advances the simulation by one frame;
  4. saves the state of frame F into a ring buffer.

When the opponent's real input for an earlier frame F′ arrives over the network:

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 towd and a frame time Δt:

R= ⌈towdΔt⌉ = ⌈50/16.67⌉ =3 frames

(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 R frames inside a single frame budget:

Cresim ≈ R·Cstep ≤ Δt

At R=3 that is "run the simulation ~4× per frame" (a rollback of R frames plus the current one = R+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.

Frame 103: the opponent's real input for frame 100 arrived frames: 99 100 101 102 103 ✓ confirmed ← predicted roll back to frame 100 recompute: 100 101 102 103 correct input · 4 frames in one Your own input ran without delay the whole time. The "snap" = the difference between yellow and green.

Delay + rollback: one knob

Pure rollback predicts the whole window R. You can add a little input delay of d frames (your own input is applied after d rather than immediately) — which shortens the prediction horizon:

Rpred = max(0,R−d)

A shorter horizon → rarer and shorter rollbacks → fewer visual "snaps", but you add a constant input lag of d frames for everyone. It is a spectrum between "delay" (d large, no rollbacks, flat lag — as in lockstep) and "pure rollback" (d=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.

Killer Instinct (2013) the first AAA rollback

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.

Skullgirls GGPO · the indie reference

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.

Guilty Gear Strive / Street Fighter 6 / Mortal Kombat 1 the modern standard

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.

Smash Ultimate · early SFV contrast: delay netcode

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 R+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 F the opponent did the same as on F−1" 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 p, then over a horizon of R frames the probability of at least one misprediction is ≈ 1−(1−p)R. At p≈0.1 and R=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 R (you cannot roll back further than the unconfirmed input), so the cost of resimulation is bounded by R·Cstep — the algorithm has a hard ceiling on work per frame. The higher the ping, the larger R: 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.

Analogy
Two pianists playing a duet over a laggy line. Delay netcode — each waits until they hear their partner's note and only then plays their own: the duet is even, but both are permanently behind. Rollback — each guesses the partner's next note ("probably the same as now") and plays their own immediately; when the real note arrives and matches — great; if it does not — quickly "replay" the last bar with the right note. You always hear your own part with no delay; occasionally your partner gets a short "rewind".
Why it matters
Rollback is the idea "do not wait for the round trip, act speculatively and correct yourself" taken to its limit. It defined what a good networked fighting game even is, and it became an industry standard not from the top down but under player pressure. Understanding it means seeing the general move: when network latency sits inside the control loop, and rollback is cheap and mispredictions are rare, optimism beats waiting. And the converse: where state is enormous or the simulation is non-deterministic, the same move turns into poison — which is exactly what separates "works in a fighting game" from "will never fly in an RTS/MMO".
🔁 Beyond games — where this transfers
Rollback = optimistic speculative execution with a rollback on misprediction. It is one of the most transferable patterns in systems.

ML / AI: speculative decoding in LLM inference is rollback, literally. A small draft model predicts the next k 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.

Connections
foundation
Netcode taxonomy — the map of the three models; rollback = "input on the wire" plus determinism. This lesson is the third model in depth.
foundation
The game loop and fixed timestep — a deterministic fixed simulation step is a precondition for rollback (otherwise the recompute drifts apart).
contrast
Client prediction — also "predict and correct", but with an authoritative server and state on the wire; here it is P2P, determinism and resimulation on your own machine.
Questions worth asking
Why does "repeat the last input" work at all — the player is mashing buttons constantly?
Because gameplay input is strongly autocorrelated: on the overwhelming majority of frames the player holds a direction/button or presses nothing, and changes the input only on rare frames (starting an attack, switching sides). Over a short 2–4 frame horizon "the same as last frame" matches most of the time. And when it does not, the rollback window is small, the recompute is cheap and the snap is short. The predictor does not have to be smart; it has to be cheap and often right over a short horizon.
If rollback is so good, why do RTS use delay instead?
Because of the state you have to save every frame for a possible rollback. A fighting game is two characters, ~10–50 KB: a 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?
Because a fighting game is decided by reads and reaction: you look at the opponent and answer within a few frames. Constant input lag shifts your "see → press → see" loop and breaks your timings (get used to online, and you whiff offline). A short visual snap of the opponent does not touch that loop: your controls are instant, and only the prediction of the other side is wrong, briefly. For a genre where every frame of your own response counts, a jerk in someone else's picture is the lesser evil compared with sludgy controls.
Determinism is "easier" in rollback than in RTS lockstep — why, if both require it?
The volume of the simulation and the cost of an error. In rollback there are two players and a tiny state — determinism is easier to test (sync test: roll back every frame and compare checksums) and to hold, and a misprediction gets corrected by a rollback anyway. In RTS lockstep there are thousands of interacting units, and any divergence in one of them → a desync of the whole game with no chance of correction (no authority). Both use fixed-point and a deterministic PRNG, but the bug surface and the consequences in an RTS are an order of magnitude worse.
Rollback "removes" ping — so at 250 ms it will be perfect?
No. Rollback makes a small ping indistinguishable from offline and a medium one playable, but it is not free at large latencies. As ping grows, the window R grows: rollbacks get more frequent and deeper → longer visual snaps (the opponent "teleports" across more frames) and a higher resimulation cost per frame. There is a hard ceiling: recomputing R frames has to fit inside the frame budget. On top of that, 1–3 frames of input delay are usually added as a jitter buffer. So even perfect rollback at 250 ms feels noticeably jerkier than at 30 ms — it is just still better than delay at the same ping.
Further reading