Next Previous Contents

6. Interfacing the Game Subsystem

The Game Subsystem as a library is essentially a passive component. It needs to be called and executed by a program/application. This could be any application linked against the DLL, including your test programs. It is usually the Q2 game server process that interfaces the Game Subsystem and executes it at predefined intervals.

6.1 Game Subsystem API

Exporting/retrieving the Game API

Here is the definition of game.h::GetGameApi, the function that provides the main loadtime interface between server process and Game DLL :

game_export_t *GetGameApi(game_import_t *import);

The structs are mostly code pointers although there are a very small number of data pointers too. They changed during development, and between game versions.

GetGameApi is used at startup to exchange info between the server and the game dll. It is called by the server passing in a tables of functions to the dll (the game imports). It returns a struct o' pointers back to the server, the game exports.  Yes, the prototype is basically useless to you until you know what's in the structs.

The dll is linked at a fixed base address. You really, positively must leave it that way. If you don't the save/load game won't work for you.

Import from Main Engine (Server)

The main engine, as part of the server, provides a set of functions and global variables to the Game Subsystem. The Game Subsystem can refer to these and call them, but they can not be changed or replaced. The imported functions are listed in game.h::game_import_t. As the server cannot be changed, and as there is no override mechanism, this part of the game is fixed. This includes, among other things, the client side prediction mechanism.

The majority of the import functions relate to former QuakeC builtin "standard functions", like traceline(), etc.

The following is a list of the Quake2 engine internal commands which can be, or have to be referenced by the Game DLL. The functions are effectively referenced as gi.function.

Text output Commands


The gi.bprintf (print_level, text, variables) function sends message to all players. print_level gives the message priority, and can be:


The gi.dprintf (text, variables) function emits debug messages. Debug cvar allows you to view these.


The gi.cprintf (entity, print_level, text, variables) function sends message to only one entity. Again, print_level sets message priority (see gi.bprintf above for values).


The gi.centerprintf (entity, text, variables) function sends message to one entity and displays message in centre of screen.

Audio Commands


The gi.soundindex (sound_name) function will cache a sound during spawning, after that it simply returns the index which refers to that sound. The name is both string identifier of the sound, and the filename in the PACK file, minus the "sounds/" prefix. You say gi.soundindex("misc/w_pkup.wav") to refer to the file/lump "sounds/misc/w_pkup.wav".


The gi.sound (entity, channel, sound_index, volume, attenuation, time_ofs) function generates sound centered on a given entity. The channel parameter specifies the sort of sound - a second sound of the same sort will overwrite the first. The sound to play is given by sound_index, which is retrieved using gi.soundindex. The volume is between 0 and 1. The attenuation controls how far away sound can be heard, effectively. The time_ofs might be a delay time before playing the sound ???not yet found anything other than 0.

The channel parameter can be:

These can be OR'ed with optional flags: The attenuation control parameter, which hints on how to decrease the sound volume by distance, can be:


The gi.positioned_sound (origin, entity, sound_index, volume, attenuation, time_ofs) function is similar to gi.sound. See there (above) for most of settings.

The additional origin sets position for sound to come from relative to level origin (NOT entity origin), e.g. to avoid sending entities to the client that are usually invisible. You do not link (and send) the entitiy, you just tell the client to place a sound at its position, without the client ever to hear about the entitiy as such.

Model Stuff


The gi.modelindex (model_filename) function is handling cacheing/retrieval: during spawning it caches the model, after that it simply returns the index which refers to that model.


The gi.setmodel (entity, model_filename) sets the entities' model to model_filename.

Icon Stuff


Another cache/retrieval function, gi.imageindex (image_filename). Is used to precache the icon during initialisation, and reference it thereafter.

Movement clipping


The gi.trace (point1, vector1, vector2, point2, ignore, mask) function traces a box (given by min/max coordinates in the two vectors) from point1 to point2, ignoring entity ignore, stoping if it hits an object of type specified in mask.

The vector1, vector2 set the box which will do the tracing - if NULL, then a line is used instead (clipping a ray, e.g. a laser beam or instant hit bullet shot).

The function returns a pointer to a struct of type trace_t which contains these components

The mask can be one (or more) of contents values (again see gi.pointcontents) or can be one of:


gi.AreasConnected (int areanum1, int areanum2) checks if two areas are connected - used to check whether a monster can hear a sound or not. Connected thus relateds to the PHS processing, which is essentially a runtime floodfill of an adjacency graph each time an areaportal opens or closes.

It presumably returns 1 if the two areas are connected, 0 else.


gi.pointcontents (point) returns the content of the given point, will be one of:


gi.BoxEdicts(mins, maxs, entity_list, maxcount, areatype) generates a list of all the entities contained in a certain axial box. The mins, maxs vectors define the box (relative to level origin).

The function returns number of entities found.


gi.SetAreaPortalState (poral_num, open) is used to (re)set portal when a door opens or closes (e.g. to prevent sound detection through closed doors).


The gi.inPHS (point1, point2) function checks to see if point2 is in the Potentially Hearable Set of point1. The potentially hearable set (PHS) is a PVS-like data structure used by Quake II to determine whether there is any unoccluded path connecting two points. In contrast to PVS, which determines visibility and lines of sight, no direct ray tracing is done.

