Design a Multiplayer Game Server (Real-Time)
Difficulty: Tier 3 Asked at: gaming companies, Amazon (GameLift), Meta, senior loops Time budget: 45–60 min
The finale: a real-time multiplayer game is the most latency-brutal and consistency-brutal system in the
repo at once. Dozens of players’ actions must sync across the world in tens of milliseconds, everyone must
agree on the game state, and cheaters must be stopped — all while the physics keeps ticking. It pulls
together stateful connections, authoritative state, and cheat-resistance. A fitting capstone that reuses
half the repo.
Prerequisites: Real-Time Communication, Design a Chat System, Geospatial / Spatial partitioning
1. Requirements
Functional:
- Many players join a game session/match; their actions (move, shoot) affect a shared world.
- Real-time state synchronization — all players see a consistent, up-to-date world.
- Matchmaking — group players into sessions (by skill, region, mode).
- Handle joins/leaves, disconnects/reconnects.
- Prevent cheating.
Non-functional:
- Ultra-low latency — actions reflected in tens of ms; lag ruins gameplay. 🚨 Defining constraint.
- Consistency of game state — players must agree on what happened (who shot first).
- Scale — millions of concurrent players across many sessions.
- Cheat-resistant — clients are untrusted (players hack them).
- Availability; graceful handling of network jitter/loss.
Out of scope: the game’s art/logic, payment/store, the physics engine internals.
2. Estimation
- 1M concurrent players, sessions of ~10–100 players each → tens of thousands of active game servers,
each running one/few sessions’ simulation.
- Tick rate: a session updates state ~20–60 times/sec; each tick processes inputs and broadcasts state to
all players in the session. 🚨 High-frequency, low-latency loops per session.
- Bandwidth per player is modest, but the latency and update frequency are the challenge, not raw
volume.
3. The core: authoritative server + state sync
🚨 The server is authoritative — it holds the true game state and simulates the world; clients send
inputs and render the state the server sends back. This is the foundation of consistency and cheat
resistance: 🚨 never trust the client with authoritative state (a hacked client would cheat).
flowchart TB
P1[Player 1] <-->|inputs / state, UDP| GS[Game Server<br/>authoritative simulation<br/>tick loop 30-60Hz]
P2[Player 2] <--> GS
P3[Player 3] <--> GS
MM[Matchmaking] -->|assign players| GS
GS --> State[(Session state<br/>in memory)]
GS -.persist results.-> DB[(Durable store<br/>stats, progression)]
Each game session runs on a server as a tick loop: gather player inputs → advance the simulation
(physics, collisions, rules) → broadcast the new authoritative state to all players → repeat 30–60×/sec.
4. Deep dives
4a. Latency hiding — client prediction & reconciliation
Even at tens of ms, waiting for the server round-trip before showing your own movement feels laggy. So
clients predict locally (apply your input immediately) and reconcile when the authoritative state
arrives (correct any divergence, smoothly). Other players’ positions are interpolated between received
states (and slightly delayed) for smoothness. 🚨 Client prediction + server reconciliation + interpolation
is how games feel instant despite network latency — the signature technique.
4b. Why UDP, not TCP
🚨 Games use UDP, not TCP. TCP’s ordered, reliable, retransmit-everything behavior causes head-of-line
blocking — a lost packet stalls everything, and an old position retransmitted is useless (you want the
latest, not the missed old one). UDP sends fast, unordered, lossy datagrams; the game handles what little
reliability it needs at the app layer (e.g. resend critical events, ignore stale positions). Fresh-over-
complete is the right trade for real-time state. (Real-Time Communication)
4c. The authoritative tick loop & consistency
The server resolves all conflicts deterministically each tick (who hit whom, using server-side positions and
timestamps), so every player agrees on the outcome — no client can claim “I shot first.” Lag
compensation: the server rewinds to the shooter’s view-time to fairly adjudicate hits despite latency. This
authoritative, tick-based resolution is what gives consistency in a chaotic real-time world.
4d. Matchmaking
Group players into sessions by skill (rating), region (latency — put players on a nearby server), and mode.
It’s a matching/queueing problem: players enter a queue, matchmaker forms balanced groups, assigns them to a
game server (spun up on demand near them). Balance match quality vs wait time. Sessions are then handed to a
game server from a fleet (autoscaled).
4e. Cheat prevention
Clients are untrusted and actively hacked. Defenses: 🚨 server-authoritative state (the big one — the
server validates every action; a client can’t teleport or shoot through walls because the server simulates
truth), server-side validation of inputs (is this move physically possible?), anti-cheat detection
(statistical anomalies), and not sending clients data they shouldn’t see (e.g. don’t send enemy positions
behind walls → prevents wallhacks). Never trust the client is the through-line.
4f. Scaling & session management
Each session is a stateful, in-memory simulation on a server (like a shard). Scale by running many
independent sessions across a fleet — sessions don’t interact, so this scales horizontally by adding
servers (like partitioning by session). Handle server failure (a crashed session is bad — replicate critical
state or accept match loss); place servers regionally for latency. Reconnect logic restores a dropped player
into the ongoing session.
5. Bottlenecks & scaling further
- Latency → UDP, client prediction/reconciliation, interpolation, regional servers.
- Consistency → authoritative server tick loop with deterministic resolution + lag compensation.
- Cheating → server-authoritative + input validation + minimal client info.
- Scale → many independent sessions across an autoscaled fleet (partition by session).
- Matchmaking → queue + skill/region-based grouping; balance quality vs wait.
- Disconnects → reconnect into the running session; handle jitter/loss gracefully.
6. Trade-off summary
| Decision |
Chosen |
Alternative |
Why |
| Authority |
Server-authoritative |
Client-authoritative / P2P |
Consistency + cheat resistance |
| Transport |
UDP |
TCP |
Fresh-over-complete; no head-of-line blocking |
| Perceived latency |
Client prediction + reconciliation |
Wait for server |
Feels instant despite real latency |
| Consistency |
Authoritative tick + lag compensation |
Trust client claims |
Fair, agreed outcomes |
| Scale |
Independent sessions on a fleet |
One big server |
Sessions don’t interact; scale horizontally |
| Anti-cheat |
Never trust client + validate |
Trust client |
Clients are hacked |
7. Follow-up questions
Why must the server be authoritative, and what does that give you?
Because the clients are untrusted — players hack them — so if the client held the authoritative game state or
its claims were believed, cheating would be trivial (teleport, infinite health, shoot through walls). Making
the server authoritative means the server holds the one true game state, simulates the world each tick, and
treats client messages only as *inputs* to validate and apply, not as facts to accept. This gives you two
things at once. First, consistency: since a single authoritative simulation resolves every interaction
deterministically (using server-side positions and timestamps), all players agree on outcomes — there's a
single source of truth for "who shot first," rather than conflicting client claims. Second, cheat resistance:
the server validates that each action is legal (is this move physically possible? is this shot line-of-sight
valid?) and rejects impossible ones, so a hacked client can't do things the rules forbid because the server,
not the client, decides what actually happens. "Never trust the client" is the through-line of multiplayer
design, and server authority is how it's enforced.
If the server is authoritative, how does the game still feel instant despite network latency?
Through client-side prediction, server reconciliation, and interpolation. When you press move, the client
immediately applies the input locally and shows your character moving — predicting what the authoritative
server will do — so your own actions feel instant instead of waiting for a round-trip. Meanwhile the input
also goes to the server, which simulates authoritatively and sends back the true state; the client reconciles
its predicted state against that authoritative state, correcting any divergence smoothly (usually small,
since the prediction is usually right). For *other* players, the client interpolates their positions between
the state updates it receives (rendering them slightly in the past) so their movement looks smooth despite
arriving as discrete, occasionally-late packets. Together these hide latency: your own actions are predicted
for immediacy, others' are interpolated for smoothness, and the authoritative server keeps everyone
eventually consistent while correcting mispredictions. This trio — predict locally, reconcile with the
server, interpolate others — is the signature technique that makes fast-paced online games playable over
real networks.
Why do games use UDP instead of TCP?
Because real-time games want the *freshest* data fast, and TCP's guarantees actively work against that. TCP
provides reliable, in-order delivery by retransmitting lost packets and holding back later data until the
missing piece arrives — head-of-line blocking — which means one dropped packet stalls the whole stream, adding
latency spikes that ruin gameplay. And TCP's reliability is often useless here: if a player-position update
is lost, you don't want it retransmitted later, because by then a newer position has superseded it — the stale
old one is worthless. UDP sends fast, unordered, best-effort datagrams with no retransmission or ordering
overhead, so a lost packet is simply skipped and the next fresh update arrives on time. The game then adds
back *only* the reliability it actually needs at the application layer — for example, resending critical,
non-superseded events (a player died) while letting continuous state updates be lossy. This "fresh over
complete" trade-off is exactly right for real-time state, which is why UDP (and UDP-based protocols) dominate
multiplayer networking.
How do you scale to millions of concurrent players?
By exploiting that game sessions are independent and scaling them horizontally across a fleet, like
partitioning by session. Players are grouped by matchmaking into sessions of a few to a hundred players, and
each session is a self-contained, in-memory authoritative simulation running on a game server — sessions
don't interact with each other, so there's no cross-session coordination. To handle millions of players you
run tens of thousands of these independent sessions across a large, autoscaled fleet of game servers, spinning
servers up as matchmaking creates sessions and tearing them down as matches end, and placing servers in
regions close to their players to minimize latency. This is the same "partition into independent units and
add machines" scaling used for ride-hailing (by geography) or a matching engine (by symbol) — here the
independent unit is the match. The remaining concerns are operational: matchmaking as a queueing/grouping
service, regional placement for latency, autoscaling for the fleet, and handling server failure (replicating
critical state or accepting the loss of a match) and player reconnects into ongoing sessions.
8. What junior / mid / senior answers look like
- Junior: has clients send positions to each other or trusts client state — cheatable, inconsistent, and
laggy; may propose TCP and per-action request/response.
- Mid: server-authoritative sessions with a tick loop, UDP transport, client prediction, matchmaking, and
server-side validation for anti-cheat.
- Senior: builds the authoritative tick loop with deterministic resolution and lag compensation, the full
prediction/reconciliation/interpolation latency-hiding stack, UDP with app-level selective reliability,
cheat resistance rooted in never trusting the client (validation + minimal client info), and horizontal
scaling by independent sessions on an autoscaled regional fleet with reconnect handling — synthesizing
latency, consistency, and security at once.
Further reading