Skip to main content

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.

The Source Engine organizes its codebase into a layered set of static libraries (the “tier” libraries) and dynamically loaded modules that communicate exclusively through versioned factory interfaces. Understanding this structure is essential before you navigate any part of the TF2 source tree, because every major subsystem — the game DLL, the engine, the material system, vphysics — follows the same pattern: export a CreateInterface symbol, register named interface versions, and let callers look them up at runtime.

The tier library system

The tier libraries form a strict dependency hierarchy. Higher tiers may link to lower ones, but never the reverse. Each tier lives in its own directory under tf2_src/.

tier0 — Platform and memory

The lowest layer. Provides platform abstraction, memory allocation (mem.cpp, memstd.cpp), debug assertions (dbg.cpp), fast timers (fasttimer.cpp), thread primitives (threadtools.cpp), and CPU profiling (vprof.cpp). Every other library and module links against tier0.

tier1 — Containers and utilities

Builds on tier0. Provides the canonical data structures: CUtlVector, CUtlBuffer, CUtlString, CUtlSymbol, KeyValues (KeyValues.cpp), ConVar (convar.cpp), the INetAdr networking address helper (NetAdr.cpp), bit buffers (bitbuf.cpp), and the CreateInterface implementation (interface.cpp).

tier2 — Filesystem and sound

Sits above tier1. Houses the filesystem abstraction layer and sound system helpers. Modules that need to read assets from VPK or loose files request the IFileSystem interface through this layer.

tier3 — Rendering helpers

The topmost shared library. Contains higher-level rendering helpers, mesh builders, and material system utilities consumed by the game client and other rendering modules.

Key tier1 files

FilePurpose
tier1/interface.cppImplements CreateInterface — the single exported DLL entry point
tier1/KeyValues.cppKey/value tree used for configs, VDF files, and entity I/O
tier1/convar.cppConVar and ConCommand registration
tier1/bitbuf.cppBit-level read/write buffers used by the network layer
tier1/utlbuffer.cppGeneral-purpose growable byte/text buffer

The CreateInterface factory pattern

Every Source Engine module exports exactly one C-linkage function, CreateInterface. Callers supply a versioned interface name (a plain string such as "VEngineServer023") and receive back a typed pointer, or nullptr if the version is not available.
// tier1/interface.cpp — simplified
void* CreateInterfaceInternal( const char *pName, int *pReturnCode )
{
    InterfaceReg *pCur;
    for (pCur = InterfaceReg::s_pInterfaceRegs; pCur; pCur = pCur->m_pNext)
    {
        if (strcmp(pCur->m_pName, pName) == 0)
        {
            if (pReturnCode)
                *pReturnCode = IFACE_OK;
            return pCur->m_CreateFn();
        }
    }
    // ...
}
Modules register their interfaces at static-init time using the EXPOSE_INTERFACE macro, which constructs an InterfaceReg node and prepends it to the global linked list InterfaceReg::s_pInterfaceRegs. When you call CreateInterface("VEngineServer023", nullptr), the engine iterates that list and returns the matching implementation.

Versioned interface names

Interface strings carry an explicit version number. When the contract changes in a backward-incompatible way, the version number increments and both the old and new strings can coexist in the list:
// public/eiface.h
#define INTERFACEVERSION_VENGINESERVER_VERSION_21  "VEngineServer021"
#define INTERFACEVERSION_VENGINESERVER_VERSION_22  "VEngineServer022"
#define INTERFACEVERSION_VENGINESERVER             "VEngineServer023"
#define INTERFACEVERSION_VENGINESERVER_INT         23
Always request the current version constant (e.g., INTERFACEVERSION_VENGINESERVER) rather than a hard-coded string literal. This ensures your code picks up the correct version automatically when the header is updated.

Key interfaces