Note that if point2 is in the PHS of point1, then point1 will also be in the PHS of point2, hence the first law of Q2 sound propagation: if I can hear you, then you can hear me.


The gi.inPVS (point1, point2) checks to see if point2 is in the Potentially Visible Set of point1. The PVS is the same concept as a PHS, except it tracks sight instead of sound. Internally (in the engine) ther a vast differences. PVS is computed at MAP compile time, is static and could be checked by clipping the LOS against the BSP. It can thus also be improved (made smaller) by runtime checks. Sound does not require direct paths, and has to be handled at runtime (by a floodfill on each areaportal change).


gi.linkentity(entity) links entity into the world's list of active entities, so that it is sent to the client and e.g. used for client ???and server side collision detection etc. It must be re-linked if its size, position or solidity changes, ???to force client side updates.


gi.unlinkentity stops entity from interacting with the world, and must be done before deleting it.

Console Stuff


gi.argc() returns the number of arguments on the console.


Iterator gi.args() returns the next entry on the console as a string.


gi.argv(index) returns the argument number index from the console (command is arg 0).


gi.AddCommandString(text) adds a command to the server console, as if it had been typed.


gi.cvar(variable_name, value, flags) declares and sets the variable to the value, and sets flags :

Teh function returns the variable just set. Note that this is only used within the initialisation function.


gi.cvar_set(variable_name, value) sets the variable to value, returns the ???variable just set.


gi.cvar_forceset(variable_name, value) sets the variable to value. It returns the variable just set, and ???forces the value setting.



Send the prepared data to a single client by UDP, calling by gi.unicast(entity, reliable). The reliable flag enables the internal acknowledging mechanism based on sequence numbers.


gi.multicast(origin, to) sends prepared data (including origin) to :

As some info sent to (nearly all) clients it might as well be broadcast on IP level???

Write functions

The various parameters and bits of data have to be send in an endianess-insensitive manner, and in some cases (e.g. angles) conversion to lower bit resolution and/or integer representation is done, too.

Client Side Prediction


This is one of he most important functions, gi.Pmove(pmove). It moves the player, according to information given in varaible pmove.



gi.configstring (index, string) serves as a general means of communication from server to all clients. The index parameter has the values:

The define q_shared.h::MAX_CONFIGSTRINGS defines the compile time configstring table as
 char configstrings[MAX_CONFIGSTRINGS=0x620][MAX_QPATH=64]

This is remarkable as CS_STATUSBAR is longer than 64. Several adjacent configstring slots are used, and only the last one is \0 terminated. So over the network, you might see configstring 5 with length 150, configstring 6 with length 86, and configstring 7 with length 22, but it all works out in the end.


gi.DebugGraph (value, colour) draws a graph??? Doesn't seem to do anything try turning debug mode on, a cvar.


Error exit, gi.error (text, variables), shuts down the game and displays the error message.


gi.TagMalloc(size, tag) allocates size bytes of memory, ???and sets a tag to determine how/when to release/purge/clean up. See DOOM Zone Memory allocation source.


Clean up the pool/zone memory, release all allocated chunks that are labelled as/below the given tag/priority. Function is gi.FreeTags(tag).


The gi.TagFree(block) function does memory (de)allocation, is not used anywhere in code?

Export to Server

The Game Subsystem provides what is listed in game.h::game_export_t, within the data structures provided in the original headers.

One of the functions is called at the start of a game, does initialization. One of the functions is also called at the beginning of each level to do similar stuff on a per-level basis. Another one of the gameapi functions is called every frame, and that is the function that does all of the game subsystem processing.

Compile Time Interface

Note that the server was compiled with the original game DLL source partly visible at compile time. These visible parts, if not ignored by the server, can not be changed. Unfortunately, this is only partly documented.

The g_local.h::GAME_INCLUDE flag masks the server visible game.h::g_client_t and game.h::edict_s. There is also g_main.c::GAME_HARD_LINKED, to handle local copies of functions needed in q_shared.c and some q_shwin.c that does not belong to the game system.

The Game Subsystem is fully described by the Game DLL sources. However, as the server has been compiled already, many parts of the Game Subsystem will simply be ignored. The only way to interact with the server is to:

Everything else will be ignored by server and client.

Server Known Data Structs


Server Known Functions


Possibly the most important function, explained separately. See ???there.

Server Known Values/Variables


As FRAMETIME is not exported to the server except at compiletime, changing it is not possible.


Has been 1 for Quake2 test/demo. 2 for Q2 3.05??? 3 for Q2 3.10???

6.2 Server-visible Data Structures

Only a part of the world structure/game system was visible to the server at compile time, and these can neither be changed nor can anything outside be visible to the server. The server does not use all of the header's contents even if they were visible at compile time. Comments indicate:

     game.h::gclient_t, as is
     game.h::edict_s, as is
     q_shared.h::pmove_state_t, as is.

Variables include: global.

Limitations for Modification Design

The following observations have to be made:

There are several restriction which should be noted in particular:

Next Previous Contents