Skip to content

Latest commit

 

History

18 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Backstep

A backstep is the move you make to undo a commitment you have already made. So is a rollback.

An answer to a question that reading about it never settled: what is rollback netcode actually doing, and what does it cost?

Every fighting-game forum will tell you rollback "guesses your opponent's input and rewinds when it is wrong". That sentence is true and explains nothing. How often is the guess right? How deep does the rewind go? What happens when your opponent is on another continent? Those have numbers, and the only way to get them was to build the thing and measure it.

So: a Rust workspace that implements rollback netcode at two levels.

  1. A deterministic, fully instrumented 2D arena. Every byte of state is auditable, the snapshot is 204 bytes, and the checksum covers all of it.
  2. The Last Blade 2, a 1998 Neo Geo fighting game, running on the official FBNeo emulator core, driven by exactly the same rollback engine.

The second one is the point. If the engine needed to understand the game, it could not drive an arcade emulator whose state is a 415 KB opaque blob. It does not, so it can.

A person plays P1 through SDL2. A headless EC2 instance plays P2 through a scripted state machine. The peers exchange only inputs, over UDP authenticated with HMAC-SHA256, while Prometheus, Grafana, JSONL logs and an HTML report record every prediction, correction and disagreement.

New to any of this? 00 - Glossary explains every term from scratch, and 17 - How emulation works explains what it means to run a 1998 arcade board in software.

A rollback session, delay20 profile

The Last Blade 2 under rollback. The band across the top is the session's own telemetry, burned in from its log: 142 rollbacks so far, 18.5% of a frame's work being re-simulated, and ROLLBACK -5 firing on the frame it happened. The game underneath does not stutter.

Results

Both simulations ran between a desktop in Madrid and EC2 instances in Frankfurt, São Paulo and Tokyo, over the public internet.

Arena The Last Blade 2
Snapshot size 204 bytes 415 155 bytes
CPU, 300 s session ~2 s ~116 s
Prediction accuracy 92.7-95.5% 91.5-92.7%
Desyncs 0 0

2 997 checksum comparisons agreed, across three continents and zero desyncs. Two different CPUs, two operating systems, two libcs. That is the evidence behind the determinism rules in 05, and two processes of one binary on one machine could never have produced it.

What distance does, at a glance:

From Madrid to SRTT Max depth Stalls FPS
Frankfurt 50 ms 3 0 60.01
São Paulo 272 ms 8 (the limit) 827 57.37
Tokyo 267 ms 8 (the limit) 1 283 56.01

The default 8-frame prediction window is 133 ms at 60 Hz - exactly one way at 267 ms. Beyond that the session stops speculating and waits, which is the design working correctly and also the point at which it needs retuning. What that retuning costs, and who pays for it, is measured in 08 - Experiments along with all five synthetic profiles.

Documentation

Document Subject
00 - Glossary Start here. Every technical term from scratch
01 - Theory What rollback is, why it exists, what it costs
02 - Architecture The crates, and why the boundary sits where it does
03 - Protocol Datagram format, authentication, handshake
04 - Running locally Controls, commands, a session end to end
05 - Determinism The rules that prevent desync, and how they were checked
06 - AWS The infrastructure, the threat model, the session key
07 - Dashboard Prometheus, Grafana, what each panel means
08 - Experiments Five profiles, the method, the results
09 - The Last Blade 2 The FBNeo core, the boot script, the pinned commit
10 - Costs What a session costs, and where money disappears
11 - Cleanup How to destroy everything and confirm it went
12 - Troubleshooting Symptoms, causes, what to look at first
13 - Coverage What was validated, what was not
14 - Video Recording sessions, and watching rollback happen
15 - Elastic Per-event analysis: what summary.csv cannot answer
16 - The algorithm Data structures, invariants, code paths, complexity
17 - How emulation works What it means to run a 1998 arcade board in software
18 - Dashboards and queries Kibana panels, ES|QL, and the published dataset

The dataset is published. Every session log is in dataset/, 5 MB compressed, so the analysis can be checked or taken somewhere else without running a session or spending anything on AWS.

Diagrams: system topology and crate graph, a rollback on a timeline, AWS network topology.

Against the specification

What the lab set out to do, and what actually happened. The two deviations are listed with the rest rather than buried.

