Guide How to Hack Call of Duty Games & Quake Engine Games

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat


Cesspool Admin
Jan 21, 2014
Game Name
Call of Duty Series
Tutorial Link
How long you been coding/hacking?
4 Years
Coding Language
Call of Duty Engine & Quake Engine Games Guide

Our best Source Codes

Static Addresses
Many local player variables are static green addresses and don't require pointers, but not all of them.

COD Engine = Modified Quake Engine
All Call of Duty games are built upon the same engine, the Quake Engine. If you're interested in hacking any Call of Duty game need to have a good understanding of it's Engine. It's not as simple as CSGO or Assault Cube.

The latest public Quake Engine source code is available at: id-Software/Quake-III-Arena

A full breakdown of how Quake engine works is available @
Quake 3 Source Code Review: Architecture

The COD games obviously use a modified version but even the latest few COD games have very similar core code to the original engine. That is why when a new COD game comes out, hacks are released on the same day. You can take a source code from the previous game and just update a few things to make it work on the new game.

Most Important Structures You Need For Hacks
When hacking any Quake Engine based game such as COD games the most important structures to understand are:

c_entity cg_entities[64]
They will not be identical to the old Quake Engine source but they will be 75% the same. These old definitions are extremely helpful in any Quake engine game. You can find them in the github link above. Not everything will be identical in all quake engine games.

You will notice half of these structs have origin and angles, but not all of them will be what you want, you have to find the right one.

cg_t = client game struct
It contains your clientNum, current snapshot_t, the clientinfo_t array and your refdef_t which contains your viewmatrix and angles

typedef struct {
    int            clientFrame;        // incremented each frame
    int            clientNum;
    qboolean    demoPlayback;
    qboolean    levelShot;            // taking a level Menu screenshot
    int            deferredPlayerLoading;
    qboolean    loading;            // don't defer players at initial startup
    qboolean    intermissionStarted;    // don't play voice rewards, because game will end shortly
    int            latestSnapshotNum;    // the number of snapshots the client system has received
    int            latestSnapshotTime;    // the time from latestSnapshotNum, so we don't need to read the snapshot yet
    snapshot_t    *snap;                // cg.snap->serverTime <= cg.time
    snapshot_t    *nextSnap;            // cg.nextSnap->serverTime > cg.time, or NULL
    snapshot_t    activeSnapshots[2];
    float        frameInterpolation;    // (float)( cg.time - cg.frame->serverTime ) / (cg.nextFrame->serverTime - cg.frame->serverTime)
    qboolean    thisFrameTeleport;
    qboolean    nextFrameTeleport;
    int            frametime;        // cg.time - cg.oldTime
    int            time;            // this is the time value that the client
    // is rendering at.
    int            oldTime;        // time at last frame, used for missile trails and prediction checking
    int            physicsTime;    // either cg.snap->time or cg.nextSnap->time
    int            timelimitWarnings;    // 5 min, 1 min, overtime
    int            fraglimitWarnings;
    qboolean    mapRestart;            // set on a map restart to set back the weapon
    qboolean    renderingThirdPerson;        // during deaths, chasecams, etc
    qboolean    hyperspace;                // true if prediction has hit a trigger_teleport
    playerState_t    predictedPlayerState;
    centity_t        predictedPlayerEntity;
    qboolean    validPPS;                // clear until the first call to CG_PredictPlayerState
    int            predictedErrorTime;
    vec3        predictedError;
    int            eventSequence;
    int            predictableEvents[MAX_PREDICTED_EVENTS];
    float        stepChange;                // for stair up smoothing
    int            stepTime;
    float        duckChange;                // for duck viewheight smoothing
    int            duckTime;
    float        landChange;                // for landing hard
    int            landTime;
    int            weaponSelect;
    vec3        autoAngles;
    vec3        autoAxis[3];
    vec3        autoAnglesFast;
    vec3        autoAxisFast[3];
    refdef_t    refdef;
    vec3        refdefViewAngles;        // will be converted to refdef.viewaxis
    qboolean    zoomed;
    int            zoomTime;
    float        zoomSensitivity;
    char        infoScreenText[MAX_STRING_CHARS];
    int            scoresRequestTime;
    int            numScores;
    int            selectedScore;
    int            teamScores[2];
    score_t        scores[MAX_CLIENTS];
    qboolean    showScores;
    qboolean    scoreBoardShowing;
    int            scoreFadeTime;
    int        accuracys[WP_NUM_WEAPONS][2];
    int        accRequestTime;
    qboolean    showAcc;
    qboolean    accBoardShowing;
    int        accFadeTime;
    char        killerName[MAX_NAME_LENGTH];
    char            spectatorList[MAX_STRING_CHARS];
    int                spectatorLen;
    float            spectatorWidth;
    int                spectatorTime;
    int                spectatorPaintX;
    int                spectatorPaintX2;
    int                spectatorOffset;
    int                spectatorPaintLen;
    skulltrail_t    skulltrails[MAX_CLIENTS];
    int            centerPrintTime;
    int            centerPrintCharWidth;
    int            centerPrintY;
    char        centerPrint[1024];
    int            centerPrintLines;
    int            lowAmmoWarning;        // 1 = low, 2 = empty
    int            lastKillTime;
    int            crosshairClientNum;
    int            crosshairClientTime;
    int            powerupActive;
    int            powerupTime;
    int            attackerTime;
    int            voiceTime;
    int            rewardStack;
    int            rewardTime;
    int            rewardCount[MAX_REWARDSTACK];
    qhandle_t    rewardShader[MAX_REWARDSTACK];
    qhandle_t    rewardSound[MAX_REWARDSTACK];
    int            soundBufferIn;
    int            soundBufferOut;
    int            soundTime;
    qhandle_t    soundBuffer[MAX_SOUNDBUFFER];
    int            voiceChatTime;
    int            voiceChatBufferIn;
    int            voiceChatBufferOut;
    int            warmup;
    int            warmupCount;
    int            itemPickup;
    int            itemPickupTime;
    int            itemPickupBlendTime;    // the pulse around the crosshair is timed seperately
    int            weaponSelectTime;
    int            weaponAnimation;
    int            weaponAnimationTime;
    float        damageTime;
    float        damageX, damageY, damageValue;
    float        headYaw;
    float        headEndPitch;
    float        headEndYaw;
    int            headEndTime;
    float        headStartPitch;
    float        headStartYaw;
    int            headStartTime;
    float        v_dmg_time;
    float        v_dmg_pitch;
    float        v_dmg_roll;
    vec3        kick_angles;    // weapon kicks
    vec3        kick_origin;
    float        bobfracsin;
    int            bobcycle;
    float        xyspeed;
    int     nextOrbitTime;
    refEntity_t        testModelEntity;
    char            testModelName[MAX_QPATH];
    qboolean        testGun;
    int            lastPredictedCommand;
    int            lastServerTime;
    playerState_t savedPmoveStates[NUM_SAVED_STATES];
    int            stateHead, stateTail;
    int respawnTime;
    int redObeliskHealth;
    int blueObeliskHealth;
} cg_t;
The snapshot contains the data sent from the server to your client including the numEntities, your playerState_t and the entityState_t of all the entities.

typedef struct {
    int                snapFlags;            // SNAPFLAG_RATE_DELAYED, etc
    int                ping;
    int                serverTime;        // server time the message is valid for (in msec)
    byte            areamask[MAX_MAP_AREA_BYTES];        // portalarea visibility bits
    playerState_t    ps;                        // complete information about the current player at this time
    int                numEntities;            // all of the entities that need to be presented
    entityState_t    entities[MAX_ENTITIES_IN_SNAPSHOT];    // at the time of this snapshot
    int                numServerCommands;        // text based server commands to execute when this
    int                serverCommandSequence;    // snapshot becomes current
} snapshot_t;
playerstate can contain origin position, velocity, delta angles, eFlags, clientNum and your inventory stats

