TF2 separates its game logic into two distinct shared libraries that are loaded by the engine at startup:Documentation Index
Fetch the complete documentation index at: https://mintlify.com/sr2echa/TF2-Source-Code/llms.txt
Use this file to discover all available pages before exploring further.
server.dll (or server.so on Linux) runs the authoritative simulation, and client.dll (or client.so) handles rendering, sound, and the local player’s input. They never call each other directly. Instead, the engine mediates every exchange — ticking the server at a fixed rate, serializing entity state changes as delta-compressed DataTable packets, and delivering them to the client on the next network frame.
The DLL split
server.dll / server.so
Contains all authoritative game logic:
CTFPlayer, CTFWeaponBase, CObjectSentrygun, round timers, damage calculation, and AI bots. It runs at a fixed tick rate (default 66 Hz for TF2 competitive, 20–33 Hz for casual matchmaking servers) and communicates outward only through IVEngineServer and the DataTable network layer.client.dll / client.so
Contains the client-side counterparts:
C_TFPlayer, C_TFWeaponBase, HUD elements, particle effects, and clientside animations. It drives the render loop and processes user input. It communicates inward only through IVEngineClient and the RecvTable layer.Both DLLs share code from
game/shared/. Files in that directory are compiled twice — once for each DLL — with #ifdef CLIENT_DLL / #ifdef GAME_DLL guards controlling which blocks are active.The game tick
The engine drives a fixed-interval simulation loop. Each iteration is called a tick.SVC_ServerInfo message sent at signon carries the tick interval the client must use for interpolation:
Client interpolation
Because packets arrive less frequently than the client renders frames, the client maintains a short time-delayed view of the world (typicallycl_interp seconds behind the server). Interpolated variables — positions, angles, floats — are tracked in VarMapping_t inside C_BaseEntity:
VarMapEntry_t wraps an IInterpolatedVar watcher. Every networked float, vector, or angle that should lerp between snapshots registers itself here.
Entity hierarchy
Server: CBaseEntity (game/server/baseentity.h)
CBaseEntity is the root of every server-side object. It holds the authoritative position, angles, model, team, health, and physics state. It is identified in the engine by an edict_t slot and an integer entity index.
StateChanged() call at the end of every setter marks the property dirty so the DataTable system knows to include it in the next delta update. This pattern — MANUALMODE_GETSET_PROP — appears throughout CBaseEntity and its subclasses.
Client: C_BaseEntity (game/client/c_baseentity.h)
C_BaseEntity is the client-side mirror. It receives networked state through RecvTable proxies and provides a rendering and prediction interface. It is identified by the same entity index as the server counterpart but lives only in the client DLL’s memory.
CBaseEntity:
| Aspect | CBaseEntity (server) | C_BaseEntity (client) |
|---|---|---|
| Physics | Full IPhysicsObject simulation | Clientside only (cosmetic) |
| Network | Sends state via SendTable | Receives state via RecvTable |
| Prediction | Runs on server | Runs locally via CPredictionCopy |
| Think | PhysicsRunThink() each tick | ClientThink() each frame |
The DataTable (DT) replication system
The DataTable system is the backbone of entity state replication. The server declares what to send withSendTable; the client declares how to receive it with RecvTable. The engine matches them up by name during connection.
Send side (server, public/dt_send.h)
ASendVarProxyFn translates a C++ member variable into a network-transmissible DVariant. The engine calls your proxy every tick for properties that may have changed:
nullptr from a SendTableProxyFn is equivalent to calling pRecipients->ClearAllRecipients() — no delta is sent to any client for that sub-table this tick.
Receive side (client, public/dt_recv.h)
RecvVarProxyFn does the inverse: it writes the incoming DVariant value into the client entity’s memory:
Standard proxies
CStandardSendProxies and CStandardRecvProxies provide ready-made proxy functions for the most common type conversions so you do not need to write them yourself:
Client-side prediction
Prediction eliminates the “laggy controls” feel by running player movement locally before the server confirms it. The client simulates the sameCUserCmd (mouse + keyboard input) that it sent to the server, stores the predicted result, and corrects its position when the authoritative server update arrives.
CTFPlayer (and its shared base CTFPlayerShared) declare both a send table and a recv table in the same header file using compile-time guards:
CLIENT_DLL is defined, the compiler sees EXTERN_RECV_TABLE; when GAME_DLL is defined it sees EXTERN_SEND_TABLE. The table name DT_TFPlayerShared matches on both sides, allowing the engine to bind them together.
The game/shared/ directory
Code ingame/shared/ compiles into both the server and client DLLs. This is where you find:
tf/tf_player_shared.h/tf_player_shared.cpp— condition flags, stun state, ammo trackingtf/tf_weaponbase_shared.cpp— firing logic and recoil that must match on both sidesshareddefs.h— team numbers, max players, damage flags used everywherebasegrenade_shared.cpp— grenade physics that must be deterministic for prediction