Engine-Level Multiplayer Netcode
Building netcode that is fast, fair, and stable requires decisions at the transport, protocol, simulation, and presentation layers. This skill gives the full architecture used by modern FPS/MOBA engines, independent of any specific engine — then shows how each engine (Unity/Unreal/Godot) maps onto it.
1. Core Architecture
Client Server
Input sampler (fixed rate) -> Input validator
| (unreliable channel) Simulation (authoritative, fixed tick)
Local Prediction (client sim) Snapshot builder (delta vs ack)
Interpolation buffer Priority queue (interest management)
Reconciliation (rebase on snap) <- Unreliable snapshots (30-60Hz)
The Rule of Three
- Server owns truth (movement, damage, loot, spawns).
- Client predicts its own entity to hide latency.
- Clients interpolate remote entities to hide jitter.
2. Transport Choices
2.1 UDP vs TCP vs WebSocket
| Protocol |
Native use |
Web use |
| UDP |
game state (fast) |
not in browsers (use WebRTC) |
| TCP |
reliable control, telemetry |
-- |
| WebSocket |
web game both |
byte framing, reliable only |
| WebRTC |
-- |
unreliable + datachannel for web |
Rule: use an unreliable+reliable multiplexed channel on UDP (RakNet/uNet-like, custom). Web fallback: WebRTC datachannel or WebSocket with its own reliable/unreliable split.
2.2 Reliable-UDP Basics
- Sequence numbers per message.
- ACK every N ms or per packet; retransmit only lost ranges.
- Keep a send/receive window; message priorities.
- Fragmentation for >MTU messages.
3. Client Frame (Prediction)
struct ClientInput { uint32_t seq; float forward, right; bool jump, fire; };
Client::Frame() {
auto in = Input::sample(); // fixed rate (e.g., 60Hz)
pending_.push({ in, localTick_, state_ }); // store for reconciliation
Simulate(in); // LOCAL prediction
transport_.send(unreliable, in); // to server
}
4. Server Loop
Server::Tick(tick) {
processInputs(); // apply each client's input in arrival order
simulate(); // physics, AI, combat (fixed step)
buildSnapshot(); // delta against client acks
}
- Deterministic timestepping: align server to 30-60Hz fixed; physics substeps fixed.
- Client auth validation: reject out-of-order old inputs, clamp count, verify plausibility.
5. Reconciliation & Correction
- Client keeps history
tick -> state.
- A server snapshot carries
tick, positions, velocities, HP.
- On mismatch beyond tolerance: correct from server state, then replay pending inputs (fast forward).
void Client::OnSnapshot(const Snapshot& s) {
auto& predicted = history_[s.tick];
if (dist(predicted.pos, s.pos) > kTolerance) {
state_ = s.state; // rebase
for (auto& p : pendingAfter(tick)) // replay
Simulate(p.input);
}
}
6. Interpolation for Remote Entities
- Buffer 100-150ms of snapshots per remote actor.
- Render at
serverTime - interpDelay -> smooth between two snapshots.
- For fast, small, time-critical entities (projectiles) combine with extrapolation if necessary.
- Jitter smoothing: average buffered RTT/delay; adjust.
7. Lag Compensation (FPS Hit Detection)
- Fire event: client sends
t_client + hit params.
- Server rewinds dynamic actor positions to
t_client (using 100-250ms history).
- Test hit against rewound world; apply damage.
- Limit rewind window; validate.
8. Rollback (Fighters / RTS)
- Keep a history window of last N ticks of full world state; on late input: rewind, apply, resimulate, render.
- Requires deterministic simulation and fixed ticks.
- Null-space for exact replay of "same inputs -> same state".
9. Bandwidth & Interest Management
- Delta-compress snapshots vs the last ACK (reference frame).
- Per-entity priority:
f(relevance) — high for your combat, low for ambient.
- Interest: only send what's in the player's area/interest (spatial relevance).
- Interleave: send far/ambient entities at 5-15Hz; nearby at 30-60Hz.
Snapshot size rules of thumb
- Position: 3×12 bytes → quantize to 3×12 bits (or 8-10 bits/axis) → ~4 bytes.
- Include only deltas.
- Entities per player budget: ~32 per second target, total ~200B-1KB per frame.
10. Replication & Object Authority
- World objects owned by server; replicated with conditions (owner-only, initial-only).
- Player transforms replicated via unreliable snapshots.
- Latency-sensitive (bullets) started with reliable order-enforced message.
- State via
RepProps/SyncVar patterns; update only when changed, throttle by frequency.
11. Mapping to Engines
| Feature |
Unity (NGO) |
Unreal |
Godot (HLLAPI) |
| Main loop |
NetworkManager tick |
NetTick |
SceneMultiplayer |
| RPC |
[ServerRpc]/[ClientRpc] |
UFUNCTION(Server) |
rpc() |
| Sync |
NetworkVariables |
ReplicatedProps+conditions |
MultiplayerSynchronizer |
| Prediction |
custom (no built-in) |
built-in move |
manual |
| Interpolation |
NetworkTransform (lerp) |
built-in |
MultiplayerSynchronizer |
| Rollback |
custom |
built-in (fighter support) |
custom |
Old Unity MLAPI migrated to Netcode for GameObjects (2023.1+). Unreal has the strongest built-in for prediction. Godot's is minimal — you implement buffers yourself.
12. Connection Lifecycle
- Connect → handshake (version/checksum) → spawn match + entities → snapshot baseline → deltas.
- Disconnect: last-ack persisted; rejoin with authoritative baseline.
- Reconnect/resync: fast snapshot with key frame.
13. Anti-Cheat Practical Checks (server-side)
- Velocity plausibility: clamp max speed, direction constraints.
- Teleport detection: distance vs time.
- Command flooding: rate-limit inputs/sec.
- Verified input:
client tick monotonic and within window.
14. Scaling
- Shard per region/lobby; matchmaking external.
- Interest management reduces broadcast to O(visible).
- Dedicated servers: heavy simulation per world cell; stream content per region.
- For 100+ players: reduce snapshot rate for distant entities drastically.
15. Quality Gates (before shipping netcode)
- RTT test: 0ms and 150ms both playable.
- Loss test: 2% random loss no jitter for remote actors.
- Bandwidth: < 1 Mbit/s down, < 200 Kbit/s up typical FPS.
- Determinism replay: same inputs → same result (rollback/RTS).
- Reconnection within 2s after transient drop.
16. References
- CPU-side full skill:
skills/game/multiplayer-netcode/SKILL.md
- Deterministic sim:
skills/game/game-engine/ecs-pattern/references/ecs-netcode.md
- Engine mapping:
skills/game/unity/SKILL.md (§9), skills/game/unreal/SKILL.md (§7), skills/game/godot/SKILL.md (§8)
- Physics step:
skills/game/game-development/physics-engine/SKILL.md
1---2name: multiplayer-netcode-23description: Engine-level multiplayer netcode engineering - transport protocols, replication, latency hiding (prediction/interpolation), lag compensation, rollback, and scalability for game engines.4---56# Engine-Level Multiplayer Netcode78Building netcode that is fast, fair, and stable requires decisions at the transport, protocol, simulation, and presentation layers. This skill gives the full architecture used by modern FPS/MOBA engines, independent of any specific engine — then shows how each engine (Unity/Unreal/Godot) maps onto it.910## 1. Core Architecture1112```13Client Server14 Input sampler (fixed rate) -> Input validator15 | (unreliable channel) Simulation (authoritative, fixed tick)16 Local Prediction (client sim) Snapshot builder (delta vs ack)17 Interpolation buffer Priority queue (interest management)18 Reconciliation (rebase on snap) <- Unreliable snapshots (30-60Hz)19```2021### The Rule of Three22231. **Server owns truth** (movement, damage, loot, spawns).242. **Client predicts** its own entity to hide latency.253. **Clients interpolate** remote entities to hide jitter.2627## 2. Transport Choices2829### 2.1 UDP vs TCP vs WebSocket3031| Protocol | Native use | Web use |32|----------|-----------|---------|33| UDP | game state (fast) | not in browsers (use WebRTC) |34| TCP | reliable control, telemetry | -- |35| WebSocket | web game both | byte framing, reliable only |36| WebRTC | -- | unreliable + datachannel for web |3738Rule: use an unreliable+reliable multiplexed channel on UDP (RakNet/uNet-like, custom). Web fallback: WebRTC datachannel or WebSocket with its own reliable/unreliable split.3940### 2.2 Reliable-UDP Basics4142- Sequence numbers per message.43- ACK every N ms or per packet; retransmit only lost ranges.44- Keep a send/receive window; message priorities.45- Fragmentation for >MTU messages.4647## 3. Client Frame (Prediction)4849```cpp50struct ClientInput { uint32_t seq; float forward, right; bool jump, fire; };5152Client::Frame() {53 auto in = Input::sample(); // fixed rate (e.g., 60Hz)54 pending_.push({ in, localTick_, state_ }); // store for reconciliation55 Simulate(in); // LOCAL prediction56 transport_.send(unreliable, in); // to server57}58```5960## 4. Server Loop6162```cpp63Server::Tick(tick) {64 processInputs(); // apply each client's input in arrival order65 simulate(); // physics, AI, combat (fixed step)66 buildSnapshot(); // delta against client acks67}68```6970- Deterministic timestepping: align server to 30-60Hz fixed; physics substeps fixed.71- Client auth validation: reject out-of-order old inputs, clamp count, verify plausibility.7273## 5. Reconciliation & Correction7475- Client keeps history `tick -> state`.76- A server snapshot carries `tick`, positions, velocities, HP.77- On mismatch beyond tolerance: correct from server state, then **replay pending inputs** (fast forward).7879```cpp80void Client::OnSnapshot(const Snapshot& s) {81 auto& predicted = history_[s.tick];82 if (dist(predicted.pos, s.pos) > kTolerance) {83 state_ = s.state; // rebase84 for (auto& p : pendingAfter(tick)) // replay85 Simulate(p.input);86 }87}88```8990## 6. Interpolation for Remote Entities9192- Buffer 100-150ms of snapshots per remote actor.93- Render at `serverTime - interpDelay` -> smooth between two snapshots.94- For fast, small, time-critical entities (projectiles) combine with extrapolation if necessary.95- Jitter smoothing: average buffered RTT/delay; adjust.9697## 7. Lag Compensation (FPS Hit Detection)98991. Fire event: client sends `t_client` + hit params.1002. Server rewinds dynamic actor positions to `t_client` (using 100-250ms history).1013. Test hit against rewound world; apply damage.1024. Limit rewind window; validate.103104## 8. Rollback (Fighters / RTS)105106- Keep a history window of last N ticks of full world state; on late input: rewind, apply, resimulate, render.107- Requires deterministic simulation and fixed ticks.108- Null-space for exact replay of "same inputs -> same state".109110## 9. Bandwidth & Interest Management111112- Delta-compress snapshots vs the last ACK (reference frame).113- Per-entity priority: `f(relevance)` — high for your combat, low for ambient.114- Interest: only send what's in the player's area/interest (spatial relevance).115- Interleave: send far/ambient entities at 5-15Hz; nearby at 30-60Hz.116117### Snapshot size rules of thumb118119- Position: 3×12 bytes → quantize to 3×12 bits (or 8-10 bits/axis) → ~4 bytes.120- Include only deltas.121- Entities per player budget: ~32 per second target, total ~200B-1KB per frame.122123## 10. Replication & Object Authority124125- World objects owned by server; replicated with conditions (owner-only, initial-only).126- Player transforms replicated via unreliable snapshots.127- Latency-sensitive (bullets) started with reliable order-enforced message.128- State via `RepProps`/`SyncVar` patterns; update only when changed, throttle by frequency.129130## 11. Mapping to Engines131132| Feature | Unity (NGO) | Unreal | Godot (HLLAPI) |133|---------|-------------|--------|----------------|134| Main loop | `NetworkManager` tick | `NetTick` | `SceneMultiplayer` |135| RPC | `[ServerRpc]/[ClientRpc]` | `UFUNCTION(Server)` | `rpc()` |136| Sync | NetworkVariables | ReplicatedProps+conditions | MultiplayerSynchronizer |137| Prediction | custom (no built-in) | built-in move | manual |138| Interpolation | NetworkTransform (lerp) | built-in | MultiplayerSynchronizer |139| Rollback | custom | built-in (fighter support) | custom |140141Old Unity `MLAPI` migrated to Netcode for GameObjects (2023.1+). Unreal has the strongest built-in for prediction. Godot's is minimal — you implement buffers yourself.142143## 12. Connection Lifecycle144145- Connect → handshake (version/checksum) → spawn match + entities → snapshot baseline → deltas.146- Disconnect: last-ack persisted; rejoin with authoritative baseline.147- Reconnect/resync: fast snapshot with key frame.148149## 13. Anti-Cheat Practical Checks (server-side)150151- Velocity plausibility: clamp max speed, direction constraints.152- Teleport detection: distance vs time.153- Command flooding: rate-limit inputs/sec.154- Verified input: `client tick` monotonic and within window.155156## 14. Scaling157158- Shard per region/lobby; matchmaking external.159- Interest management reduces broadcast to O(visible).160- Dedicated servers: heavy simulation per world cell; stream content per region.161- For 100+ players: reduce snapshot rate for distant entities drastically.162163## 15. Quality Gates (before shipping netcode)164165- RTT test: 0ms and 150ms both playable.166- Loss test: 2% random loss no jitter for remote actors.167- Bandwidth: < 1 Mbit/s down, < 200 Kbit/s up typical FPS.168- Determinism replay: same inputs → same result (rollback/RTS).169- Reconnection within 2s after transient drop.170171## 16. References172173- CPU-side full skill: `skills/game/multiplayer-netcode/SKILL.md`174- Deterministic sim: `skills/game/game-engine/ecs-pattern/references/ecs-netcode.md`175- Engine mapping: `skills/game/unity/SKILL.md` (§9), `skills/game/unreal/SKILL.md` (§7), `skills/game/godot/SKILL.md` (§8)176- Physics step: `skills/game/game-development/physics-engine/SKILL.md`