typedef struct playerState_s {
    int            commandTime;    // cmd->serverTime of last executed command
    int            pm_type;
    int            bobCycle;        // for view bobbing and footstep generation
    int            pm_flags;        // ducked, jump_held, etc
    int            pm_time;
    vec3        origin;
    vec3        velocity;
    int            weaponTime;
    int            gravity;
    int            speed;
    int            delta_angles[3];    // add to command angles to get view direction
    // changed by spawns, rotating objects, and teleporters
    int            groundEntityNum;// ENTITYNUM_NONE = in air
    int            legsTimer;        // don't change low priority animations until this runs out
    int            legsAnim;        // mask off ANIM_TOGGLEBIT
    int            torsoTimer;        // don't change low priority animations until this runs out
    int            torsoAnim;        // mask off ANIM_TOGGLEBIT
    int            movementDir;    // a number 0 to 7 that represents the reletive angle
    // of movement to the view angle (axial and diagonals)
    // when at rest, the value will remain unchanged
    // used to twist the legs during strafing
    vec3        grapplePoint;    // location of grapple to pull towards if PMF_GRAPPLE_PULL
    int            eFlags;            // copied to entityState_t->eFlags
    int            eventSequence;    // pmove generated events
    int            events[MAX_PS_EVENTS];
    int            eventParms[MAX_PS_EVENTS];
    int            externalEvent;    // events set on player from another source
    int            externalEventParm;
    int            externalEventTime;
    int            clientNum;        // ranges from 0 to MAX_CLIENTS-1
    int            weapon;            // copied to entityState_t->weapon
    int            weaponstate;
    vec3        viewangles;        // for fixed views
    int            viewheight;
    // damage feedback
    int            damageEvent;    // when it changes, latch the other parms
    int            damageYaw;
    int            damagePitch;
    int            damageCount;
    int            stats[MAX_STATS];
    int            persistant[MAX_PERSISTANT];    // stats that aren't cleared on death
    int            powerups[MAX_POWERUPS];    // level.time that the powerup runs out
    int            ammo[MAX_WEAPONS];
    int            generic1;
    int            loopSound;
    int            jumppad_ent;    // jumppad entity hit this frame
    // not communicated over the net at all
    int            ping;            // server to game info for scoreboard
    int            pmove_framecount;    // FIXME: don't transmit over the network
    int            jumppad_frame;
    int            entityEventSequence;
} playerState_t;
this contains eType, eFlags, trajectory, clientNum, angles and origin

typedef struct entityState_s {
    int        number;            // entity index
    int        eType;            // entityType_t
    int        eFlags;
    trajectory_t    pos;    // for calculating position
    trajectory_t    apos;    // for calculating angles
    int        time;
    int        time2;
    vec3    origin;
    vec3    origin2;
    vec3    angles;
    vec3    angles2;
    int        otherEntityNum;    // shotgun sources, etc
    int        otherEntityNum2;
    int        groundEntityNum;    // -1 = in air
    int        constantLight;    // r + (g<<8) + (b<<16) + (intensity<<24)
    int        loopSound;        // constantly loop this sound
    int        modelindex;
    int        modelindex2;
    int        clientNum;        // 0 to (MAX_CLIENTS - 1), for players and corpses
    int        frame;
    int        solid;            // for client side prediction, trap_linkentity sets this properly
    int        event;            // impulse events -- muzzle flashes, footsteps, etc
    int        eventParm;
    // for players
    int        powerups;        // bit flags
    int        weapon;            // determines weapon and flash model, etc
    int        legsAnim;        // mask off ANIM_TOGGLEBIT
    int        torsoAnim;        // mask off ANIM_TOGGLEBIT

    int        generic1;
} entityState_t;
cg_entities[] - Half of the entity list
cg_entities[] is a global entity array, but you also need the array of clientinfo_t because you need data from both structs. the clientinfo_t array is inside cgs_t
cg_entities is defined like this

struct cg_entities
    centity_t cgents[MAX_GENTITIES];
contains more entityState_t, this is the class that is used in the entitylist, the one that contains the other players in multiplayer

// centity_t have a direct corespondence with gentity_t in the game, but
// only the entityState_t is directly communicated to the cgame
typedef struct centity_s {
    entityState_t    currentState;    // from cg.frame
    entityState_t    nextState;        // from cg.nextFrame, if available
    qboolean        interpolate;    // true if next is valid to interpolate to
    qboolean        currentValid;    // true if cg.frame holds this entity
    int                muzzleFlashTime;    // move to playerEntity?
    int                previousEvent;
    int                teleportFlag;
    int                trailTime;        // so missile trails can handle dropped initial packets
    int                dustTrailTime;
    int                miscTime;
    int                snapShotTime;    // last time this entity was found in a snapshot
    playerEntity_t    pe;
    int                errorTime;        // decay the error from this time
    vec3_t            errorOrigin;
    vec3_t            errorAngles;
    qboolean        extrapolated;    // false if origin / angles is an interpolation
    vec3_t            rawOrigin;
    vec3_t            rawAngles;
    vec3_t            beamEnd;
    vec3_t            lerpOrigin;
    vec3_t            lerpAngles;
} centity_t;
So the entity list is an array of these centity_t objects. This class contains the entityState_t currentState which contains the "number variable which is the entity index. You use this number to find the corresponding clientinfo_t object in the clientinfo array. The number var here = the element number of the correspondening clientinfo_t object.

clientinfo_t has the names of the clients, once you find the names, you can find the clientNum in the same struct and then you can find the centity_t in the cg_entities array by using that clientNum

For instance if I wanted to check the players team, I would use this index to enter the other array

if (OA->cgs->clientinfo[OA->cgents->cgents[i].currentState.number].team == TEAM_SPECTATOR) return false;
The clientinfo_t array is inside cgs_t

cgs_t is a structure which gets replicated from the server to you. The gameState_t is important, but most important is the clientinfo array.

typedef struct {
    gameState_t        gameState;            // gamestate from server
    glconfig_t        glconfig;            // rendering configuration
    float            screenXScale;        // derived from glconfig
    float            screenYScale;
    float            screenXBias;
    int                serverCommandSequence;    // reliable command stream counter
    int                processedSnapshotNum;// the number of snapshots cgame has requested
    qboolean        localServer;        // detected on startup by checking sv_running
    // parsed from serverinfo
    gametype_t        gametype;
    int                dmflags;
    int             videoflags;
    int                elimflags;
    int                teamflags;
    int                fraglimit;
    int                capturelimit;
    int                timelimit;
    int                maxclients;
    char            mapname[MAX_QPATH];
    char            redTeam[MAX_QPATH];
    char            blueTeam[MAX_QPATH];
    int                voteTime;
    int                voteYes;
    int                voteNo;
    qboolean        voteModified;            // beep whenever changed
    char            voteString[MAX_STRING_TOKENS];
    int                teamVoteTime[2];
    int                teamVoteYes[2];
    int                teamVoteNo[2];
    qboolean        teamVoteModified[2];    // beep whenever changed
    char            teamVoteString[2][MAX_STRING_TOKENS];
    int                levelStartTime;
    int            ffa_gt;    //Forced FFA
    int                roundStartTime;    //Elimination
    int                roundtime;
    int                attackingTeam;    //CTF Elimination
    int                lms_mode;    //Last Man Standing
    int                nopickup;    //instantgib + nexuiz style rocket arena:
    int                 timetaken;    //Double Domination DD
    int domination_points_count;    //Domination
    int domination_points_status[MAX_DOMINATION_POINTS];
    int                scores1, scores2;        // from configstrings
    int                redflag, blueflag;        // flag status from configstrings
    int                flagStatus;
    qboolean  newHud;
    // locally derived information from gamestate
    qhandle_t        gameModels[MAX_MODELS];
    sfxHandle_t        gameSounds[MAX_SOUNDS];
    int                numInlineModels;
    qhandle_t        inlineDrawModel[MAX_MODELS];
    vec3            inlineModelMidpoints[MAX_MODELS];
    clientInfo_t    clientinfo[MAX_CLIENTS];
    // teamchat width is *3 because of embedded color codes
    char            teamChatMsgs[TEAMCHAT_HEIGHT][TEAMCHAT_WIDTH * 3 + 1];
    int                teamChatMsgTimes[TEAMCHAT_HEIGHT];
    int                teamChatPos;
    int                teamLastChatPos;
    int cursorX;
    int cursorY;
    qboolean eventHandling;
    qboolean mouseCaptured;
    qboolean sizingHud;
    void *capturedItem;
    qhandle_t activeCursor;
    // orders
    int currentOrder;
    qboolean orderPending;
    int orderTime;
    int currentVoiceClient;
    int acceptOrderTime;
    int acceptTask;
    int acceptLeader;
    char acceptVoice[MAX_NAME_LENGTH];

    // media
    cgMedia_t        media; //MFD
    int                delagHitscan;//
    int             altExcellent;//
} cgs_t;
clientInfo_t Array - Second half of the Entity List
clientInfo_t    clientinfo[MAX_CLIENTS];
The main reason you want this struct is because it's the only place where the entity name is

