Doom 3 Network Architecture: Client/Server Model, Snapshots, and Compression Techniques
The article explains Doom 3's deterministic client‑server network architecture, describing how player inputs are sent to the server, how compressed state snapshots are returned at 10‑20 Hz, and the UDP‑based reliable/unreliable messaging and compression methods used to keep the game synchronized.
Doom 3 uses a deterministic client‑server architecture where the client samples player input and sends it to the server, which replies with compressed state snapshots inside the Potentially Visible Set (PVS). The game logic updates at a fixed 60 fps regardless of rendering performance.
C/S Architecture Diagram
The server sends state snapshots at 10‑20 Hz. Because each snapshot represents a moment in the past, the client rewinds to that point, applies the snapshot, and then re‑predicts forward to the current time using both its own and other players' inputs.
Prediction Example
Unlike Quake 3, Doom 3 renders the game state without visual lag, so the client does not need large local prediction buffers. Both server and client run the same code for entity updates, simplifying single‑player and multiplayer development.
Communication Layer
Doom 3 implements a lightweight UDP‑based protocol that carries both reliable and unreliable messages over a single connection. Unreliable messages transport frequent input and snapshot data, while only critical events use the reliable channel, which is piggy‑backed on the unreliable stream.
Message Header
The server includes a 32‑bit game ID and an 8‑bit message type. The client includes the latest server sequence ID for ACK handling, game ID for environment validation, snapshot sequence ID for delta compression, and its own message type.
Snapshots
Each snapshot contains a sequence ID, frame ID, frame time, and client‑ahead time, followed by delta‑compressed entity states, PVS bit‑strings, non‑PVS game updates, and other players' commands.
User Commands
Commands include a debug prediction time, the frame number of the first command, and subsequent per‑frame input deltas.
Compression Techniques
Bit‑packing removes unused bits (e.g., health stored in 7 bits instead of 32) and uses half‑precision floats where appropriate.
Delta compression works at variable, entity, and PVS levels, transmitting only changes between successive snapshots.
Zero‑compression (0‑compressor) packs runs of zeros efficiently, achieving up to a 4:1 reduction; an example shows a 14:8 compression ratio.
Effectiveness
4‑bit packing: 10‑15 % size reduction
Delta compression: >90 % reduction
Zero‑compressing: 15‑50 % reduction
Potential Improvements
Initialize snapshot base from a fully‑initialized map state to avoid initial traffic.
Convert non‑critical reliable events (e.g., visual effects) to unreliable with caching.
Allow the client to maintain purely visual entities locally.
Provide finer‑grained prediction control and the ability to disable synchronization for dead entities.
Implement level‑of‑detail syncing so less important entities update at lower frequencies.
Merge multiple prediction frames for low‑importance entities to reduce CPU load.
© Content sourced from the internet; original authors retain copyright.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.