The interface the engine exposes to the server game DLL (server.dll/server.so). It gives server-side game code access to level management, entity/edict lookup, PVS queries, player info, precaching, and more.
// public/eiface.h (lines 90–147)
abstract_class IVEngineServer
{
public:
    virtual void     ChangeLevel( const char *s1, const char *s2 ) = 0;
    virtual int      IsMapValid( const char *filename ) = 0;
    virtual bool     IsDedicatedServer( void ) = 0;
    virtual int      PrecacheModel( const char *s, bool preload = false ) = 0;
    virtual int      GetClusterForOrigin( const Vector &org ) = 0;
    virtual bool     CheckOriginInPVS( const Vector &org,
                         const unsigned char *checkpvs, int checkpvssize ) = 0;
    virtual int      GetPlayerUserId( const edict_t *e ) = 0;
    virtual int      GetEntityCount( void ) = 0;
    virtual edict_t *PEntityOfEntIndex( int iEntIndex ) = 0;
    // ...
};
The game DLL receives a pointer to this interface during GameDLL_Init. Treat it as the server’s window into engine services.
The counterpart exposed to the client DLL (client.dll/client.so). Provides access to player info structs, view angles, local player index, network channel info, and the client frame lifecycle stages.
// public/cdll_int.h — ClientFrameStage_t shows the frame pipeline
enum ClientFrameStage_t
{
    FRAME_UNDEFINED = -1,
    FRAME_START,
    FRAME_NET_UPDATE_START,
        FRAME_NET_UPDATE_POSTDATAUPDATE_START,
        FRAME_NET_UPDATE_POSTDATAUPDATE_END,
    FRAME_NET_UPDATE_END,
    FRAME_RENDER_START,
    // ...
};
The ClientFrameStage_t enum illustrates the ordered pipeline the engine drives every frame: network update → post-data-update → interpolation/prediction → render.
Owned by materialsystem.dll. Manages shaders, materials, render targets, and the GPU pipeline. The client DLL and rendering subsystems request it via CreateInterface("VMaterialSystem080", ...). You interact with it through g_pMaterialSystem.
Provided by vphysics.dll. Encapsulates a Havok simulation environment. The server creates one environment per map; the client creates a separate one for clientside physics. Both sides request the interface through CreateInterface("VPhysics031", ...).

The appframework application framework

appframework/ (implemented in AppSystemGroup.cpp) provides the lifecycle manager that orchestrates how a running executable loads, connects, initializes, and shuts down all its DLL modules.

Module lifetime

1

LoadModule

CAppSystemGroup::LoadModule(pDLLName) calls Sys_LoadModule (a thin wrapper around LoadLibrary/dlopen). The loaded module is tracked in m_Modules[] by name, so the same DLL is never loaded twice.
// appframework/AppSystemGroup.cpp (lines 47–78)
AppModule_t CAppSystemGroup::LoadModule( const char *pDLLName )
{
    // ...
    CSysModule *pSysModule = LoadModuleDLL( pDLLName );
    if (!pSysModule)
    {
        Warning("AppFramework : Unable to load module %s!\n", pDLLName);
        return APP_MODULE_INVALID;
    }
    // track in m_Modules[]
    return nIndex;
}
2

AddSystem

After loading, you add an IAppSystem-implementing object retrieved from the module’s CreateInterface. The group maintains an ordered list of systems in m_Systems[].
3

Connect / Init

The group calls Connect(factory) then Init() on every system in registration order. Systems use the provided factory to request other interfaces they depend on.
4

Shutdown / Disconnect

On exit, Shutdown() and Disconnect() are called in reverse order, ensuring clean teardown of dependent subsystems before the DLLs they use are unloaded.
The parent/child system group design (m_pParentAppSystem) lets the game DLL define a child group that adds game-specific systems on top of the engine’s base group without duplicating the engine’s module list.

Module boundary diagram

The following shows the major DLL boundaries and the factory interfaces that cross them at runtime:
┌─────────────────────────────────────────────────────────┐
│                      engine.dll                          │
│  IVEngineServer ──► game/server/server.dll               │
│  IVEngineClient ──► game/client/client.dll               │
│  INetworkStringTableContainer                            │
│  IStaticPropMgrServer / IStaticPropMgrClient             │
└──────────────────────────┬──────────────────────────────┘
                           │ CreateInterface
         ┌─────────────────┼──────────────────┐
         ▼                 ▼                  ▼
  materialsystem.dll   vphysics.dll    soundemittersystem.dll
  IMaterialSystem      IPhysics        ISoundEmitterSystemBase


    shaderapidx9.dll
    IShaderAPI
Each arrow represents a CreateInterface call at startup. No module calls into another’s internal functions directly — all cross-module communication flows through these abstract interfaces.

Directory reference

DirectoryRole
tier0/Platform, memory, debug, threading primitives
tier1/Containers, KeyValues, ConVar, interface registry
tier2/Filesystem, sound helpers
tier3/Rendering utilities
appframework/Module group lifecycle (load/connect/init/shutdown)
public/eiface.hIVEngineServer — engine → server game DLL contract
public/cdll_int.hIVEngineClient — engine → client game DLL contract
public/interface.hCreateInterface type definitions and EXPOSE_INTERFACE macro

Build docs developers (and LLMs) love