typedef struct {
    qboolean        infoValid;
    char            name[MAX_QPATH];
    team_t            team;
    int                botSkill;        // 0 = not bot, 1-5 = bot
    vec3            color1;
    vec3            color2;
    int                score;            // updated by score servercmds
    int                location;        // location index for team mode
    int                health;            // you only get this info about your teammates
    int                armor;
    int                curWeapon;
    int                handicap;
    int                wins, losses;    // in tourney mode
    int                teamTask;        // task in teamplay (offence/defence)
    qboolean        teamLeader;        // true when this is a team leader
    int                powerups;        // so can display quad/flag status
    int                medkitUsageTime;
    int                invulnerabilityStartTime;
    int                invulnerabilityStopTime;
    int                breathPuffTime;
    char            modelName[MAX_QPATH];
    char            skinName[MAX_QPATH];
    char            headModelName[MAX_QPATH];
    char            headSkinName[MAX_QPATH];
    char            redTeam[MAX_TEAMNAME];
    char            blueTeam[MAX_TEAMNAME];
    qboolean        deferred;
    qboolean        newAnims;        // true if using the mission pack animations
    qboolean        fixedlegs;        // true if legs yaw is always the same as torso yaw
    qboolean        fixedtorso;        // true if torso never changes yaw
    vec3            headOffset;        // move head in icon views
    footstep_t        footsteps;
    gender_t        gender;            // from model
    qhandle_t        legsModel;
    qhandle_t        legsSkin;
    qhandle_t        torsoModel;
    qhandle_t        torsoSkin;
    qhandle_t        headModel;
    qhandle_t        headSkin;
    qhandle_t        modelIcon;
    animation_t        animations[37];
    sfxHandle_t        sounds[MAX_CUSTOM_SOUNDS];
    int        isDead;
} clientInfo_t;
Modifying View Angles - they are not easily writeable
It's hard to find the correct view angles, in some games they are writeable and others they are not. In CSGO you use the usercmd to change view angles and in Quake Engine games it can have similar method of doing it.

Last edited:


Cesspool Admin
Jan 21, 2014
OpenArena - Quake Engine Hack Source Code Review
You will learn alot by watching this

Regular quake engine games do not use the virtual machine, OpenArena is the only game I know that uses it.

COD ViewMatrix is not a 4x4 matrix
Call of Duty does not use a regular float[16] view matrix. They use a struct called refdef_t

typedef struct {
    int            x, y, width, height;
    float        fov_x, fov_y;
    vec3        vieworg;
    vec3        viewaxis[3];        // transformation matrix

    int            time;
    int            rdflags;            // RDF_NOWORLDMODEL, etc
    // 1 bits will prevent the associated area from rendering at all
    byte        areamask[MAX_MAP_AREA_BYTES];

    // text messages for deform text shaders
} refdef_t;
The vec3 named viewaxis is the rotation matrix, if you combine it with the FOV and other values in this structure, you can do a WorldToScreen transformation like so:
bool WorldToScreen(vec3_t src, vec3_t dst, vec3_t &screen, float fovx, float fovy, float windowWidth, float windowHeight, vec3_t left, vec3_t up, vec3_t forward)
        vec3_t transform;
        float xc, yc;
        float px, py;
        float z;

        px = tan(fovx * PI / 360.0);
        py = tan(fovy * PI / 360.0);

        transform = Subtract(dst, src);

        xc = windowWidth / 2.0;
        yc = windowHeight / 2.0;

        z = DotProduct(transform, left);

        if (z <= 0.1)
      return false;

        screen.x = xc - DotProduct(transform, up) *xc / (z*px);
        screen.y = yc - DotProduct(transform, forward) *yc / (z*py);

        return true;
To learn more about view matrices read this Guide - How to get started with learning ViewMatrix

How to Find refdef_t
For refdef I like to search for the resolution as an array of bytes

if my resolution is 800x600

800 = 0x320 = {0x20 0x03 0x0 0x0}
600 = 0x258 = { 0x58 0x2 0x0 0x0}

So I search for "20 03 00 00 58 02 00 00", usually get like ~20 addresses and it's easy to tell if they're refdef_t once you view them in reclass/struct dissector. There will be many copies of this structure in memory, all containing the same data, but there is one which is the source for all the others, this is the logical refdef that the game uses, this is what you need to find. The correct refdef_t is inside cg_t

That's all I got for now, but basically you find all those structs and you can do anything you want. Mind you health is not replicated to the clients in the games I have looked at.

Checking for Valid Entities
In OpenArena I did this:

bool player::bValid()
    if (cgents->cgents[i].currentValid == qtrue)

    //if not a player
    if (ent->currentState.eType != ET_PLAYER) return false;

    //if local player
    if (ent->currentState.number == OA->cg->clientNum) return false;

    //if spectator
    if (client->team == TEAM_SPECTATOR) return false;
    //if not valid
    if (client->infoValid == qfalse) return false;

    //if current state not valid - ESP won't get drawn sometimes when far away
    if (ent->currentValid == qfalse)

        //if dead
        if (ent->currentState.eFlags & EF_DEAD) return false;
    if (ent->currentState.legsAnim == BOTH_DEATH1) return false;
    if (ent->currentState.legsAnim == BOTH_DEATH2) return false;
    if (ent->currentState.legsAnim == BOTH_DEATH3) return false;
    if (ent->currentState.legsAnim == BOTH_DEAD1) return false;
    if (ent->currentState.legsAnim == BOTH_DEAD2) return false;
    if (ent->currentState.legsAnim == BOTH_DEAD3) return false;

    return true;
Setting up an SDK and your hack
Use ReClass and the Quake Engine Source code to find the vars you need in the structures I talked about in this tutorial. Generate your classes. Find these objects, some are static some require pointers.

Define the addresses which these 4 structures point at:

cg_entities * cgents = nullptr;
cg_t * cg = nullptr;
cgs_t * cgs = nullptr;
gentity * localgent = nullptr;
Everything you need can easily be accessed by those pointers. For each entity in the cgents, find the corresponding entity in the clientInfo_t array and you have everything you need for aimbot & ESP.

Master the Quake Arena III Source code from github, and there is nothing that can stop you from hacking Call of Duty Games

Learn more with @Mystic's tutorial
Tutorial - How to find lots of engine functions in IDA (black ops 1)

Quake/Call of Duty Prefixes
Quake and Call of Duty name functions and variables based this this system.
Call of Duty added a bunch and sometimes will be inconsistent.

Client Sided
CLClient LocalHandles anything to do with the local client.
CGClient GameHandles anything to do with handing the game logic on the client.
RRenderDoes rendering stuff by adding to a render queue.
RBRenderer BackendPretty much the thing that actually processes and renders the queue.
SNDSoundIt does sound what more do you need?
UIUser InterfaceDoes things related to the UI like drawing text and usually use virtual screen space of 640 x 480.
MenuMenuHandles the logic of the ingame menus.
SCRScreen/Screen RefreshSomething to do with "refreshing the screen".

Server Sided
ClientClientHandles the logic for all client entities.
GGameHandles general game logic.
GScrGame ScriptUsually the bindings for the funtions used in the VM.
ClientScr/CScrClient ScriptUsually binding for player related functions in the VM.
ScrScriptAnything to do with the Script VM.
HudElemHud ElementNetworked UI elements that get rendered on the client.
CmdCommandUsually the command handlers for commands sent from the client like noclip, godmode etc.
SVServerServer related things.

BGBoth GamesAny code that is shared between the client and the server.
ConConsoleHandles the ingame console.
ComCommonCommon functions used by the server and the client.
Dvar/CvarDeveloper/Console VariableHandles the console variables system.
NETNetworkHandles the actual sending of packets.
MSGMessageHandles the crafting of packets similar to a stream.
SysSystemPlatform specific code.
QQuake??Usually different implementations of libc functions.
FSFile SystemHandles the reading/writing of files etc.
INInputHandles user button input.
VMVirtual MachineHandles executing and loading scripts.
Last edited:


Cesspool Admin
Jan 21, 2014
Credits to PhaethonH for this excellent guide:

BASEQ3 Runthrough, Server-side

Illustration of an execution path through a Q3 game module (baseq3), from startup through gameplay though shutdown, for dedicated server mode, in outline form.

Motivation: Enhanced understanding of Q3A to assist in modding and total conversions.
Caveats: This document certainly is not a thorough understanding nor explanation of Q3. Information was gleaned by poring through the game source text, with minimal testing. Most of the statements made in this document are based on source comments and gut feelings.

Engine Start
Before the game (modification) even loads, Quake 3 on startup:

  1. searches for/in pak (.pk3) files according to the server cvar fs_game
  2. runs startup configuration (.cfg) files default.cfg and q3config.cfg
  3. initializes the dynamic memory hunk (com_hunkMegs, com_soundMegs, com_zoneMegs)
  4. opens network sockets for listening
Game Start
The most straight-forward way to launch the game is to start a valid map, with the server command map mapname. Quake 3 on game start:

  1. loads the map/level (.bsp) file
  2. looks through the pak (.pk3) files found on startup
  3. loads the game qvm file (qagame.qvm)
  4. compiles the qvm bytecode to native code if vm_game is set to 2
  5. jumps to the QVM procedure linked at code address 0 (i.e. vmMain())
  6. upon returning from the QVM procedure, does some other things (such as network tasks, reading in server commands, complaining with Hitch Warnings, etc.)
  7. jumps to code address 0 again
  8. rinse, lather, repeat.
