Guide
Game rollback netcode systems explained
Harbor Brawl's closed online beta shipped with delay-based netcode: every input waited eight frames before the simulation advanced so both clients could receive packets in time. On a 60 fps cadence that is 133 ms of baked-in lag before your button press registers — tolerable on LAN, brutal across the Atlantic. EU players reported matches feeling “underwater”; session telemetry showed 34% of cross-region sets ending in disconnect before round three. Local rollback tests on the same build felt crisp at two frames of delay because the engine could rewind and replay when late packets arrived instead of freezing the whole match.
The refactor adopted rollback netcode in the GGPO tradition: each frame stores a compact savestate, remote inputs are predicted when missing, and when the real input shows up the simulation rolls back to the divergence frame and resimulates forward. Input delay dropped to two frames; rollback depth capped at eight frames with visible correction only on prediction misses. Cross-region disconnect rate fell from 34% to 6%; average player-reported input lag dropped from 7.2 to 2.1 on a 1–10 scale. This guide explains rollback mechanics, savestate design, prediction and correction UX, desync prevention, the Harbor Brawl refactor, a technique decision table versus client-server lag compensation, pitfalls, and a production checklist.
What rollback netcode is
Rollback netcode is a peer-synchronized simulation model where both clients advance the same deterministic game state every frame, but each side may temporarily guess the opponent's input when network latency hides it. When the guess was wrong, the engine restores an earlier savestate and replays frames with the correct inputs until it catches up to the present. Players feel local responsiveness because their own inputs apply immediately; corrections happen on mispredicted remote actions only.
Rollback is the standard answer for frame-perfect genres — fighting games, platform fighters, and some sports titles — where frame advantage and one-frame links are design currency. It contrasts with delay-based netcode (add fixed input lag for everyone) and with lag compensation on authoritative shooters (rewind hit detection, not full state rollback). The general netcode overview covers when each model fits; this guide goes deep on rollback implementation.
GGPO (Good Game Peace Out) popularized the pattern in the early 2000s: broadcast local inputs, predict remote inputs, rollback on mismatch, and optionally add a small static input delay to reduce rollback frequency. Modern engines (Godot rollback addons, custom C++ cores, Unity/Godot fighting templates) follow the same loop.
The rollback loop: savestates, predict, resimulate
Each simulation frame follows a fixed pipeline:
- Collect local input for frame n and send it to peers.
- Receive remote input for frame n (or mark as missing).
- Save state before advancing — a snapshot of positions, velocities, timers, RNG seeds, and combat FSM phase.
- Advance simulation using local input plus remote input (or predicted remote input if the packet has not arrived).
- On late remote input for frame k < n: load savestate at k, inject true input, resimulate frames k…n, then continue.
The cost of rollback is CPU time proportional to
rollback_depth × simulation_cost_per_frame. Fighting games keep
state small — two characters, a handful of projectiles, integer positions
in fixed-point math — so eight-frame rollbacks at 60 Hz stay under a
millisecond on modern hardware. Harbor Brawl budgets 1.2 ms per frame for
simulation including worst-case eight-frame rollback; profiling showed median
0.18 ms and p99 0.94 ms.
Savestate design
Savestates must capture everything that affects future outcomes:
- Character positions, velocities, facing, animation phase, and combat FSM state (idle, startup, active, recovery, hitstun, blockstun).
- Projectile and hitbox lifetimes, including clash resolution counters.
- Timer registers: super meter, cooldowns, juggle decay, round clock.
- RNG state if any move uses randomness — must be deterministic and included in snapshots.
- Input history ring buffer pointers (which inputs were confirmed vs predicted).
Do not snapshot rendering-only data (particles, camera shake, UI tweens). Visual layers should read simulation state after resimulation completes and interpolate for display. Mixing render state into savestates causes flicker and desync when VFX diverge from logic.
Input delay as a tuning knob
Adding static input delay (often 1–3 frames) gives remote packets time to arrive before you need to predict. More delay means fewer rollbacks but higher baseline lag. Harbor Brawl settled on two frames: enough to cut rollback events per round from 41 to 12 on 80 ms RTT links, while keeping local responsiveness acceptable. Tune delay per game mode — ranked may use two frames; casual cross-region may use three.
Input buffering on the local side stacks with netcode delay: a 5-frame parry buffer plus 2-frame network delay means the player experiences 7 frames from press to parry active. Document total latency in training modes so competitive players can calibrate.
Prediction, correction, and player-facing UX
Remote input prediction defaults to repeat last input for fighters: if the opponent was holding back on frame n−1, assume back on frame n. More sophisticated predictors exist (neutral on knockdown, block during blockstun) but add bug surface. Harbor Brawl uses repeat-last with a neutral override when the remote character is in hard knockdown or super freeze.
When prediction fails, the simulation corrects. Players see one of three UX patterns:
- Invisible correction — small position snaps during blockstrings; acceptable if rollback depth stays under 3 frames on good connections.
- Hit rollback — a hit that looked like it connected is undone; frustrating but honest. Cap rollback depth so full combos do not unravel.
- Rollback smoothing — interpolate visual sprites between wrong and corrected poses over 1–2 display frames. Hides micro-corrections; can mislead on exactly which frame was plus on block.
Harbor Brawl shows a subtle screen-border flash when rollback depth exceeds four frames so players understand lag spikes without reading debug overlays. Training mode exposes rollback count, max depth, and prediction accuracy per round for netcode QA.
Determinism requirements
Rollback only works if both clients produce identical simulation results from identical inputs. Common determinism breakers:
- Floating-point math across platforms — use fixed-point
or lock IEEE-754 behavior; avoid
sin()without identical libraries. - Frame-order dependency — iterating hash maps in undefined order when multiple hits resolve same frame.
- Time-based logic tied to wall clock instead of frame index.
- Physics engines with non-deterministic solvers — fighting games usually custom-author collision, not full rigid-body stacks.
Pair rollback with the fixed timestep pattern: simulation always steps at 60 Hz (or 120 for some rollback fighters) regardless of render FPS. Variable render delta must not leak into combat math.
Run desync detection every frame: hash a checksum of critical state and exchange with peers. On mismatch, dump inputs and savestates to disk, halt the match, and offer rematch. Harbor Brawl uses a 32-bit FNV hash of character state + projectile list; desync rate in beta dropped from 0.8% to 0.04% after fixing a float knockback bug on ARM Chromebooks.
Harbor Brawl online refactor
The beta's delay-based stack added input_delay = ceil(rtt / 2) + 2
capped at 8 frames. Cross-region RTT of 140 ms forced eight-frame delay for
everyone in the match — even the low-latency player paid for the worst link.
The rollback refactor changed:
- Fixed two-frame input delay for all ranked matches regardless of RTT; casual uses adaptive 2–4.
- Eight-frame rollback cap — beyond eight, the match pauses briefly (GGPO-style “timesync”) rather than simulating deeper history.
- Compact savestates at 1.4 KB per frame, ring buffer of 16 frames (128 KB total per peer).
- UDP input channel with redundant packet sends on frames 0, 2, 4 after each input to survive 2–3% packet loss without rollback storms.
- Spectator delay of 10 frames on relayed streams so observers never see correction flicker.
Results after four weeks of ranked season: disconnect rate 34% to 6%, rematch rate +18%, average match length unchanged (no defensive meta shift). EU–NA sets became viable where delay-based had been abandoned.
Technique decision table
| Netcode model | Use when | Skip when |
|---|---|---|
| Rollback (GGPO-style) | 1v1 or small-player fighters, platform fighters, frame-critical sports | 16+ player shooters, persistent MMO worlds, heavy physics sandboxes |
| Delay-based | Lan-only party games, turn-based, prototypes with zero rollback budget | Competitive cross-region fighters where 5+ frames of delay kills the meta |
| Client-server + lag comp | FPS/TPS hitscan, authoritative anti-cheat requirements | Peer-to-peer fighting games needing zero server simulation cost |
| Rollback + static delay (2–3f) | Production fighters balancing rollback frequency and feel | Games where any added delay breaks muscle-memory links at 1f precision |
| Timesync pause | Spike RTT beyond rollback cap; prevent runaway resimulation | Casual mobile titles where visible pause reads as broken |
| Input spectator delay | Streamed tournaments and relay spectators | Local same-screen play |
Default for new fighting-game online modes: rollback first, two-frame delay, eight-frame cap, deterministic fixed-point combat core. Add dedicated relay servers for NAT traversal but keep simulation peer-to-peer unless cheat risk demands server authority.
Common pitfalls
- Non-deterministic RNG — using unseeded
Math.random()per client; seed from match ID and frame index. - Saving too much — full-scene snapshots blow CPU and RAM; diff only simulation-critical fields.
- Unbounded rollback — 20-frame rewinds unravel combos and spike CPU; cap and timesync instead.
- Prediction complexity — clever predictors that disagree with real human mixups cause visible snap-back every neutral scramble.
- Ignoring input buffers — local buffer + net delay stacks; tune offline buffers separately for online.
- Render interpolation desync — interpolating sprites between uncorrected poses while logic already rolled back.
- No desync telemetry — shipping without checksum exchange means rare ARM/x86 float bugs surface only in production tournaments.
- Rollback-unfriendly moves — full-screen cinematics that freeze simulation for 90 frames multiply resimulation cost; gate supers behind short logic freezes with cached outcomes.
Production checklist
- Prove deterministic simulation with lockstep input replay tests on all ship targets.
- Implement per-frame savestates; profile worst-case rollback depth at target FPS.
- Send inputs over UDP with redundancy; measure packet loss per region.
- Set static input delay (start at 2 frames); A/B rollback event rate vs player surveys.
- Cap rollback depth; implement timesync pause beyond cap.
- Exchange state checksums every frame; auto-dump inputs on desync.
- Separate simulation from render; never snapshot VFX-only state.
- Expose rollback stats in training mode for QA and player trust.
- Document total input latency (buffer + net delay) in frame data overlays.
- Load-test cross-region RTT matrix before season launch; set matchmaking RTT caps if needed.
Key takeaways
- Rollback netcode predicts remote inputs and resimulates when guesses fail — local inputs stay immediate.
- Savestates must be compact, deterministic, and free of render-only data.
- Static input delay trades a few frames of lag for fewer visible corrections.
- Harbor Brawl cut cross-region disconnects 34% to 6% by replacing delay-based netcode with capped rollback.
- Checksum every frame — desync bugs from float math are easier to prevent than to debug live.
Related reading
- Game netcode and multiplayer networking explained — prediction, lag compensation, and architecture overview
- Game frame data explained — startup, active, recovery, and frame advantage
- Game input buffering explained — pre-input queues and rollback determinism
- Game loop and frame timing explained — fixed timestep and delta-time discipline