06
Deep Dive — Real-Time Game Networking
Everything else in this design is standard distributed systems. The hard, novel problem is keeping 100 players in sync on an 8km map with sub-100ms perceived latency over unreliable networks. Four interlocking techniques make this work.
The fundamental problem: The server and every client are always looking at different moments in time because of network latency. A player in Mumbai is ~30ms from the server; add interpolation delay and they see the world ~80ms in the past. The netcode's job is to create the illusion of a shared present moment.
Client
1. Client-Side Prediction
Your inputs are applied locally and instantly — the client runs the same physics engine as the server. You don't wait for the server to confirm movement. The client "predicts" the result. This is why your own movement always feels responsive.
Client
2. Server Reconciliation
When the server's authoritative state arrives, the client checks if its predictions were correct. If not, it re-simulates all unconfirmed inputs on top of the server's position. Small errors are blended smoothly; large errors cause a visible "rubber-band" snap.
Client
3. Entity Interpolation
Other players' positions arrive at 20 Hz but must render at 60–144 FPS. The client renders them one tick behind (~50ms in the past), smoothly interpolating between the two most recent snapshots. This is why enemies look smooth even at low tick rates.
Server
4. Lag Compensation
When you fire, you're aiming at a target ~80ms in the past. The server rewinds hitboxes to the time you saw the world and checks your shot there. This makes shooting feel accurate, but means targets can occasionally die "behind cover."
Client-Side Prediction — How It Works
Without prediction, pressing W results in 83ms of nothing (30ms to server + 3ms processing + 50ms back), then a sudden teleport. At 60 FPS, that's 5 frames of your character ignoring you. Unplayable.
With prediction: the client immediately applies your input to a local physics simulation — the same code the server runs. It stores each predicted result in a prediction buffer (ring buffer, ~128 entries = 2 seconds of inputs). When the server confirms input #N, the client discards entries up to N and re-simulates everything after N on top of the server's authoritative position.
The re-simulation typically processes 4–14 frames and takes < 0.1ms — invisible in the frame budget. When prediction matches (99% of the time), the player sees nothing. When it doesn't, the correction is blended over 50–200ms depending on error magnitude.
Lag Compensation — The Shot Sequence
sequenceDiagram
participant A as Player A (Shooter)
participant S as Game Server
participant B as Player B (Target)
Note over A: Sees B at position
from 80ms ago
A->>A: Click fire — muzzle flash
plays instantly (prediction)
A->>S: PLAYER_INPUT {fire, yaw, pitch, seq:47}
Note over S: Receives after ~30ms
S->>S: Calculate A's view time
T - 30ms - 50ms = T-80ms
S->>S: Rewind B's hitbox to T-80ms
S->>S: Raycast → HIT on B's torso
S->>S: Apply 38 HP damage (AKM)
S-->>A: HIT_CONFIRM {damage:38, zone:torso}
S-->>B: DAMAGE_EVENT {from:A, dmg:38, dir:NW}
Note over A: ~63ms total — sees hit marker
Note over B: ~63ms total — takes damage,
screen shakes
The server maintains a position history buffer for every player (last 1 second, 20 entries × ~40 bytes = 80 KB total). On a shot, it interpolates between the two history entries bracketing the shooter's view time, reconstructs hitboxes, and checks the ray. The rewind is capped at 250ms to prevent abuse from players intentionally adding latency.
The Peeker's Advantage — An Unavoidable Tradeoff
Because of lag compensation, the player who peeks a corner has a ~80ms advantage over the defender. The peeker sees the defender immediately, but the defender sees the peeker ~80ms late. This is a fundamental consequence of physics (speed of light + network latency) — there is no solution that eliminates it without breaking hit registration for the shooter. Every competitive FPS makes this same tradeoff.
The Tick Budget
At 20 Hz, the server has 50ms per tick to process everything:
| Phase | Budget | What Happens |
| Network receive | ~2ms | Read all queued UDP packets from 100 clients |
| Input processing | ~3ms | Apply 100 players' inputs to world state |
| Physics simulation | ~8ms | Movement, collision, vehicles, projectiles |
| Hit detection | ~5ms | Lag-compensated raycasts for active shooters |
| Game logic | ~4ms | Zone damage, loot, airdrops, kills/knocks |
| Anti-cheat | ~3ms | Speed checks, fire rate, position sanity |
| State broadcast | ~10ms | Per-player AOI filter + delta compress + send |
| Headroom | ~15ms | Absorbs late-game spikes (30 players in small circle) |
Area of Interest (AOI) — Spatial Grid
The 8km × 8km map is divided into 500m × 500m cells (16 × 16 = 256 cells). Each player belongs to one cell. Their AOI = their cell + 8 neighbors (3×3 grid). Only entities within these 9 cells are included in their state update — cutting broadcast from 100 to ~20 players. Cell lookup is O(1) by hashing position to cell index.
Delta Compression — 60–80% Bandwidth Savings
Instead of sending full state every tick, the server tracks what each client has ACK'd and sends only changed fields. A field bitmask (1 byte) indicates which fields follow. If only position changed: 7 bytes instead of 19 bytes per entity. If nothing changed: 1 byte. Typical savings: ~82%.