The QVM file lacks a symbol table, so the QVM interpreter/compiler subsystem must make a blind decision on a reasonable entry point into the QVM. Currently, Q3 simply branches to QVM code address 0. While Q3 (the engine) decides to jump to code address 0, the game (mod) expects Q3A to jump into the function named vmMain(). To ensure these two expectations coincide, vmMain() must be the first function compiled before any other function into QVM bytecode. This means the function vmMain() must be the first fully-defined function in the first assembled file. In baseq3, this is vmMain() in g_main.c, which gets assembled first as specified in the file game.q3asm, thus ensuring vmMain() links as code address 0. Note that other variables may be declared/defined before vmMain(), and other functions may be prototyped/declared before vmMain(), but the first function body to be compiled must be vmMain().

The function at code address 0 is the critical function. The associated name can be anything, but for consistency I shall refer to code address 0 as vmMain, as it is in baseq3.

On each server frame (time period determined by cvar sv_fps), the Q3 engine calls vmMain() with a number of arguments. The first argument (command in baseq3) determines what major action to which the game should react. These actions are enumerated in g_public.h, as enum type gameExport_t. Since the arguments to vmMain() are specified by the Q3 engine, and merely reflected in gameExport_t, modifying gameExport_t does not change anything in the game module, except possibly to confuse it badly.

Overview: vmMain command/major game function
A quick overview of the vmMain commands (major game functions) in baseq3:

  • GAME_INIT - called on game load/startup.
  • GAME_SHUTDOWN - called on end of map/level or server quitting, before actual exit.
  • GAME_CLIENT_CONNECT - called when a new player connects to the server.
  • GAME_CLIENT_THINK - called (frequency unknown) to handle player inputs and movements.
  • GAME_CLIENT_USERINFO_CHANGED - called when a client userinfo record has changed (such as name or player model) and the new information needs to be relayed to all players.
  • GAME_CLIENT_DISCONNECT - called when a client disconnect (voluntary disconnect, kicked, timed out), for selective cleanup. Not called on map/level end (which is wholesale destruction of QVM memory, See also GAME_SHUTDOWN).
  • GAME_CLIENT_BEGIN - called when a client's connection is established, map loaded, and ready to start playing (i.e. supply user inputs).
  • GAME_CLIENT_COMMAND - when a client sends a command to the server (such as say, say_team, or kill).
  • GAME_RUN_FRAME - called once per server frame to animate and move all non-player entities that need movement or thinking.
  • GAME_CONSOLE_COMMAND - called to handle server-side commands (from admin), including rcon commands.
  • BOTAI_START_FRAME - Schedules and executes bot user command (framerate of AI is not synchronized with server framerate).
The call to vmMain() is made by the Q3 engine. The parameters passed to vmMain are marshalled by the Q3 engine. Additions or changes to these major game functions must be implemented in the engine and reflected in the game module (mod). Such ability (to add or change major functions) is limited to developers with access to the Q3 engine (not mod) source. Currently (2002 Aug) this is Id Software itself and its direct licensees (proprietary license).

vmMain(GAME_INIT, levelTime, randomSeed, restart)
When the game module is first loaded on game start, Q3 calls vmMain with major function GAME_INIT, along with three additional arguments:

  1. levelTime - the number of milliseconds the game has been in progress, usually 0 (non-zero for restarts).
  2. randomSeed - a seed value for the random number generator (RNG).
  3. restart - ???, affects bot AI initialization only.
In vmMain(), the code immediately branches to G_InitGame() when vmMain recognizes GAME_INIT.

G_InitGame(levelTime, randomSeed, restart)
  1. Initialize random number generator with randomSeed -- srand(randomSeed).
  2. Register cvars (associates cvars in the Q3 engine with cvar_t variables in game engine) -- G_RegisterCvars().
    1. Goes through all entries in array gameCvarTable[], associating the cvar in the Q3 engine with the cvar_t variable in the game engine (QVM memory).
  3. Initialize banlist -- G_ProcessIPBans().
    1. Reads list of space-separated IP addresses from cvar g_banIPs.
    2. Adds each banned address to ban filter list -- AddIP().
      1. Maximum of 1024 banned addresses.
      2. Converts dotted-quad notation to an internal filter format -- StringToFilter().
      3. Adds filter record to array ipFilters[].
  4. Initialize the in-QVM dynamic memory pool of 256KB (hey, 64KB sent us to the moon!) -- G_InitMemory().
  5. Start logging if so configured.
  6. Initialize world session information -- G_InitWorldSession().
    1. Resets cvar session
  7. Initialize game entities.
  8. Initialize game clients (players).
  9. Tell the Q3 engine about g_entities, for network engine purposes -- trap_LocateGameData().
  10. Initialize body queue (for sinking corpses, etc.) -- InitBodyQue().
    1. Allocates 8 game entities and reserves them for the body queue.
  11. Initialize item registration -- ClearRegisteredItems().
    1. Clears the array itemRegistered[].
    2. Registers machine gun -- RegisterItem().
      1. Sets itemRegistered[10] to true (offset for machine gun weapon item)
    3. Registers gauntlet -- RegisterItem().
      1. Sets itemRegistered[8] to true (offset for gauntlet weapon item)
  12. Spawn map entities -- SpawnEntitiesFromString().
    1. Enable entities spawning with level.spawning = qtrue.
    2. Parse worldspawn variable, which must be the first such entity in the map file -- SP_worldspawn().
      1. Read a spawn string -- G_SpawnString("classname", "", &s).
        1. Retrieve the next spawn string, complain if the spawn string key doesn't match the first argument (i.e. "classname"), otherwise store the value into the third argument. The second argument provides a default value if no value is specified in the map file.
      2. Complain if the string isn't the worldspawn variable, with a fatal error (game stops).
      3. Set ConfigString CS_GAME_VERSION according to mod.
      4. Set ConfigString CS_LEVEL_START_TIME according to the recorded start time.
      5. Set ConfigString CS_MUSIC (map-specific background music) according to spawn entity -- G_SpawnString("music", "", &s).
      6. Set ConfigString CS_MESSAGE (map-specific message) according to spawn entity -- G_SpawnString("message", "", &s).
      7. Set ConfigString CS_MOTD (server MOTD) according to server cvar sv_motd.
      8. Set cvars g_gravity, g_enableDust, and g_enableBreath according to spawn entities.
      9. Record the existence of the worldspawn entity.
      10. Set warmup time as needed, based on cvar g_doWarmup.
    3. Parse spawn variables -- G_ParseSpawnVars().
      1. Expect an opening brace ({), fail miserably if not found -- trap_GetEntityToken().
      2. Read all key/value pairs.
        1. Read a token as a key -- trap_GetEntityToken().
        2. Stop loop if this token is a closing brace (}).
        3. Read a token as a value -- trap_GetEntityToken().
        4. Complain if this token is a closing brace (}).
        5. Complain if there are now more than 64 spawn variables.
        6. Record spawn variable key.
        7. Record spawn variable value.
    4. Spawn entities from spawn variables -- G_SpawnGEntityFromSpawnVars().
      1. Allocate a game entity -- G_Spawn().
      2. Parse the fields for each spawn variable -- G_ParseField().
        1. ???!
        2. Takes key/value pairs and sets fields in game entity using arcane binary doohackery.
      3. Destroy entity according to game type and gametype spawnflags (notsingle, notteam, notfree).
      4. Destroy entity according to spawn variable gametype and active game type.
      5. Adjust origins(?)
      6. Call assocated game spawn function -- G_CallSpawn().
        1. Complain if entity classname is NULL.
        2. Attempt to spawn as an item -- G_SpawnItem().
          1. Set entity field random according to spawn variable random.
          2. Set entity field wait according to spawn variable wait.
          3. Register this item -- RegisterItem().
          4. Check if item is disabled and don't spawn if so -- G_ItemDisabled().
            1. Returns the value of cvar disable_classname. So to disable quad damage would be /set disable_quaddamage 1.
          5. Record global/master/templated item information.
          6. Set think time and function -- FinishSpawningItem().
            1. Makes items fall to floor. Falling is disabled at spawn time to prevent items spawning within other entities.
          7. Make entity bouncy.
          8. Set powerup fields for powerup items (in particular, speed according to spawn variable noglobalsound).
        3. If the entity didn't spawn as an item, attempt to spawn as a normal entity by finding the classname and calling the associated spawn function -- s->spawn(). (XXX: spawn[] and list of SP_*() functions)
        4. Failing that, complain about lack of spawn function.
      7. Destroy object if no such spawn function exists.
  13. Initialize teams information -- G_FindTeams().
    1. Create a linked list for each team composed of all entities belonging to the team. The team master is the lowest numbered entity that is not a slave.
  14. Broadcast item registration information via ConfigString -- SaveRegisteredItems().
    1. Sets ConfigString CS_ITEMS to a sequence of ASCII 1 and 0 according tot he content of array itemRegistered[]. The information is transmitted to all clients for precaching purposes.
  15. Initialize bot AI -- BotAISetup(), BotAILoadMap(), G_InitBots(). (XXX: Aieeee!)
  16. Various other server-side tweaks.
That was just for game initialization.

vmMain(GAME_SHUTDOWN, restart)
When Q3 wants to shut down the game module, Q3 calls vmMain() with major function GAME_SHUTDOWN, and one additional argument:
  1. restart - ???, affects bot AI initialization only.
In vmMain(), the code immediately branches to G_ShutdownGame() when vmMain() recognizes GAME_SHUTDOWN.

For map changes (as after end of round), the game module is shut down then initialized again with the new map (partly to have a clean slate for the new map's entities). Any data within QVM memory is irretrievably destroyed. Any data that needs to survive across map changes, or even map restarts, must be committed to files (disk) or cvars (Q3 engine memory).

  1. Closes any logs.
  2. Save session data -- G_WriteSessionData().
    1. Save world session data to cvar session, which is just current gametype.
    2. Save each client session data -- G_WriteClientSessionData().
      1. Save to cvar sessionclientnum the information pertaining to client's team, spectator time, spectator state, client being spectated, wins, losses, and teamLeader.
  3. Shutdown bot AI -- BotAIShutdown().
    1. (XXX: yeah, right...)
vmMain(GAME_CLIENT_CONNECT, clientNum, firstTime, isBot)
When a client connects to the server, Q3 calls vmMain() with major function GAME_CLIENT_CONNECT, with arguments:

  1. clientNum - the assigned client number according to the Q3 engine.
  2. firstTime - zero if a persistent connection from a previous game on the same server, non-zero if a fresh connect to the server.
  3. isBot - non-zero if the connecting player is a bot AI.
In vmMain(), code immediated branches to ClientConnect() when vmMain recognizes GAME_CLIENT_CONNECT.

ClientConnect(clientNum, firstTime, isBot)
  1. Get userinfo string from client -- trap_GetUserinfo().
  2. Check if client's IP address is banned -- G_FilterPacket().
    1. Check client's IP address against each entry in array ipFilters[].
  3. Check for password.
  4. Read/initialize session data -- G_InitSessionData().
    1. Figure out a team to join; autojoin, spectator, predetermined join (same team as last game), waiting-in-line.
  5. Set various flags if bot.
  6. Enact effects of client userinfo -- ClientUserinfoChanged().
    1. Get userinfo from client (again).
    2. Enforce sanity-check on name -- Info_Validate().
    3. Check if local client (non-networked).
    4. Check if client wants item prediction.
    5. Set client name.
    6. Set client team.
    7. Announce any during-play name change (a client that just connected technically had a name-change from nothing to something, but this particular name change isn't announced).
    8. Set max health (influence by handicap value).
    9. Set player model.
    10. Select skin according to team (if applicable).
    11. Set teamtask.
    12. Arrange a subset of userinfo into a ConfigString, stored into a CS_* slot based on client number.
  7. Announce team joining -- BroadcastTeamChange().
    1. Tells everyone in the game about someone joining/changing teams.
  8. Calculate client ranks -- CalculateRanks().
    1. (XXX: lotsa math and sorting...)
vmMain(GAME_CLIENT_THINK, clientNum)
Called (frequency unknown) to handle player input to the game and player movement within the game, with one additional argument:

  1. clientNum - the client number of which to handle input.
In vmMain(), the code immediately branches to ClientThink() when vmMain recognizes GAME_CLIENT_THINK.

  1. Retrieve user input -- trap_GetUsercmd().
  2. Mark the time input was received.
  3. If not a bot, do non-bot client think process -- ClientThink_real().
  1. Check if client is still in Connecting phase. Return from function if so.
  2. Record time of retrieval of usercmd (user input).
  3. Basic timestamp sanity check to squelch speedup cheats.
  4. Sanity checks on cvar pmove_msec.
  5. Timestamp checks for cvar pmove_fixed.
  6. Check for intermission -- ClientIntermissionThink().
    1. Disable talk balloons and weapons.
    2. Toggle READY status each time attack button is pressed (changes from low to high).
  7. If client is spectator, do spectator thinking and return -- SpectatorThink().
    1. If not following, do almost-normal movements and some triggering.
    2. Start following or cycle to next spectatee(?) if attack button is pressed.
  8. Check for inactivity timer -- ClientInactivityTimer().
    1. If cvar g_inactivity is unset or 0, set inactivity timer to 1 minute, but don't do any actual inactivity kicking. This gives some leeway to all players during the instant when admin decides to actually set inactivity timer during gameplay (otherwise the time since game start counts as inactivity at that instant).
    2. Otherwise check if client is moving or doing some action, and push forward the inactivity timestamp.
    3. Do not kick local player(s).
    4. Otherwise check if current time surpassed inactivity timestamp and kick.
    5. Otherwise see if time to send 10-second warning.
  9. Clear overhead reward icons if time expired.
  10. Check movement style (noclip, dead, normal).
  11. Inherit gravity value.
  12. Set client speed.
  13. Increase client speed due to any powerups (haste, scout).
  14. Prepare for pmove (predictable movement) calculation.
  15. Check for gauntlet hit for the gauntlet-hit animation if attack button is held with gauntlet, store into pm.gauntletHit -- CheckGauntletAttack().
    1. See if any damageable object is within 32 units of player.
    2. Create blood splatters.
    3. Increase damage due to powerups (Quad, Doubler).
    4. Apply damage to victim.
    5. Return true if gauntlet made a hit, otherwise return false.
  16. Check for gesturing and set animation if needed.
  17. Check for invulnerability powerup (Team Arena only).
  18. Make pmove calculations -- Pmove().
  19. Save results of pmove into client structs.
  20. Smooth out if cvar g_smoothClients is non-zero -- BG_PlayerStateToEntityState().
  21. Snap various vectors (round components to nearest integer).
  22. Execute client events -- ClientEvents().
    1. Retrieve next client event.
    2. Check fall damages (EV_FALL_MEDIUM, EV_FALL_FAR):
      1. Check that fall damage is being applied to a player.
      2. Check if fall damage is disabled (cvar g_dmflags & DF_NO_FALLING (8)).
      3. Set damage amount, 10 for far, 5 for medium.
      4. Set pain debounce time (XXX: what??)
      5. Apply damage to player -- G_Damage().
    3. Check weapon firing (EV_FIRE_WEAPON):
      1. Fire weapon -- FireWeapon().
        1. (XXX: Aieee!)
    4. Check using handheld teleporter (EV_USE_ITEM1):
      1. Drop CTF flag(s).
      2. Select a random spawn point -- SelectSpawnPoint().
      3. Teleport the player to the new point -- TeleportPlayer().
        1. Create appropriate teleport effects at old and new points.
        2. Unlink player from game -- trap_UnlinkEntity().
        3. Place player at new spawn point with some altered properties (speed, angle).
        4. Telefrag.
        5. Update from pmove calculations -- BG_PlayerStateToEntityState().
        6. Link player back into game.
    5. Check using medkit (EV_USE_ITEM2):
      1. Sets health to player's maximum health plus 25; the maximum health may vary according to handicap. The medkit is used up (removed) elsewhere.
    6. Check using kamikaze (Team Arena only, EV_USE_ITEM3):
    7. Check using portal(???) (Team Arena only, EV_USE_ITEM4):
    8. Check using invulnerability (Team Arena only, EV_USE_ITEM5):
    9. Ignore other event types.
  23. Link entity into the world and PVS -- trap_LinkEntity().
  24. Trigger any... triggers -- G_TouchTriggers().
    1. Ensure the triggerer(?) is a living player.
    2. Find all entities reaching into player's space (bounding box), store list into array touch[].
    3. For each touched entity:
      1. Ignore if not a trigger.
      2. If spectating, ignore certain triggers.
      3. If an item, ignore (since item-touching is handled elsewhere) -- BG_PlayerTouchesItem()
      4. Otherwise check that the player really is in contact with this entity -- trap_EntityContact().
      5. If this entity has a touch() function, call it -- hit->touch().
      6. If client has a touch() function, call it -- ent->touch().
    4. Check for jumppad.
  25. Some bot AI checks -- BotTestAAS().
    1. (XXX: foo)
  26. Interact with other objects/entities -- ClientImpacts().
    1. For each entity the player is touching (pm->numtouch, pm->touchents[]):
      1. Run the player's touch() function, if it exists.
      2. Run the other entity's touch() function, if it exists.
  27. Save results of triggers and events.
  28. Swap/latch button actions, helps check for button press/release.
  29. If player is dead, check for respawning, based on button press or cvar g_forcerespawn -- respawn().
    1. Create a corpse in body queue -- CopyToBodyQue().
      1. Selects next spot in queue, overwriting as it goes.
      2. Unlink the body queue entry -- trap_UnlinkEntity().
      3. Copy properties of corpse, unset inapplicable properties (such as powerups).
      4. Set to last frame of death animation, to prevent looped animation.
      5. Set movement properties.
      6. Link corpse into game -- trap_LinkEntity().
    2. Spawn the player -- ClientSpawn().
      1. If a spectator, just set a spawn point for spectators -- SelectSpectatorSpawnPoint().
        1. Spawn at intermission location -- FindIntermissionPoint().
          1. Look for info_player_intermission entity.
          2. Set viewing angles, towards a target if specified.
          3. If intermission entity does not exist, the map creator is a moron:
            1. Select a single-player spawn point as if normally playing, to use as intermission entity -- SelectSpawnPoint().
      2. If a team game, choose spawn point for team games -- SelectCTFSpawnPoint().
        1. Choose a team spawn point -- SelectRandomTeamSpawnPoint().
          1. If just joining the team, look for spawn point named team_CTF_redplayer or team_CTF_blueplayer according to team.
          2. If not just joining (as in having been just fragged and respawned), look for spawn point named team_CTF_redspawn or team_CTF_bluespawn.
          3. Look through each spawn point of the appropriate name:
          4. If the spot would cause a telefrag, choose next spawn point. Otherwise record the location in array spots[].
          5. If a telefrag-less spot doesn't exist, just use first spawn point. Otherwise, choose a random spawn point from spots[].
          6. Return the spawn point information.
        2. If there is no team spawn point, choose a single-player (info_player_deathmatch) spawnpoint -- SelectSpawnPoint().
      3. Otherwise choose spawn point single-player style:
        1. If spawned for the first time, choose a good-looking spot -- SelectInitialSpawnPoint().
          1. Look for a spawn point (info_player_deathmatch) with the initial flag set (0x01).
          2. If no such special spawn point exists, or if the initial spawn point would cause telefrag, choose spawn point the normal way with SelectSpawnPoint().
        2. If not spawned for first time, select a spawn point far from death point -- SelectSpawnPoint().
          1. Just calls SelectRandomFurthestSpawnPoint().
            1. Look through all info_player_deathmatch entities:
              1. Check if telefrag would occur, choose another spot if so -- SpotWouldTelefrag().
                1. Check if any players occupy some of the space the would-be-spawned player would occupy.
              2. Record distance of this spawn point from the avoidPoint (i.e. where player's death occurred) into list_dist and list_spot using [reverse] insertion sort.
              3. If no spawn point can avoid telefrag, choose first spawn point (info_player_deathmatch).
              4. Now that list_dist and list_spot are sorted in decreasing order of distances (furthest first), choose a random spot from the first half of the list.
              5. Return selected spawn point location.
        3. Juggle spawn point according to nobot or nohuman flags for spawn points.
        4. Initialize various client flags, fields, and records.
        5. Provide client with gauntlet and machine gun, with some ammo.
        6. Set spawn-time health.
        7. Move client position to spawn point -- G_SetOrigin().
        8. Set client view angle -- SetClientViewAngle().
          1. Sets angle vectors based on spawn point variables (XXX: which one?).
        9. Telefrag as needed -- G_KillBox().
        10. Link client into game -- trap_LinkEntity().
        11. Bring up the base weapon, the machine gun.
        12. Prevent full-tilt running at spawn time.
        13. Reset inactivity timer.
        14. Set initial animation.
        15. If on intermission, do intermission stuff -- MoveClientToIntermission().
          1. If spectator, stop following -- StopFollowing().
            1. Clears spectator-following fields.
          2. Move to intermission location (set by spawn variable info_player_intermission).
          3. Set movement type to PM_INTERMISSION.
          4. Clear powerups, attributes, flags, sounds, events, etc.
        16. Otherwise, trip triggers at spawn point, then select highest player weapon available (which may not be machine gun) -- G_UseTargets().
        17. Drop player to the floor by gravity -- ClientThink(). No loop as needs to respawn has been cleared by now.
    3. Add teleportation effect at spawn point -- G_TempEntity().
    4. Link client into game (again, just to be sure).
    5. Run end-of-frame stuff -- ClientEndFrame.
    6. Entity state stuff -- BG_PlayerStateToEntityState().
  30. Run once-per-second client actions -- ClientTimerActions().
    1. Check if one-second period has passed.
    2. Increase health due to regeneration powerup, rate is influenced by any excessive health.
    3. Tick down excessive health (health greater than maxHealth) if no regeneration powerup.
    4. Tick down excessive armor (armor greater than maxArmor).
    5. Tick up ammo due to ammo-regen powerup (Team Arena only).
  1. Check for quad damage powerup.
  2. Accumulate accuracy statistics.
  3. Determine firing direction.
  4. Fire according to weapon in use:
    • gauntlet -- Weapon_Gauntlet()
      1. (Non-operation. Nothing. Nada. Zilch.)
      2. (Checking for gauntlet hit is done in CheckGauntletHit()).
    • lightning gun -- Weapon_LightningFire()
      1. Check for anything in front for 768 units (LIGHTNING_RANGE)
      2. Bounce off any invulerability spheres (Team Arena only)
      3. Apply damage to victim.
      4. Cause visual and audio effects.
      5. Record accuracy stats.
    • shotgun -- weapon_supershotgun_fire()
      1. Seed RNG for shotgun pattern.
      2. Fire shotgun pellets -- ShotgunPattern().
        1. For 11 (DEFAULT_SHOTGUN_COUNT) pellets:
          1. Fudge local up and right by random positive or negative values.
          2. Check for damage dealt by pellet.
          3. Record accuracy stats.
    • machine gun -- Bullet_Fire()
      1. Fudge the aim direction by a bit (for random spread).
      2. Search along fudged direction for a victim (or wall).
      3. Bounce off invulnerability spheres (Team Arena only).
      4. Cause bullet-hit effect.
      5. Deal damage -- G_Damage().
    • grenade launcher -- weapon_grenadelauncher_fire()
      1. Fudge the aim direction upwards a little bit for loft.
      2. Create and launch a grenade -- fire_grenade()
        1. Allocate a game entity -- G_Spawn().
        2. Set fields for a grenade entity.
        3. Snap velocity vectors (components rounded to integers).
      3. Increase grenade damage by any quad damage.
    • rocket launcher -- Weapon_RocketLauncher_fire()
      1. Create and launch a rocket -- fire_rocket()
        1. Allocate a game entity -- G_Spawn().
        2. Set fields for a rocket entity.
        3. Snap velocity vectors (components rounded to integers).
      2. Increase rocket damage by any quad damage.
    • plasma gun -- Weapon_PlasmaGun_Fire()
      1. Create and launch a plasma round -- fire_plasma()
        1. Allocate a game entity -- G_Spawn().
        2. Set fields for a plasma entity.
        3. Snap velocity vectors (components rounded to integers).
      2. Increase plasma damage by any quad damage.
    • railgun -- weapon_railgun_fire()
      1. Railgun range is 8192 units.
      2. Bounce off invulnerability (Team Arena only).
      3. Record accuracy stats.
      4. Damage up to 4 (MAX_RAIL_HITS) players, passing through three.
      5. Rail slug stops at walls or other impenetrable obstacles.
      6. Create rail beam/trail effect.
      7. Fudge locations a bit for visual effect (so rail appears to start from gun location)
      8. Check for Impressive (two consecutive railgun hits without a miss) reward.
    • BFG10K -- BFG_Fire()
      1. Create and launch a BFG round -- fire_bfg()
        1. Allocate a game entity -- G_Spawn().
        2. Set fields for a BFG entity.
        3. Snap velocity vectors (components rounded to integers).
      2. Increase BFG damage by any quad damage.
    • grappling hook -- Weapon_GrapplingHook_Fire()
    • nailgun (Team Arena only) -- Weapon_Nailgun_Fire()
    • proximity mine launcher (Team Arena only) -- weapon_proxlauncher_fire()
    • chaingun (Team Arena only) -- Bullet_Fire()
TODO: Pmove()

When a client user's info has changed (name, player model, handicap, etc.), the Q3 server is notified and reacts with a call to vmMain() with major function GAME_CLIENT_USERINFO_CHANGED, with one argument:

  1. clientNum - the client number of the client that changed its info.
Immediate branch to ClientUserinfoChanged().

  1. Note client number and client object/entity.
  2. Retrieve client userinfo -- trap_GetUserinfo().
  3. Check for invalid string -- Info_Validate().
  4. Check if local client (key ip) -- Info_ValueForKey().
  5. Check item prediction (key cg_predictItems).
  6. Set name (key name).
  7. Announce if in-game name change.
  8. Set max health according to handicap (key handicap).
  9. Set player model (keys model and hmodel, or team_model and team_hmodel).
  10. If a bot, join team a short time later.
  11. Otherwise just set team (or spec).
  12. Set model skin according to team.
  13. Determine if client wants teamoverlay info (key teamoverlay).
  14. Set team task (key teamtask) and leader.
  15. Set colors (keys color, g_redteam, g_blueteam).
  16. Construct a ConfigString from a subset of userinfo, to broadcast to all clients.
When client disconnects (voluntary disconnect, kicked, dropped, etc.). Arguments:
  1. clientNum - the client number of the client just disconnected.
Immediate branch to ClientDisconnect().

  1. Check if kicking a still-unspawned bot -- G_RemoveQueuedBotBegin().
  2. If any spectator is following this client, stop their following -- StopFollowing().
  3. Create teleport-out effect.
  4. Drop any powerups client had -- TossClientItems().
  5. If a tournament game, award remaining player with a win.
  6. Unlink the client -- trap_UnlinkEntity().
  7. Mark ex-client as being disconnected and mark its slot as unused.
  8. Clear associated ConfigString.
  9. Recalculate ranks -- CalculateRanks().
  10. If a bot AI, shutdown AI -- BotAIShutdownClient().
vmMain(GAME_CLIENT_BEGIN, clientNum)
When client is ready to play. This occurs when client has finished connecting and loading, or has switched teams, or has continued in from the previous map. Does not occur on respawns (why should the Q3 engine know about respawns+teleport? The game engine does, though). Arguments:

  1. clientNum - client number.
Immediate branch to ClientBegin().

  1. Note client number and client entity/record.
  2. Unlink client entity -- trap_UnlinkEntity().
  3. Initialize client game entity -- G_InitGentity().
    1. Clear and initialize some critical game entity fields.
  4. Initialize client fields.
  5. Select a spawn point -- ClientSpawn().
  6. Announce the player joining the game, if not tournament mode.
  7. Recalculate ranks -- CalculateRanks().
vmMain(GAME_CLIENT_COMMAND, clientNum)
When client sends a command to the server. This usually occurs when the client-end doesn't recognize a console command from the user, and so passes off the command to the server in hopes the server understands it. Arguments:

  1. clientNum - client number
Immediate branch to ClientCommand().

  1. Check client validity.
  2. Retrieve command name from string -- trap_Argv().
  3. Check against known command names:
    • say
    • say_team
    • tell
    • vsay
    • vsay_team
    • vtell
    • vosay
    • vosay_team
    • votell
    • vtaunt
    • score
    • (At intermission, the following commands are ignored, and instead are transmitted as global chat)
    • give
    • god
    • notarget
    • noclip
    • kill
    • teamtask
    • levelshot
    • follow
    • follownext
    • followprev
    • team
    • where
    • callvote
    • vote
    • callteamvote
    • teamvote
    • gc
    • setviewpos
    • stats
  4. Unrecognized commands return an error message to the originating client.
vmMain(GAME_RUN_FRAME, levelTime)
Called by Q3 each server frame (period determined by cvar sv_fps, default value of 20 fps (or 50 ms/frame)) to advance all non-player objects. Arguments:

  1. levelTime - the elapsed time on the level as reported by Q3 engine (this is where level.time comes from!).
Immediate branch to G_RunFrame().


Check if waiting for level to restart.
  1. Remember previous levelTime.
  2. Store current levelTime.
  3. Calculate the time difference.
  4. Update all cvar changes -- G_UpdateCvars().
  5. Record the current timestamp (not levelTime) -- trap_Milliseconds().
  6. For all allocated game entities:
    1. Ignore unused entity.
    2. Clear out old events.
    3. Free entity slot if no longer needed -- G_FreeEntity().
    4. Unlink entity if needed -- trap_UnlinkEntity().
    5. Ignore temporary entity (they don't think).
    6. Ignore unlinked entity.
    7. If a missile type, run missile think -- G_RunMissile().
      1. Find missile's current position -- BG_EvaluateTrajectory().
      2. Do checks for what the missile will or will not pass through.
      3. See if missile passed through anything noteworthy -- trap_Trace().
      4. Check if missile is stuck in anything, otherwise just keep moving forward.
      5. Link missile into the game (XXX: when was it unlinked?) -- trap_LinkEntity().
      6. If missile collided with something:
        1. Just free up the entity if hit a SURF_NOIMPACT surface (such as sky or a black hole).
        2. Otherwise do missile impact stuff -- G_MissileImpact().
          1. Check for bouncing -- G_BounceMissile()
            1. (vector math for ricochet/bounce effect)
          2. Deal impact damage, if applicable.
          3. Stick proximity mine to victim (Team Arena only)
          4. Latch grappling hook and start pulling, if applicable.
          5. Create any explosion effect -- G_AddEvent().
          6. Snap vectors.
          7. Deal any splash damage -- G_RadiusDamage().
            1. Sanity check on damage radius, create a damage bounding box.
            2. Find entities within damage bounding box and save to array entityList[] -- trap_EntitiesInBox().
            3. For all the entities in entityList[]:
              1. Ignore if entity marked for ignore.
              2. Ignore if entity cannot be damaged.
              3. See if entity is actually within damage radius.
              4. Get distance from explosion center to entity center.
              5. Calculate damage based on this distance.
              6. Deal damage and knockback, while recording accuracy stats.
          8. Link missile into the world (XXX: again?) -- trap_LinkEntity().
      7. If missile hasn't exploded yet, run its think function -- G_RunThink().
    8. If an item type that's bouncing, run item think -- G_RunItem().
      1. Check if item is in free fall.
      2. Check if item has stationary trajectory, if so then just run G_RunThink() and return.
      3. Get current position of item.
      4. See if it hit/landed on anything.
      5. Run think function -- G_RunThink().
      6. If in a nodrop brush, remove entity from game.
      7. Cause item to bounce -- G_BounceItem().
        1. (lots of vector math)
    9. If a mover, run mover think -- G_RunMover().
      1. Check if a team slave, return if so (team captain handles all activities).
      2. If not stationary, run mover -- G_MoverTeam().
        1. Make sure all team members (other movers in a chain) are able to move -- G_MoverPush().
          1. Establish boundaries of mover and entire movement area covered.
          2. Unlink mover temporarily -- trap_UnlinkEntity().
          3. Find entities within mover's movement area and store into list listedEntities[] -- trap_EntitiesInBox().
          4. Move pusher to final position.
          5. Link mover back into the game -- trap_LinkEntity().
          6. For each entity in listedEntities[]:
            1. Try pushing proxmine (Team Arena only).
            2. Ignore entity if not an item nor a player.
            3. Move entities standing on mover, and ensure it doesn't try to occupy the same space as the mover -- G_TestEntityPosition().
            4. Push entity -- G_TryPushingEntity().
            5. Any entites that bob (such as items) get killed if stuck inside a mover position (such as between doors), move on to next pushed object.
            6. Otherwise move entities back, since this is an obstacle.
        2. Restore old position(s) if any mover member can't move.
        3. Run blocked() function for any mover member that couldn't move -- part->blocked().
        4. Otherwise calculate and set new positions.
        5. If a mover member reachd one end of its movement, call its reached() function -- part->reached();.
      3. Run entity-specific think function -- G_RunThunk().
    10. If a client, run client think -- G_RunClient().
      1. Check if a bot AI, return if so.
      2. Record levelTime
      3. Call ClientThink_real().
    11. Otherwise, just run think -- G_RunThink().
      1. Check if there is any thinktime.
      2. Check if levelTime exceeded thinktime, return if not.
      3. Disable nextthink and run entity's think -- ent->think().
  7. Record current timestamp -- trap_Milliseconds().
  8. (Time elapsed during run cycle can be calculated (but isn't)).
  9. Do final fixups on players -- ClientEndFrame().
  10. Check for tournament restart -- CheckTournament().
  11. Check for end of level -- CheckExitRules().
    1. If still intermission time, check that all non-bot clients are READY to leave intermission, return if not ready to exit level/map -- CheckIntermissionExit().
    2. Check for queued intermission (???)
      1. Ignore if queue time not yet exceeded.
      2. Unqueue intermission.
      3. Begin intermission -- BeginIntermission().
        1. Return if intermission already in progress.
        2. For tournament mode, adjust tournament scores -- AdjustTournamentScores().
          1. Update number of wins or losses for the two competitors.
        3. Record the current levelTime as the intermission time.
        4. Find the intermission location -- FindIntermissionPoint().
        5. If single-player game:
          1. Update tournament information -- UpdateTournamentInfo()
            1. Take note of (the only) player's record/slot/entity.
            2. Calculate ranks -- CalculateRanks().
            3. Record postgame data?
            4. Calculate accuracy percentage.
            5. Check for Perfect reward.
            6. Record rewards information.
          2. show the victory podium -- SpawnModelsOnVictoryPads()
            1. Spawn the podium -- SpawnPodium().
              1. Create podium entity.
              2. Place podium in front of intermission view.
            2. Place the top three players in their appropriate places -- SpawnModelOnVictoryPad().
              1. Copy entity properties of the player into a new game entity.
              2. Set entity location relative to podium and intermission viewpoint.
        6. Move all clients to intermission point -- MoveClientToIntermission()
        7. Send current scores to all clients -- SendScoreboardMessageToAllClients().
          1. For each client:
            1. Send scoreboard info -- DeathmatchScoreboardMessage()
              1. For all clients:
                1. Construct string based on client number, frags, ping time, play time, score flags, powerups, rewards, and captures.
              2. Send this huge-ass string, along with blue team score and red team score, to client -- SendServerCommand().
  12. Update team status -- CheckTeamStatus().
    1. Check if time to do team location update.
    2. For all clients:
      1. Ignore clients still connecting.
      2. Determine team.
      3. Store team location information into client record.
    3. For all clients (again):
      1. Ignore clients still connecting.
      2. Determine team.
      3. Transmit team location information to client -- TeamplayInfoMessage().
        1. Return if client has no (doesn't want?) teaminfo.
        2. Extract top eight teammates on player's team.
        3. Sort the eight teammates by client number.
        4. For all clients or the first 32 (TEAM_MAXOVERLAY) (whichever is less):
          1. Ignore if this client is not in use, or is not on the same team.
          2. Store client's health and armor.
          3. Construct a string based on the client's list location (cycle in this particular loop), location, health, armor, current weapon, and active powerup.
          4. Truncate constructed string if too long (8192 bytes).
        5. Transmit the constructed string to the client -- trap_SendServerCommand().
  13. Check global votes -- CheckVote().
    1. If vote string is to be executed and it's time to execute, execute the string as a command -- trap_SendConsoleCommand().
    2. Return if no vote in progress.
    3. Check if vote time expired, report vote having failed if so.
    4. Otherwise check for votes:
      1. If Yes has simple majority, report success and execute vote string within 3 seconds.
      2. If No has simple majority, report failure.
      3. Otherwise keep waiting for a majority one way or another.
    5. Update ConfigString CS_VOTE_TIME accordingly.
  14. Check team votes -- CheckTeamVote().
    1. Determine which team to check (blue or red).
    2. Return if no vote is in progress.
    3. If voting time limit is exceeded, report failure to... everyone(!).
    4. Otherwise:
      1. Check if Yes votes has simple majority.
        1. If the vote was to change leader, set new team leader -- SetLeader().
        2. Otherwise execute vote string as a command -- trap_SendConsoleCommand().
      2. Check if No votes has simple majority; cause vote to fail if so.
      3. Otherwise keep waiting for a majority vote.
    5. Set CS_TEAMVOTE_TIME (+1 for blue) accordingly.
  15. Check on changes in cvars -- CheckCvars().
    1. Do nothing (return) if cvar g_password has not changed.
    2. If cvar g_password is set, and is not none, set g_needpass to 1.
    3. Otherwise, set cvar g_needpass to 0.
  16. Dump entities list if cvar g_listEntity is non-zero.
When a console command is entered directly to the server from the server console (admin commands). No arguments. Immediate branch to ConsoleCommand().

  1. Retrieve command name -- trap_Argv().
  2. Check command:
    1. entitylist
    2. forceteam
    3. game_memory
    4. addbot
    5. botlist
    6. abort_podium
    7. addip
    8. removeip
    9. listip
  3. If command is not found and server is running in dedicated mode (cvar dedicated is 1 or 2), repeat the command line as global chat text from console.
Created 2002.08.26
Updated 2002.09.16 - re-wordings suggested by members of Quake 3 World Modifications Programming Forum (MPF).
Updated 2003.11.10 - corrections sent in by cyrri.
Updated 2011.07.11 - change of contact e-mail.

~ Phaethon
Last edited:


Meme Tier VIP
Dank Tier Donator
Apr 1, 2015
I'm trying to find the address for the rounds in the zombies, I find it, but every time I close the game, the address changes.

I tried with pointer scanning but end up with an empty table.

How do I go about this?
  • Haha
Reactions: SMILE7


edgy 12 y/o
Escobar Tier VIP
Fleep Tier Donator
Dec 22, 2013
What exactly are you looking for? The address which stores the current round you're in? Anyway what pointer scanning settings are you using?
Have you tried to find the pointer path manually using the CE debugger?


Meme Tier VIP
Dank Tier Donator
Apr 1, 2015
What exactly are you looking for? The address which stores the current round you're in? Anyway what pointer scanning settings are you using?
Have you tried to find the pointer path manually using the CE debugger?
Yeah, the address that stores the current round. I only tried the default settings and I didn't try to find the path manually


Escobar Tier VIP
Trump Tier Donator
Jun 25, 2014
searching manually is 100% reliable friendo


Meme Tier VIP
Dank Tier Donator
Apr 1, 2015
do video 2, use the little template in the thread for backtracing pointer chains manually, will be very helpful
Video Tutorial - Cheat Engine 6.7 Tutorial Video Guide
I'm struggling to attach the CE debugger to the game. Windows debugger doesn't even work, it outputs the error 87 when trying to attach it directly to the process. When I follow your method;

Find what writes to this address --> Yes to attach the debugger

I get the "I couldn't attach the debugger to this process. You could try to open the process using the processpicker.... bla bla"

With the VEH debugger, I manage to attach it but the game crashes when I try to find out what writes to that address
Last edited:


Meme Tier VIP
Dank Tier Donator
Apr 1, 2015
It has antidebug, check the anticheat section for more information
I did some researching on it, seems way to complicated for my level of knowledge. I'll give it a shot after some time.
Seems pretty weird for such an old game to have any anticheat especially in single player


PPC Haxor
Trump Tier Donator
Dank Tier Donator
Full Member
Jul 10, 2020
Looks great so far. Thanks for taking the time to documenting it. It would also be good to mention the leaked PDB's for COD 4/Ghosts etc. They are really useful for understanding. Also sounds weird, but looking at quake and wolfenstein in ida with symbols can be a great help as you get used to what certain functions look like and how compiler optimisation affects them and I find it easier to navigate than hundreds of source files but if i can think of anything specific ill chuck you a message.
  • Like
Reactions: Rake


PPC Haxor
Trump Tier Donator
Dank Tier Donator
Full Member
Jul 10, 2020
Quake/Call of Duty Prefixes
Quake and Call of Duty name functions and variables based this this system.
Call of Duty added a bunch and sometimes will be inconsistent.

Client Sided
CLClient LocalHandles anything to do with the local client.
CGClient GameHandles anything to do with handing the game logic on the client.
RRenderDoes rendering stuff by adding to a render queue.
RBRenderer BackendPretty much the thing that actually processes and renders the queue.
SNDSoundIt does sound what more do you need?
UIUser InterfaceDoes things related to the UI like drawing text and usually use virtual screen space of 640 x 480.
MenuMenuHandles the logic of the ingame menus.
SCRScreen/Screen RefreshSomething to do with "refreshing the screen".

Server Sided
ClientClientHandles the logic for all client entities.
GGameHandles general game logic.
GScrGame ScriptUsually the bindings for the funtions used in the VM.
ClientScr/CScrClient ScriptUsually binding for player related functions in the VM.
ScrScriptAnything to do with the Script VM.
HudElemHud ElementNetworked UI elements that get rendered on the client.
CmdCommandUsually the command handlers for commands sent from the client like noclip, godmode etc.
SVServerServer related things.

BGBoth GamesAny code that is shared between the client and the server.
ConConsoleHandles the ingame console.
ComCommonCommon functions used by the server and the client.
Dvar/CvarDeveloper/Console VariableHandles the console variables system.
NETNetworkHandles the actual sending of packets.
MSGMessageHandles the crafting of packets similar to a stream.
SysSystemPlatform specific code.
QQuake??Usually different implementations of libc functions.
FSFile SystemHandles the reading/writing of files etc.
INInputHandles user button input.
VMVirtual MachineHandles executing and loading scripts.
Last edited:
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Similar threads

Community Mods