Server Architecture
The server (server/) is the authoritative simulation layer. It owns the canonical game state, runs the selected game library as a shared library, and delivers entity snapshots to clients each frame. For the default Warcraft III build, game logic lives in games/warcraft-3/game/ and is built as libgame.
Startup
SV_Init (server/sv_main.c) runs at process startup:
- Loads the game library (
libgame.so/game.dylib) viadlopenand obtains agame_export_t *pointer by callingGetGameAPI. - Calls
ge->Init()to let the game library set up its global state. - Calls
SV_InitGamewhich reads the map archive, spawns world entities (doodads, pre-placed units, starting locations), and notifies the game library that a new map has loaded.
Main Loop
SV_Frame(DWORD msec) is called from the platform main loop alongside CL_Frame. Unlike the client, the server advances only when its monotonic real-time clock reaches the next fixed FRAMETIME (100 ms) simulation deadline:
void SV_Frame(DWORD msec) {
svs.realtime += msec;
SV_ReadPackets(); // network remains live even while simulation is paused
if (sv.paused) {
if (paused_keepalive_due)
SV_SendClientMessages(); // frozen frame/time; prevents timeout
return; // do not advance ge->RunFrame()
}
if (svs.realtime < sv.next_frame_msec)
return; // not yet time for a new game frame
sv.next_frame_msec = SV_ClampSimulationDeadline(
svs.realtime, sv.next_frame_msec);
SV_RunGameFrame(); // 1. advance simulation
sv.next_frame_msec += FRAMETIME;
SV_SendClientMessages(); // 2. send snapshots to clients
}
This fixed-step approach decouples the simulation rate from the render rate and makes replay and deterministic simulation practical. SV_SetPaused() gates only simulation advancement; transport stays active, and resuming rebases sv.next_frame_msec so paused wall-clock time is not simulated as catch-up work. Warcraft III pause policy is documented in Pause And Modal UI.
SV_Frame executes at most one simulation step per outer-loop call. Before that step it applies SV_ClampSimulationDeadline: normal sub-tick lateness is preserved, but if the simulation deadline is more than one FRAMETIME behind wall time the deadline is rebased to the current svs.realtime. This follows Quake II's server policy of never allowing a multi-tick wall-clock stall to become a long burst of catch-up simulation. It is especially important for the local-client architecture because synchronous map loading and renderer/resource registration can block the outer loop for seconds. That wall time is loading latency, not simulation work, and must not later be replayed as consecutive 100 ms game ticks at render-loop speed.
A September 2026 WC3 campaign trace confirmed the failure mode directly: multi-second map-load stalls left the server several seconds overdue; at a ~15–16 ms render-loop cadence, each subsequent outer iteration advanced the fixed simulation by 100 ms until the backlog drained, making JASS/camera time run roughly six times faster than wall time. The deadline clamp removes that debt while preserving ordinary scheduler jitter. The regression is covered by server_net.scheduler_clamps_multi_tick_wall_clock_backlog. See WC3 Cinematics for the campaign symptom.
Reference behavior: id Software Quake II server/sv_main.c, SV_RunGameFrame, clamps server realtime when the simulation falls behind so the engine does not replay an arbitrarily large backlog after a stall.
1. SV_ReadPackets
Drains the network receive buffer (loopback ring or UDP socket) for each connected client slot. Dispatches clc_* messages:
| Opcode | Effect |
|---|---|
clc_connect |
Allocate a client slot, send svc_serverdata + baselines + UI layout |
clc_move |
Store the usercmd_t for this client for use in ge->ClientThink |
clc_stringcmd |
Forward console commands to SV_ExecuteClientCommand |
2. SV_RunGameFrame
void SV_RunGameFrame(void) {
sv.framenum++;
sv.time += FRAMETIME;
ge->RunFrame(); // runs the game library's g_main.c G_RunFrame
}
G_RunFrame (in games/warcraft-3/game/g_main.c) iterates every live entity and calls G_RunEntity, which dispatches on movetype and calls the entity's think or update callback.
3. SV_SendClientMessages
For each connected client, SV_BuildClientFrame (server/sv_ents.c) collects the entities visible to that client into a snapshot. SV_WriteFrameToClient then delta-encodes the snapshot against the previous one using MSG_WriteDeltaEntity and transmits it as an svc_packetentities message.
After copying each authoritative edict_t.s, SV_BuildClientFrame calls the mandatory
game_export.CustomizeEntity hook. Games that do not author recipient-specific state must
export an explicit no-op; setting the hook to NULL crashes at the first visible entity.
WoW uses the hook for per-client quest markers, while WC3 and SC2 leave the copied state unchanged.
Delta compression ensures only changed entity fields are sent, keeping bandwidth usage low even with many active entities.
Game Library Interface
The server communicates with the game library through two vtable structs defined in the selected game's API directory. For Warcraft III, that is games/warcraft-3/game/api/.
game_import_t — server services provided to the game
The server fills this struct and passes it to GetGameAPI. It provides:
| Function | Purpose |
|---|---|
LinkEntity / UnlinkEntity |
Register an entity with the spatial index |
BoxEdicts |
Spatial query — all entities inside a bounding box |
Trace |
Sweep test against world geometry |
PointContents |
Terrain/water flags at a world position |
SetModel |
Assign a model by name to an entity |
MemAlloc / MemFree |
Server heap |
SetPaused |
Gate authoritative simulation while keeping server transport alive |
WriteXxx |
Network message write helpers |
game_export_t — entry points exported by the game library
| Function | Purpose |
|---|---|
Init |
Called once at server startup |
Shutdown |
Called when the server shuts down |
SpawnEntities |
Parse map entities and place initial units |
RunFrame |
Advance simulation by one FRAMETIME tick |
ClientConnect |
Called when a new client slot is allocated |
ClientBegin |
Called when a client finishes loading the map |
ClientDisconnect |
Quake 2 contract: called once from SV_DropClient for a client that had reached ClientBegin (cs_spawned). The game releases connection-scoped state (WC3: connected, modal/pause ownership) and keeps the player's simulation state. Reached by the client's disconnect stringcmd (SV_Disconnect_f) or a server-side drop. Remote slots then sit in cs_zombie for BZ_CLIENT_ZOMBIE_MSEC; loopback slots are freed at once. |
ClientThink |
Per-frame input processing for each client |
Entity System
Entities are fixed-size edict_t structs stored in a flat array (g_edicts[]) in the game library. The server and game library share a read-only view of this array via ge->edicts and ge->num_edicts.
Each entity has:
- entityState_t s — the fields transmitted to clients (origin, angles, model index, frame, effects flags, …)
- entityShared_t shared — spatial data used by the server for link/unlink/trace (bounding box, link count, …)
- All other fields (health, AI, move state, callbacks) are game-library-private and never sent over the network
Only the entityState_t part is delta-encoded and sent to clients.
Snapshots and Delta Compression
The server maintains a ring buffer of clientSnapshot_t records per client (in server/sv_ents.c). Each snapshot stores the full entityState_t set visible to that client at a given frame. SV_WriteFrameToClient compares the latest snapshot against the acknowledged one and emits only the fields that differ:
// server/sv_ents.c
MSG_WriteDeltaEntity(&msg, &oldState, &newState, false, newState.number == cl->clientNum+1);
The client's acknowledged frame number is tracked per slot so the server can re-send if a snapshot was lost.
When the visible candidate set exceeds MAX_PACKET_ENTITIES, the server keeps
the nearest candidates as before. Games may implement
game_export.IsSnapshotPriorityEntity for recipient-visible entities whose
presentation must survive ordinary saturation. The callback is game-owned and
does not expose game-specific marker types to the server. Priority entities
still share the packet budget; if they alone exceed it, distance determines
which ones fit.
Key Files
| File | Purpose |
|---|---|
server/sv_main.c |
SV_Frame, SV_Init, SV_ReadPackets |
server/sv_ents.c |
SV_BuildClientFrame, SV_WriteFrameToClient, delta encoding |
games/warcraft-3/game/g_main.c |
G_RunFrame, G_ClientBegin, entity-per-frame dispatch |
games/warcraft-3/game/g_phys.c |
Entity movement, collision response |
games/warcraft-3/game/g_commands.c |
Player command handlers (move, attack, ability) |
games/warcraft-3/game/g_monster.c |
Unit lifecycle, animation state machine |
common/net.c |
Loopback + UDP transport shared with the client |
common/msg.c |
Message serialisation and delta encoding helpers |