Requirement Status Where
Rollback engine: prediction, limit, history, re-simulation done 16
Defaults: 1 input delay, 8 prediction, 16 states, 60 Hz done SessionConfig::default
Stop on window full; end after 3 s of silence done 16
Checksums every 60 confirmed frames; desync ends the session done 05
UDP/7000, versioned wire, 1 200-byte limit, HMAC-SHA256 done 03
Six message types, 8-input redundancy, sequence + ACK done 03
Handshake validates version, commit, config, seed, hashes done 03
Integer-only 2D arena + FSM bot done 02
SDL2 client, overlay, keyboard and gamepad done, played end to end 18
FBNeo core in a reproducible container done, different commit 09
Emulated game via retro_serialize, scripted boot, no ROM offsets done 09
Prometheus, Grafana, JSONL, all listed metrics done 07
Five profiles, 180 s, summary.csv + self-contained HTML done 08
Terraform VPC, SSM, S3, IMDSv2, 4 h terminate, no SSH done 06
just test / local-up / aws-up / play / bench / collect / aws-down done 04
Unit, property, 100 k replay, golden protocol, fake core, E2E done 13
Gates: fmt, clippy, tests, shellcheck, terraform, docs done just test
AWS smoke: handshake, session, collect, destroy done, ×6 across three regions 08
Didactic documentation of every technical term done 00

One deviation, forced and documented.

The FBNeo commit differs. The spec pinned finalburnneo/FBNeo@f1c3545f…, which has no makefile.libretro. The build uses libretro/FBNeo@0332bb98… and records both hashes in the artefact's provenance, so the deviation is visible from the binary rather than only from this table.

Beyond the spec: video recording with burned-in telemetry (14), per-event Elasticsearch analysis (15), an algorithm reference (16), and runs against three regions rather than one.

Every acceptance criterion that was reachable has been met, including the last one to fall: a human on P1, playing The Last Blade 2 against the scripted bot in Frankfurt over the real internet. That session produced the most surprising number in the project, and it contradicted an assumption written into these docs. See 18 - Dashboards.

What still has no measurement is perception. The lab can say a correction was 8 frames deep; it cannot say whether that was noticeable. That is the one question rollback exists to answer and the one this repository does not.

Quick start

Prerequisites

Tool For Check
Rust ≥ 1.82 building everything cargo --version
SDL2 ≥ 2.0.20 the graphical client pkg-config --modversion sdl2
Docker FBNeo build, Prometheus, Grafana, Elastic docker --version
ffmpeg recording sessions (optional) ffmpeg -version
just the commands below just --version
Terraform ≥ 1.6 AWS infrastructure terraform version
AWS CLI aws-up, collect, aws-down aws sts get-caller-identity
shellcheck the script lint gate shellcheck --version

x86_64. The observability docker-compose uses host networking and is therefore Linux-only; the reason is written in the file itself.

A full test, no AWS and no ROM

just test        # fmt, clippy, tests (debug and release), shellcheck, terraform, docs
just e2e         # two real processes, a real socket, all five profiles
just bench       # 180 s per profile, writes summary.csv and report.html

just bench produces artifacts/report/summary.csv, one row per session across about 37 columns, and artifacts/report/report.html, which is self-contained: no CDN, no script, charts as inline SVG.

Local observability

just local-up
# Grafana     http://127.0.0.1:3000
# Prometheus  http://127.0.0.1:9090
# Exporter    http://127.0.0.1:9898/metrics

A session against AWS

cp terraform/example.tfvars terraform/terraform.tfvars
$EDITOR terraform/terraform.tfvars     # allowed_cidr = your address, as a /32
curl -s https://checkip.amazonaws.com  # to find it

just aws-up arena
just play arena
just collect      # ALWAYS before aws-down
just aws-down

The Last Blade 2

You supply the ROM. It needs neogeo.zip, the Neo Geo BIOS, in artifacts/system/: a Neo Geo game is only half the code that runs, and the BIOS is hashed into the handshake alongside the ROM.

just build-core                                  # builds FBNeo in a container
just check-determinism /path/lastbld2.zip        # check the core before anything
just e2e 90 lastblade2 /path/lastbld2.zip
just aws-up lastblade2 /path/lastbld2.zip
just play lastblade2 /path/lastbld2.zip

just check-determinism is not fussiness. FBNeo as shipped seeds its RNG and its emulated calendar clock from the host clock, so two peers that start in different wall-clock seconds diverge before the first input. just build-core patches that; the measurement and the fix are in 05 - Determinism.

What this repository does not contain

No ROMs and no BIOS. lastbld2.zip and neogeo.zip are yours, and are never committed, redistributed, or included in any artefact here.

No savestates or personal logs. All of artifacts/ is gitignored.

No keys. The session key is ephemeral, generated per run, kept in SSM SecureString and in a local file with mode 0600, and never enters Terraform state or a command line.

Out of scope

STUN, relay, matchmaking, spectating, reconnection, state synchronisation, Tekken 3, vision-based AI and memory-reading bots. Fightcade is a reference point, not a dependency.

Multiple regions were originally out of scope and are now covered: Frankfurt, São Paulo and Tokyo, with ops/scripts/region-run.sh to reproduce any of them.

About

Rollback netcode built and measured: a deterministic arena and a 1998 Neo Geo arcade game under the same engine, played across three continents. 2 997 checksums, zero desyncs, dataset included.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages