MOHAA netcode, visualised

What your screen shows is never quite now. Move the sliders and watch. Hover any ? for an explanation.

Enemies: you see the past

Click the field to shoot at the enemy you see, and find out where they really were.

Waiting for the first updates…

Try a connection

Your connection

Game settings

The enemy

You: move first, get checked later

Hold the mouse (or a finger) on the field to steer, or click it and use WASD / arrow keys.

Waiting for the first updates…

Try a connection

Your connection

Game settings

How it works, in four points
  1. The server is the referee. Only the server knows where everybody really is. Your PC gets updates from it (snapshots), normally 20 a second, and each one is already old when it arrives: it spent half your ping travelling.
  2. Other players are shown slightly in the past. Twenty updates a second would look like a slideshow, so your game waits until it holds two updates and slides the enemy from one to the other. The price is delay: about half your ping plus one update. If the next update is late or lost, your game guesses for a moment, then gives up and the enemy freezes.
  3. You are shown in the present, optimistically. If your own movement waited for the server, every key press would feel as slow as your ping. So your game moves you at once and tells the server what you pressed. The server replays it and reports back. Usually they agree. When they don't, your game takes the server's answer and re-applies the key presses the server hasn't seen yet: a rubber-band.
  4. Why the shot missed. This game has no lag compensation: the server checks your shot against where the enemy is when the shot arrives, not where you saw them. Your screen is late, and the shot takes another half ping to get there. Against a runner you aim ahead, by roughly your ping plus one update.
What every setting does
How faithful is this to the game?

The simulation follows the OpenMoHAA source (checked at commit 667c50f9), with the game's defaults and limits:

  • Which moment your screen shows: the game tracks the server's clock from the updates it receives, creeps forward 1 ms per update and backs off 2 ms whenever it nearly ran out of data (CL_SetCGameTime, CL_AdjustTimeDelta). Nobody sets the delay; it finds its own level, which is why jitter increases it.
  • Other players: slide between two updates when both exist; otherwise continue along the last known velocity for at most cg_smoothClientsTime, or hold still if cg_smoothClients is off (CG_CalcEntityLerpPositions, BG_EvaluateTrajectory).
  • Updates leave only on server ticks, and only when 1000/snaps ms have passed, with snaps capped at sv_fps (SV_SendClientMessages, SV_UserinfoChanged).
  • Key presses: one command per rendered frame; a packet every 1000/cl_maxpackets ms carrying every command since cl_packetdup packets ago; the server runs them as they arrive and ignores ones it already ran (CL_WritePacket, SV_UserMove). Out-of-order packets are dropped (Netchan_Process).
  • Prediction: start from the newest update's verdict, replay every command after it (at most 128 of them, CMD_BACKUP, which is why a high ping limits your FPS), smooth a correction over 100 ms (CG_PredictPlayerState, cg_errordecay).
  • There is no lag compensation in the source; shots here are judged the same way.

Simplified on purpose: a flat field, one enemy, no walls, no acceleration; no delta compression, rate limiting or reliable commands; jitter and loss are evenly random, where a real line is bursty; your frame rate is your browser's (in the game, com_maxfps); and when you move the ping, sv_fps or snaps sliders the game's clock re-syncs at once, as if you had reconnected, so you see the effect right away (in the game it drifts there at 1–2 ms per update).

The long version of the enemy-visibility side, with the source quoted line by line: You Cannot Hide a Player and Show Them at the Same Time.

Settings calculator

Describe your connection and it suggests client settings. Hover any ? to see why.

fine-tune jitter and loss

● differs from the game default.