Quake2 game DLL documentation

“the unofficial”

by Olle Källander

Abstract

This document is intended to give a good introduction to and deeply cover the details of Quake2 game DLL programming. It is useful as hands-on guidance and reference to programmers. It is pretty useless to map makers, skin authors and other graphics artists using, or intending to use, Quake2 as target for their creations.

Foreword

When I wrote this document, I decided to stay on focus and strive towards maximum clarity and conciseness. Therefore, I refrain from using chargong, monolouges, hilarious remarks, etc. If you miss them, I suggest you scan a mail-list or a newsgroup until you are fed up with them and then get back to this document.

I suggest that you browse the code as you read this document. Arm yourself with grep, qgrep or a C development environment, which can produce browse information and find in files. It will help you get a picture of what I am talking about.

Acknowledgment

I have extracted information, rewritten information, and compiled information from a lot of tutorials and emails that I have found on the Internet. Without all those scraplets of information this document would have been much harder to write. Thanks !!

Table of Contents

Quake2 game DLL documentation	1

1 Quake2 Fundamentals	5

1.1 APIs	5

1.2 Objects in the Quake2 World	5

1.3 Keeping the Game Going	6

1.4 Exchanging the APIs	6

1.5 Important Entries in the API	7

2 Initializing the Quake2 World	7

2.1 Files	7

2.2 Global Data Structures	7

2.3 Starting the Game	7

3 How the Player Works	8

3.1 Data Structures	8

3.2 How the client is spawned in the game	8

3.3 Movement and viewing	8

3.4 Firing weapons	10

3.5 Inventory	10

3.6 Collision detection	10

3.7 The player’s trail	11

3.8 Death and Resurrection	11

4 How a monster works	11

4.1 Data structures	11

4.2 Monster movement	12

4.3 Monster AI	12

4.4 Firing weapons	12

5 How weapons work	12

5.1 Animating the weapons	13

5.2 Ammunition	13

6 How armor works	13

7 How items work	13

8 How doors and elevators work	13

9 How a trigger works	13

10 The Engine API	13

10.1 Text output Commands	13

10.2 Audio Commands	14

10.3 Models and Textures	14

10.4 Spatial Sensing	15

10.5 Entity Linking	17

10.6 The Console	17

10.7 Global Game Variables	17

10.8 Network Broadcast	17

10.9 Miscellaneous Functions	18

10.10 Memory Management	18

11 File structure	19

11.1 g_phys.c	19

11.2 g_ai.c	19

11.3 g_utils.c	19

12 Data structures	20

12.1 edict_t	21

12.2 entity_state_t	24

12.3 gclient_s	24

12.4 player_state_t	26

12.5 pmove_state_t	26

12.6 pmove_t	27

12.7 usercmd_t	27

12.8 gitem_t	27

12.9 monsterinfo_t	28

12.10 moveinfo_t	28

12.11 Entity effects	29

12.12 Rendering flags	29

12.13 Content types and masks used by edict_t.clipmask and edict_t.watertype	29

13 Quake2 network protocol	30

13.1 The multicast function	30

13.2 The unicast function	30

13.3 Packet types	31

13.4 Player muzzle flashes	31

13.5 Monster muzzle flashes	31

13.6 Layout	32

13.7 Inventory	32

13.8 Temporary entities	32

Quake2 Fundamentals

The Quake2 server comprises an executable (quake2.exe) and a game DLL (gamex86.dll on INTEL systems). I will refer to them as the Quake2 engine and the game DLL. The game DLL is responsible for the AI of the monsters, weapons, bots and other “game-rule-related” things.

In contrast to the Quake2 server, the quake2 executable also contains a client part, which is connected to the server over a network, or locally if possible. This makes it possible to run a dedicated Quake2 server, which runs the game DLL and has a number of clients connected to that server in runtime. The clients need not even have that game DLL. It is the server that dictates the events and controls the entities in the Quake2 world. Everything conceived by the server is conveyed as network messages to the clients. They only have to render and tell the server what the connected player is up to.

Quake2 is also accompanied by PAK-files. A PAK-file contains one or several maps, which comprise the Quake2 world. Also, they contain information about where entities, like monsters, are placed in the world.

Id software has chosen to release the source code for the game DLL, at least up to version 3.14 of Quake2. This means that an experienced C-programmer can change the rules and behaviour of the game. A combined effort between a C-programmer and a graphics artist, fond of using Qole or similiar tool, can result in a completely different game running on top of the Quake2 3D rendering engine.

However, the possibilities are not endless. You are still bound by the specification of the network protocol when starting visual effects like sparks, smoke trails and alike in the Quake2 world. You are also bound by the limitations of the rendering engine.

APIs

The game DLL and the server communicate through a couple of APIs. In this context, API means a structure if function pointers, very much like the foundation of Micrsofts COM architecture or C++ vtables.

The game DLL receives a game_import_t pointer and the server receives a game_export_t pointer, each constituting an API.

This means that the DLL only needs a single entry point in its export table: the GetGameAPI function. It is used for exchanging APIs, and after that all calls are made through the APIs.

Objects in the Quake2 World

Quake2 renders a 3D world. To keep this effective and updateable over a network, the Quake2 world is divided into static objects and entities. The static objects are the walls, floor, water, sky etc. Each client has its own map of these things, called a PAK-file. The server has an identical copy of the PAK-file. The entities, on the other hand, are movable, changeable and removable. Some of them are also positioned by the PAK-file, while others appear only temporarily. Monsters, windows and barrels are positioned by the PAK-file, while explosions and gun sparks can be created and removed dynamically during game play.

Each entity in the Quake2 world is represented by an edict_t structure. This structure contains data about that entity, which can be a player, a piece of equipment, a monster, a door or a window; anything that is not static to the world. The edict can also be extended by a couple of structures, if it is an item or a player. Noticeable is the fact that the edict_t structure is divided into two parts: the engine part and the DLL part. This means that the engine and the DLL have agreed on a contract, as to which the structure’s first 19 attributes should be. The rest of the structure is up to the DLL to design. Hence, the DLL can add a lot of attributes and functionality to each entity, without having to take the engine’s behaviour into account. In the original Quake2 3.14 code, the DLL adds about 70 attributes to the edict_t structure.

The edict_t structure and its substructures also contain function pointers. Most prominent is the think function, which is the means by which each entity gets some processor time for thinking and acting. The entity can set a nextthink variable, which tells when it needs processor time again. This is an efficient way of simulating multithreading without the overhead of context switching and problems of synchronization. It does require the collaboration of all entities for the scheduling to be fair.

A global variable, g_edicts, contains an array of all the entities in the world, which is used for looping through the entities to animate them, finding them, and disabling them. The g_edicts variable is allocated by a TagMalloc function in the import API and exported through the export API. This means that the server can also iterate through, and modify all the entities in the world.

Note about the allocation: It is accomplished using the TagMalloc function imported through the game import API. This means that the memory is allocated by the engine using runtime functions linked with the engine. This means that the access violations, which often arise when allocating and releasing memory in DLLs linking with different runtime allocator functions, are remedied.

Keeping the Game Going

The game is driven by the Quake2 engine. Every 100ms it calls a RunFrame function of the game DLL, which in turn calls a tree of functions for animating all the items in the world. This is called a server frame.

Every 50ms or so the Quake2 engine calls a function named ClientThink in the game DLL. Actions taken by the player is introduced into the game at that point.

Exchanging the APIs

You can find the GetGameAPI function in the g_main.c file. This is the only function exported from the gamex86.dll. It is used for exchanging two sets of function pointers between the game DLL and the server. A game_export_t struct is set up by the DLL to point at a dozen functions inside the game DLL. The GetGameAPI function also stores away a pointer to a game_import_t struct, received from the server, in the global gi pointer. The gi pointer is used through out the game DLL to communicate things back to the server.

The export interface consists of a number of functions, which are used more or less infrequently for initializing the game, initializing a new player, exiting game or disconnection a player and changing info about a player like skin, name, etc.

Two functions of the game_export_t are used as entry points for driving the game forward: RunFrame and ClientThink functions. RunFrame is setup to point at the G_RunFrame function while ClientThink points at ClientThink in p_client.c.

Important Entries in the API

G_RunFrame

The game export API RunFrame function pointer is set up to point at this function. The engine calls this function every 100ms to advance the world that much. This is called a server frame.You can find this function in the g_main.c file. The function iterates through the g_edicts array and lets every entity get a chance to run its think function. Also, it calls G_RunEntity for each entity.

The G_RunEntity calls functions, which handles movement of an entity and calls the entity’s think function (SV_RunThink). For example: a grenade being thrown, an item being dropped, or a door swinging. This is also how monsters get their share of CPU for AI.

G_RunFrame also calls ClientBeginServerFrame and ClientEndServerFrame, which we will get back to frequently in this document.

ClientThink

This function is also exported through the game export API. The engine calls this function every 50ms or so. That interval is the client frame rate. In this function the engine and the DLL exchange information about client movement. The player may be standing on a conveyor belt, for instance. In that case the movement of the belt affects the movement of the player.

The client part of Quake2 predicts movement inbetween network updates from the server. Eventually, the engine updates the client with correct info.

Initializing the Quake2 World

Files

g_main.c, g_save.c, g_spawn.c

Global Data Structures

game, level

You can find both of these in g_main.c.

The global variable game is used to carry cooperative spawn points and the array of clients.

Level is used to hold level specific information like number of monsters, secrets, killed monsters, etc.

g_edicts and game.clients

g_edicts is an array of game entities. The first entities in the array are reserved for use by clients. They are “max clients” many and they all have a link into the “game.clients” array, where the clients’ gclient_t data structures reside. All entities in use has an inuse flag set and are linked together as a list. This improves performance when the DLL iterates through active entities and when the DLL is reusing unused entities.

Starting the Game

Everything starts by exchanging the game import and the game export API. You can find the GetGameAPI function in g_main.c.

Next, the engine will call InitGame though the API. You can find this function in g_save.c. A lot of global variables, shared between engine and dll, are set up. InitGame also pre-allocates space for all edict_t entities and all eventual gclient_t structures. The number of entities in these is specified by the engine and can be configured when starting the server.

After calling InitGame, the engine will call SpawnEntities. Here, all entities are parsed off the game PAK file. Depending on skill level, some entities are spawned while others are not. Some entities are also linked together as teams. Presumably, the flyers are linked as teams. A call to PlayerTrail_Init makes sure that the trail produced by the client is initialized (see 3 How the Player Works).

How the Player Works

The player can also be referred to as a client since it connects to the Quake server / engine. The player has two “heart beats”. One is the server frame, which occurs every 100ms. The other is the client frame, which occurs according to client frame rate. The call graphs of these are depicted above.

In each server frame the DLL calls ClientBeginServerFrame for all clients in the world and G_RunEntity for each entity in the world. After that it calls ClientEndServerFrame for each client in the world. This order is important because this is the life cycle for each player in the world.

In ClientThink, which is called more frequently, the player has a chance to move and his weapon gets a chance to animate. The touch functions for entities touched by the player are called from ClientThink.

The files involved in player movement and action are: p_client.c, p_hud.c, p_trail.c, p_view.c, p_weapon.c and g_items.c

Data Structures

The player is represented by an edict_t just like any other object in the world. In addition to this, the player has a pointer to a gclient_s structure. In figure 3, you can see the data linked to the gclient_s structure. The gclient_s structure contains data about aiming direction, ping time, respawn info, current weapon, etc.

The most peculiar structures are the player_state_t and pmove_state_t. These are used for client movement prediction. The client frequently updates the Quake server with movement information, like buttons pressed, and movement direction.

How the client is spawned in the game

First, the engine calls ClientConnect. This means that the player begins connecting to the server. The game can refuse entrance to a client by returning false. Changing levels will NOT cause ClientConnect to be called again, but “loadgames” will.

If the client is allowed, the connection process will continue and eventually ClientBegin will be called by the engine. This function actually calls PutClientInServer. ClientBegin itself does not do much, it is more of a kick off for PutClientInServer and ClientendServerFrame.

PutClientInServer takes care of the actual spawning of the player’s body. It selects the spawn point, clears and configures the edict for the player and links it into the chain of active edicts in the engine.

Movement and viewing

Movement and position is divided into three parts: the players origin, the players velocity and the players view angles. Angle vectors are interpreted as Pitch, Yaw, and Roll; all represented in degrees. Yaw is when you look to the right and left. Pitch is when you look up or down. Roll is when you tilt your head to the right or left. When you die, your head is tilted 90 degress.

Origin and velocity

The edict for the player is quite elusive. We have several different origins and velocity attributes:

edict_t has: velocity, avelocity, move_origin

entity_state_t has: origin

pmove_state_t has: origin, velocity

gclient_t has: oldvelocity, kick_origin

There are also origins and velocities in the edict_t’s moveinfo_t.

The truth is that not all of these are used for the player. However, many of them are and the only way to understand this is to understand how the different data structures are used and when they are used.

The entity_state_t’s origin is the state of the player, and also the state for any other object in the world. Needless to say, in physics, velocity belongs to object state and should be an attribute of entity_state_t. The edict_t’s velocity and the entity_state_t’s origin are used through out the game code to target enemies, calculate paths of trajectory, etc.

The origin in pmove_state_t is only used in ClientThink. In ClientThink the player’s position is calculated and modified by objects in the world. A pmove_t, with a pmove_state_t as attribute s, is prepared with the information from entity_state_t’s orgin and edict_t’s velocity. They are both converted to type short and mulitplied by eight. This structure is then sent into the engine, via Pmove of the game_import_t API. The engine presumably uses this to adjust the position of the player in the world. In return, Pmove writes a new origin and velocity into the pmove_state_t structure. These two attributes are divided by eight and copied back into the edict_t’s velocity and entity_state_t’s origin. In other words, the DLL can affect the velocity and origin of the player and send that information to the engine. The engine gets the final word and calculates the actual position, which is written back into the structures of the game DLL. Any changes made to the velocity due to conveyor belts, a push by another object, etc are made to edict_t’s velocity and transferred to the engine, and the client, in ClientThink. The true origin, however, can only be set by the engine; apart from the spawning of the player, which is set by the DLL.

Finally, gclient_t’s oldvelocity is only used for calculating damage after a free fall. It is updated to be a copy of the edict_t’s velocity in ClientEndServerFrame, which is called at the end of each server frame. The edict_t’s move_origin is only used by turrets (see g_turret.c).

Avelocity in edict_t is used for rotations. The velocity vector is interpreted as pitch, yaw and roll. The unit is degrees. The player doesn’t use this. View direction is calculated as described below.

Kick_origin is only used for weapon kicks and only affects the view angles of the player.

View direction or angles

edict_t has: angle, ideal_yaw, yaw_speed, move_angles

entity_state_t has: angles

gclient_t has: v_angle, v_dmg_roll, v_dmg_pitch, kick_angles, kick_origin, killer_yaw, oldviewangles

player_state_t has: viewangles, viewoffset, kick_angles

pmove_state_t has: delta_angles

pmove_t has: viewangles, viewheight

The truth about view angles is tightly related to the mechanisms the ClientThink function. The entity_state_t’s angles is the aiming direction of an entity. This also holds for a player, but the view rendering for the player is based on the value in player_state_t’s viewangles. However, those view angles are seldom used in the DLL. Instead, the DLL uses v_angle in gclient_t for various calculations. Especially when firing weapons. The player’s entity_state_t’s angles are set in ClientEndServerFrame, to the value in gclient_t’s v_angle. Until then, almost all calculations are based on v_angle in gclient_t.

The delta_angles of pmove_state_t of the gclient_t are updated when the player is pushed by other entities. Inside ClientThink, the engine receives those delta_angles. In return, the pmove_t’s viewangles from the engine, are copied into gclient_t’s v_angle and the player_state_t’s viewangles. The pmove_t’s viewangles are entirely used for carrying data from the engine to the DLL. In other words the engine, and presumably the player, dictates the view angles.

The kick_angles in gclient_t are used for kicks from weapon fire. These does not enter the engine through the Pmove function. However, the engine must know about them to calculate the camera shaking induced by weapon kicks. They could be referenced by the engine indirectly, since the engine knows the whereabouts of the client in the global g_edicts array. Probably, the engine looks at the player’s player_state_t, because that is where the kick_angles are stored after summing them up in SV_CalcViewOffset. SV_CalcViewOffset uses v_dmg_roll and v_dmg_pitch as factors for computing the player_state_t’s kick_angles. SV_CalcViewOffset is called from ClientEndServerFrame.

The edict_t’s ideal_yaw and yaw_speed are only used by monsters to turn. Killer_yaw in gclient_t is used when the player is dead, to have him look at the entity that killed him.

Oldviewangles in gclient_t is updated to the value of player_state_t’s viewangles in ClientEndServerFrame. It is then used in SV_CalcGunOffset to make the weapon aiming lag a bit when the player turns.

Firing weapons

Weapons are fired in the weapon’s think function. There is a global itemlist, declared in g_items.c. All types of items are declared there and those items, which are weapons, has a weaponthink function pointer setup to point at a function inside p_weapon.c. These functions are called from Think_Weapon, which is called from ClientThink and ClientEndServerFrame. ClientThink only calls Think_Weapon if the player is pressing the attack button. However, to animate the weapon, when not firing it, ClientEndServerFrame calls Think_Weapon every server frame if it hasn’t been called in ClientThink.

The currently selected weapon is pointed at from client_persistant_t’s weapon pointer. It points to an item in the global itemlist.

Generally, all weapons rely on a couple of attributes in gclient_t to animate and control the weapon. The weaponstate attribute of gclient_t is used to track if the weapon is firing, being prepared to use, etc. The gunframe attribute of the player_state_t is used to track which animation frame is being played to the client. In addition to these two, some weapons use

The ammunition, used by the currently selected weapon, is defined by client_persistant_t’s ammo_index. It is an index into the inventory of client_persistant_t. When the different weapons are fired they decrese the number of items in the inventory.

Inventory

The player’s inventory is located in client_persistant_t. It is referenced through the gclient_t’s pers attribute. The inventory is an array of integers that represent the number of items of different item types. That is, the index in the array is the item type, which corresponds to the index in the global itemlist. Finding an item in the inventory is the same as finding it in the global itemlist and then using the index of itemlist as the index in the inventory.

Note that the weapons are also items in the inventory and the global itemlist.

Items in the inventory can be picked up, used, dropped and selected. client_persistant_t has an attribute, selected_item to help the DLL with this. When an item is found its contents is represented by the data in itemlist. This means that a clip always contains the same quantity of bullets. The inventory is increased at the position for items of type bullet when you find a clip. Functions for item handling can be found in g_items.c.

Collision detection

The player has, like any entity in the Quake2 world, a bounding box. This box is spanned by the mins and maxs vectors in the edict_t structure. These two vetors are relative to the entity’s origin in entity_state_t’s origin. There are also two vectors, absmin and absmax, which span the same box but are relative to world origin. Finally, the size value in edict_t is the euclidean length of the vetor reaching from mins to maxs.

To aid collision detection there is also a couple of bounding box attributes in edict_t, which tells what type of bounding box this is and how a collision is to be defined. The solid attribute tells if collisions are defined as inside the box or outside. A clipmask tells what type of material the collision is defined for. You can find these clipmasks in chapter 12.11.

Collision detection is performed in the engine but is conveyed to the game DLL in the ClientThink function. The Pmove function of the game_import_t API, which the game DLL calls, sets the touchents attribute in the pmove_t structure. The game DLL can iterate through that array of entities and call their respective touch function. I presume that the engine also prevents the player from moving through objects by affecting the origin of the pmove_state_t. After all, the engine always has the last say about the players origin due to the call to Pmove.

The player’s trail

To help the monsters hunt the player, the player places a trail of invisible entites as he moves. These footsteps or pheromone trail is limited in length and is actually represented as a circular buffer of entities. Each of these entities tells when the player stepped here and in what direction the player was moving when he stepped here. Actually, the direction is the vector from the previous footstep to the current, which is not as useful as a vector pointing at the next footstep. Footsteps are only printed if the previous footprint is out of sight. This minimizes the number of footsteps but allows the monsters to track the player around corners.

You can find the code for the trail in p_trail.c.

Death and Resurrection

When the player is shot to bits his die function of the edict_t is called. In this case, it points at player_die in p_client.c. Basically, it sets the player to be nonsolid when touching other objects, clears the inventory and tells the player to look at the killer. If this is an ordinary game it all ends here. The player has to restart somehow and reconnect to the server.

If it is a cooperative game or a deathmatch, the player has an option to respawn by pressing the attack button. In this case, ClientBeginServerFrame calls respawn in p_client.c. It is actually in respawn that the decision is taken whether to restart the game or just respawn the player.

The player places a copy of its edict’s information in a global dead body queue. This body queue is a circular queue in the g_edicts array. It is situated just above the clients in the array. Remember that the first maxclients entities were reserved for clients. Perhaps this body queue is used to let bodies lie around on the ground whereas the player is actually resurecting in its old body.

After placing a copy of the body in the dead body queue, the DLL calls PutClientInServer with the dead edict_t as argument. This means that the player will be respawned somewhere in the game, still using the same edict_t.

How a monster works

Unlike players, monsters are entirely controlled by the game DLL. They are entities with edict_t structures like everything else in the world and they reside in the global g_edicts array.

They are created in SpawnEntities. This function reads the monsters off the PAK-file and spawns them depending on the selected skill level.

Each monster has its own C-file in the game DLL, which are prefixed with m_. These files contain code for monster behaviour and spawning. The code of each monster is backed up by common monster code in the files: m_move.c , g_monster.c and g_ai.c .

Data structures

The monster is represented by the edict_t structure, which contain the monsterinfo_t structure. They have an entity_state_t, just like the players, but they don’t have a client structure.

Other important data structures are mmove_t, mframe_t and entity_state.

Monster movement

Monsters are animated in server frame steps and their AI and movement runs at the same pace.

Monster movement relies on mmove_t and mframe_t structures. The first defines a move in terms of which frame sequence is to be executed. The latter defines the actual frame sequence. Each monster has its own frame sequences, which you will find in each monster’s C-file. The monsterinfo_t, of the monster’s edict_t, has a currentmove pointer that points to a predefined mmove_t.

In addition to specifying which model to use for the monster in each frame, the mframe_t specifies the AI-function and think function to be called each frame. Thus, AI can easily be scripted into the monster’s animation sequences. Commonly, the AI function pointer points to a function inside g_ai.c. The mframe_t also contains a dist attribute, which tells how far the monster will move the next frame.

It is common that a monster specifies ai_walk as the aifunc for a frame. That function tries to move the monster a distacnce in the monster’s moving direction. A lot of checks takes place first to see if the monster is blocked by something, but if the path is clear the SV_movestep function will update the monster’s entity_state_t’s origin.

Every monster move starts in the RunFrame function. It will call the think function of the monster further down the call graph. The think function of a monster always points at monster_think. From there, the game DLL will call M_MoveFrame. M_MoveFrame increases the frame number and calls both the AI-function and the think function of the current frame.

Monster AI

Monster AI mainly resides in the g_ai.c file. The mframe_t sequences of the monsters refers to functions in this file. These functions act upon the monsterinfo_t’s aiflags, which tells the state of the monster’s current “plan”.

Each server frame the monster tries to find a valid target. To avoid spending too much time finding targets, the monster only checks one client each server frame. This is no problem in single player game, but in cooperative game the monster will notice players a bit slower. The procedure of finding just one of the clients is supported by a few attributes in the global level structure. It contains: a sight_client pointer, sight_entity, sight_entity_framenum, sound_entity; sound_entity_framenum, sound2_entity, and a sound2_entity_framenum. Every server frame, AI_SetSightClient is set to the next living client. This means that all monsters will try to target this client in this server frame. The next server frame it will be another client. The function FindTarget is used to find the client designated for sighting. FindTarget also tries to find the client by “listening” for sound. That is when sound_entity becomes useful. FindTarget is called from various AI-functions like: ai_stand, ai_walk, and ai_run.

Firing weapons

The mframe_t sequences tells the monsters when to fire. When the proper sequence is chosen as current move, the monster will fire in one frame or another. One or several frames point at a proper firing function for that particular type of monster. The firing function relies on a few functions in g_monster.c, which in turn relies on the weapon functions of g_weapon.c. Note that the monsters never need to worry about weapon kicks, ammo and weapon animation. They only send network messages about muzzleflashes and fire the bullets.

How weapons work

A weapon is a gitem_t, in contrast to an edict_t. Weapons are referenced from the player’s client_persistant_t’s inventory.

Animating the weapons

An item does have a weaponthink function for animating the weapon when being carried by a client. To find out where the thinkweapon function pointer is pointing you will have to look in g_items.c, and browse the global itemlist, which is declared in there. All weapon think functions reside in p_weapon.c since it is only the client that uses weapon think functions. Monsters don’t have to animate their guns.

In most cases, the function WeaponGeneric is called in weaponthink. The frame sequence used for animating the gun is created just before calling WeaponGeneric, and the sequence depends on what type of weapon the client is carrying. What these frame numbers mean and where the actual frames are defined is a mystery at present time.

Ammunition

Bullets, shotgun shells, rail gun slugs

You can almost find all you want to know about bullets in the fire_lead function in g_weapon.c. Bullets are not entities in the world. It is just a matter of a muzzle flash and sparks or gib at the right position in the world. The game DLL uses the trace function of the game import API to project the bullet’s path and at the point of impact the game DLL orders for the effects. The projection is even different for water and air. The bullets change their direction somewhat when fired into water.

In water the fire_lead function also orders bubble trails to be rendered.

Grenades, BFG blasts, blaster bolts, rockets

These are entities with edict_t structures. Therefore, they can think and they use the think function to time their detonation. They also have touch function, which can be used to explode when touching other entities dynamic or static. When you fire or throw grenades, new entites are spawned and given velocities and mass. Once away, they are on their own.

How armor works

Armor is an item like any other item in the Quake2 world. It is represented as a gitem_t object in the global itemlist. Armor items do have an extension to gitem_t, which is called gitem_armor_t. This structure tells how much protection the armor item will give.

The inventory in the client_persistant_t merely tells how strong armor the player has of a particular armor type.

How items work

How doors and elevators work

How a trigger works

The Engine API

The following is a list of the Quake2 engine internal commands, which can be referenced by the game DLL.

Text output Commands

gi.bprintf (print_level, text, variables)

Sends message to all players. Print_level gives the message priority, and can be :

PRINT_LOW	pickup items

PRINT_MEDIUM	death

PRINT_HIGH	important stuff

PRINT_CHAT	chat messages

gi.dprintf (text, variables)

debug message (Debug cvar allows you to view these)

gi.cprintf (entity, print_level, text, variables)

Sends message to only one entity. Print_level sets message priority (see gi.bprintf for values)

gi.centerprintf (entity, text, variables)

sends message to one entity and displays message in centre of screen

Audio Commands

gi.soundindex (sound_filename)

Entities use this function to cache sound, stored in the PAK-file, during spawning. After that they simply refer to the index returned from this function.

gi.sound (entity, channel, sound_index, volume, attenuation, time_ofs)

Generates sound centered on given entity. Channel specifies which channel to use. It is sound type related and a second sound of the same sort will overwrite the first sound. The function plays a sound given a sound index, which is retreived with gi.soundindex. Volume is between 0 and 1. Attenuation gives how far away sound can be heard. Time_ofs signifies the delay before playing the sound.

channel can be any of:

CHAN_AUTO	new sound – does not overwrite any others

CHAN_WEAPON	weapon sound

CHAN_VOICE	sound made by entities vocal chords :)

CHAN_ITEM	invulnerability, quad damage, etc

CHAN_BODY	footsteps, stomach rumblings :) plus optional flags :

CHAN_NO_PHS_ADD	everyone hears this (same as ATTN_NONE)

CHAN_RELIABLE	important sound - make sure it gets there safely

attenuation can be :

ATTN_NONE	heard everywhere

ATTN_NORM	carries a long way

ATTN_IDLE	does not carry as far?

ATTN_STATIC	does not go very far at all

gi.positioned_sound (origin, entity, sound_index, volume, attenuation, time_ofs)

Similar to sound (see above for most of settings). Origin sets position for sound to come from relative to level origin (NOT entity origin)

Models and Textures

gi.modelindex (model_filename)

during spawning it caches the model, after that it simply returns the index which refers to that model

gi.setmodel (entity, model_filename)

Sets the entitie's model to model_filename

gi.imageindex (image_filename)

Used to pre-cache the icon during initialization, and reference it thereafter.

Spatial Sensing

gi.trace (point1, vector1, vector2, point2, ignore, mask)

Traces a box from point1 to point2, ignoring entity ignore, stoping if it hits an object of type specified in mask. Vector1 and vector2 set the box, which will do the tracing - if NULL, then a line is used instead

Returns value of type trace_t with one of these attributes:

allsolid	boolean - if true, entire trace was in a wall

startsolid	boolean - if true, trace started in a wall

fraction	fraction of trace completed (1.0 if totally completed)

endpos	point where trace ended

plane	surface normal at hitpoint (type cplane_t)

surface	surface hit (type csurface_t)

contents	contents of point at end of trace (see gi.pointcontents for values)

ent	entity hit by trace

Mask can be one (or more) of contents values (see gi.pointcontents) or can be one of :

MASK_ALL	stop on anything

MASK_SOLID	solid wall

MASK_PLAYERSOLID	solid wall or player or non-solid monster?

MASK_DEADSOLID	solid wall or player

MASK_MONSTERSOLID	solid wall or monster

MASK_WATER	any liquid

MASK_OPAQUE	walls and liquid except water (see thru)

MASK_SHOT	anything hit by weapons

MASK_CURRENT	current contents?

gi.AreasConnected (int areanum1, int areanum2)

Checks if two areas are connected - used to check whether a monster can hear a sound or not

Returns 1 or 0

gi.pointcontents (point)

Returns the content of the given point, will be one of:

CONTENTS_SOLID	solid wall (not windows)

CONTENTS_WINDOW	windows

CONTENTS_AUX	?

CONTENTS_LAVA	err.. lava

CONTENTS_SLIME	slime

CONTENTS_WATER	water

CONTENTS_MIST	!!Unused!!

LAST_VISIBLE_CONTENTS	?

CONTENTS_AREAPORTAL	portals to conserve memory used in map designing, also dont allow sounds	to pass //kami0

CONTENTS_PLAYERCLIP	brush which player may not pass through //kami0

CONTENTS_MONSTERCLIP	brush where monsters may not pass through //kami0

CONTENTS_CURRENT_0	flowing current moving at angle 0 //kami0

CONTENTS_CURRENT_90	flowing current moving at angle 90 //kami0

CONTENTS_CURRENT_180	flowing current moving at angle 180 //kami0

CONTENTS_CURRENT_270	flowing current moving at angle 270 //kami0

CONTENTS_CURRENT_UP	flowing current moving up //kami0

CONTENTS_CURRENT_DOWN	flowing current moving down //kami0

CONTENTS_ORIGIN	used for rotation, invisible //kami0

CONTENTS_MONSTER	non-solid monster?

CONTENTS_DEADMONSTER	dead monster

CONTENTS_DETAIL	brush that is not passed to vis - not in final BSP ?

CONTENTS_TRANSLUCENT	see through

CONTENTS_LADDER	ladder

gi.BoxEdicts(mins, maxs, entity_list, maxcount, areatype)

Generates a list of all the entities in a certain box. Mins and maxs define the box (relative to level origin). entity_list (type edict_t *varname[])

maxcount - number of entities to give

areatype - type of entity, can be :

AREA_SOLID	find solids

AREA_TRIGGERS	find triggers

Returns number of entities found

gi.SetAreaPortalState (poral_num, open)

Set when a door opens or closes (to prevent sound detection through closed doors?)

gi.inPVS (point1, point2)

Checks to see if point2 is in the Potentially Visible Set of point1. The potentially visible set (PVS) is a data structure used by Quake2 to determine visibility and lines of sight. Note that if point2 is in the PVS of point1, then point1 will also be in the PVS of point2, hence the first law of optics: if I can see you, then you can see me.

gi.inPHS (point1, point2)

Checks to see if point2 is in the Potentially Hearable Set of point1. The PHS is the same concept as a PVS, except it tracks sound instead of sight.

Entity Linking

gi.linkentity(entity)

links entity into the world so that it is sent to the client and used for collision detection, etc. Must be re-linked if its size, position or solidity changes.

gi.unlinkentity

Stop entity from interacting with the world. Must be done before deleting item

The Console

gi.argc()

Returns the number of arguments on the console

gi.args()

Returns the next entry on the console as a string

gi.argv(index)

Returns the argument at index from the console (command is arg 0)

gi.AddCommandString(text)

Adds a command to the server console, as if it had been typed

Global Game Variables

gi.cvar(variable_name, value, flags)

Declares and sets the variable to the value, and sets flags:

CVAR_ARCHIVE	saved to vars.rc

CVAR_USERINFO	added to userinfo when changed

CVAR_SERVERINFO	added to serverinfo when changed

CVAR_NOSET	can only be changed at command line

CVAR_LATCH	save changes until server restart

Returns the variable just set

NOTE: this is only used during the initialisation function

gi.cvar_set(variable_name, value)

Sets the variable to value and returns the variable just set

gi.cvar_forceset(variable_name, value)

Sets the variable to value and returns the variable just set. Forces the value?

Network Broadcast

Network handling is quite extensive and you can read about it in the Quake 2 Network Protocol Chapter.

gi.unicast(entity, reliable)

Send the prepared data to a single entity. Set reliable to true for important messages?

gi.multicast(origin, to)

Send prepared data to several clients.

gi.WriteChar(char)

gi.WriteByte(byte)

gi.WriteShort(short)

gi.WriteLong(long)

gi.WriteString(string)

gi.WritePosition(position)

gi.WriteDir(direction)

gi.WriteAngle(angle)

Miscellaneous Functions

gi.Pmove(pmove)

Moves the player according to information given in variable pmove

gi.configstring (index, string)

General means of communication from server to all clients

index has values:

CS_NAME	level name

CS_CDTRACK	cd sound track

CS_SKY	sky picture

CS_SKYAXIS	line around which sky wraps?

CS_SKYROTATE	where to start sky picture?

CS_STATUSBAR	either 'dm_statusbar' or 'single_statusbar'

CS_MAPCHECKSUM	appears to be unused (for detecting 'cheater' maps, apparently)

CS_MODELS	info on models in level? (unused)

CS_SOUNDS	info on level sounds? (unused)

CS_IMAGES	info on level images? (unused)

CS_LIGHTS	send message giving change in light level of a light

CS_ITEMS	used when sending a list of items on the level

CS_PLAYERSKINS	used to send name and skin of player to all

MAX_CONFIGSTRINGS	maximum configsting number ?! (unused)

gi.DebugGraph (value, colour)

gi.error (text, variables)

Shuts down the game and displays the error message

Memory Management

gi.TagMalloc(size, tag)

Allocate 'size' bytes of memory, referenced by tag

gi.FreeTags(tag)

free memory referenced by tag

gi.TagFree(block)

Memory de-allocation.

File structure

g_phys.c

A lot of functions prefixed with SV_ comprise this file. Especially interesting are the following functions:

SV_RunThink calles the think function of the entity if necesseray. We get here by:

G_RunFrame, G_RunEntity, SV_Physics_....., SV_RunThink

SV_Impact calls entities Touch functions. We get here through: G_RunEntity, (SV_Physics_Toss, SV_PushEntity or SV_Physics_Step, SV_FlyMove), SV_Impact

g_ai.c

Contains few AI-functions for finding targets, running and attacking. These are probably common and used as support functions for both bots’ and monsters’ AI.

g_utils.c

G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)

This function uses distance as three scale factors. The first is applied to forward, the second to right and the third is simply an offset on the z component.

Result is point + forward * distance[0] + right * distance[1] + {0,0,distance[2]}

Mostly used to project an entity’s origin relative to its view angles. Once you have used AngleVectors to convert the view angles into a forward and right-vector you can translate a point in the box spanned by the forward and right vector.

Data structures

Fig 3

edict_t

These are the attributes, which all entities have. No entity uses all of these attributes.

Engine part of edict_t.

This part may not be changed because the engine assumes all fields to be of these types and in this order.

Name	type	notes
s	entity_state_t	has several subtypes (see below)
client	struct gclient_s*	points to the player controlling it, NULL if not player controlled
inuse	qboolean	the entity exists in the world
linkcount	int	changed by the gi.linkentity function ?
area	link_t	used by engine.
num_clusters	int	used by engine.
clusternums[]	int	used by engine.
headnode	int	used by engine.
areanum, areanum2	int	area where you currently are used when checking if monsters can hear sounds. Used by engine.
svflags	int	SVF_NOCLIENT - don't send to clients SVF_DEADMONSTER – treat as dead monster for collision tests SVF_MONSTER – same, a bit earlier
mins, maxs	vec3_t	position of min and max points of entity relative to its origin
absmin, absmax	vec3_t	position of min and max points of entity relative to the world's origin
size	vec3_t	size of entity (vector from mins to maxs)
solid	solid_t	solidity of entity : SOLID_NOT -no interaction with other objects SOLID_TOUCH-touch inside after moving (used for triggers) SOLID_BBOX-touch on side (monsters, etc) SOLID_BDP-bsp clip, touch on edge (for solid walls, blocks, etc)
clipmask	int	type of objects the entity will not pass through can be any of MASK_* or CONTENTS_* listed at bottom of this document
owner	edict_t *	usually the entity that spawned this entity (eg bullet is 'owned' by the monster that fired it)

Game DLL part of edict_t.

Here you can add new attributes necessary for your modifications of the game DLL. The engine won’t mind. It will know the edict size by a reference in the game_export_t API. That is all it needs for proper allocation of new edicts.

Name	type	notes
accel	float	acceleration of platforms
activator	edict_t *	entity which activated the current entity
air_finished	float	time when air will run out (when underwater) compare to level.time. Players can hold breath about 10 sec.
angle	float	entity yaw set by level editor (-1 = up; -2 = down)
attenuation	float	attenuation of above sound (set at level edit)
avelocity	vec3_t	angular velocity (see note for velocity)
chain	editc_t *	used when creating a list of entities
classname	char *	type of entity (eg 'player','monster_soldier')
combattarget	char *	name of entity to attack (used once)
count	int	general purpose 'amount' variable, often set in level editor
damage_debounce_time	float	time when lava/slime can do more damage to player
deadflag	int	state of deadness : DEAD_NO, DEAD_DYING, DEAD_DEAD, DEAD_RESPAWNABLE
deathtarget	char *	name of entity to use when current entity dies
decel	float	deceleration of platforms
delay	float	time between triggering switch and effect activating
dmg	int	damage done by entity
dmg_radius	float	area to do radius damage
enemy	edict_t *	entity to attack
flags	int	various attributes : FL_FLY-flying FL_SWIM-swimming, won't drown FL_IMMUNE_LASER-not hurt by laser FL_INWATER-set when entering water, cleared when leaving FL_GODMODE- god mode on FL_NOTARGET – will not be targeted by monsters FL_IMMUNE_SLIME- not hurt by slime FL_IMMUNE_LAVA – not hurt by lava FL_PARTIAL_GROUND- ground pulled out from under monster FL_WATERJUMP- player jumping from water FL_TEAMSLAVE-not first member of a team FL_NO_KNOCKBACK-entity not thrown back by damage FL_POWER_ARMOR- active power armor FL_RESPAWN-entity waiting before respawning
fly_sound_debounce_time	float	used with looping sound when pushing player (in_trigger.c)
freetime	float	time when entity was freed-up in memory
gib_health	int	health at which to gib :)
goalentity	edict_t *	entity currently moving towards
gravity	float	not used by any entites, but can be used to define reduced/ increased gravity for individual entities (normal=1.0)
groundentity	edict_t *	what the entity is standing on
groundentity_linkcount	int	used in conjunction with linkcount to see if the current entity is still standing on the groundentity
health	int	health of entity, increased and set in many places, but should only be reduced by t_damage()
ideal_yaw	float	yaw the entity wants to face
item	gitem_t *	info about item (see below)
killtarget	char *	name of entity to remove when current entity triggered
last_move_time	float	time to play earthquake sound again
light_level	int	player's light level, used by monsters when checking if player can be seen
map	char *	filename of the next map, part of entity target_changelevel
mass	int	used when pushing entites and calculating knock-back for weapons (eg rocket hits)
max_health	int	maximum health
message	char *	message when entity activated (set by level editor)
model	char *	filename of entitie's model
monsterinfo	monsterinfo_t	info about the monster (see below)
move_angles	vec3_t	the angle that the gun turret is facing
move_origin	vec3_t	the origin of the gun turret
movedir	vec3_t	direction currently moving in
moveinfo	moveinfo_t	used by the ships flying overhead
movetarget	edict_t *	entity to wark towards when not got anything better to do(such as killing the player)
movetype	int	how the entity moves : MOVETYPE_NONE-never moves, just runs think MOVETYPE_NOCLIP-ignores walls, etc MOVETYPE_PUSH-ignores walls, pushes entities MOVETYPE_STOP-ignores walls, stops on entities MOVETYPE_WALK-players, affected by gravity MOVETYPE_STEP-gravity, special edge handling used by monsters MOVETYPE_FLY-no gravity used by flying monsters MOVETYPE_TOSS-gravity, hits ground and stops MOVETYPE_FLYMISSILE-weapons, extra clip size when hitting monsters MOVETYPE_BOUNCE-bounces (eg grenade)
mynoise,mynoise2	edict_t *	spawned when player makes a noise, monsters can home in on them
nextthink	float	time at which entity should call function 'ent->think'
noise_index	int	index of sound to play when entity is triggered
noise_index2	int	not used anywhere
oldenemy	edict_t *	player they were attacking before distracted (by another monster hitting them)
pain_debounce_time	float	prevent pain sounds/anims playing to often
pathtarget	char *	name of entity to move towards
pos1	vec3_t	top position of lift
pos2	vec3_t	bottom position of lift
powerarmor_time	float	time to stop showing translucent shield effects
radius_dmg	int	amount of radius damage done by entity
random	float	used to add a bit of time variation to func_timers
show_hostile	qboolean	any monsters seeing this one will inherit its enemy
sounds	int	type of sounds to play - used by doors, lifts, etc
spawnflags	int	set by level editor, some values are SPAWNFLAG_NOT_EASY, SPAWNFLAG_NOT_MEDIUM, SPAWNFLAG_NOT_HARD, SPAWNFLAG_NOT_DEATHMATCH, SPAWNFLAG_NOT_COOP There are other possible settings, which I have not yet looked at in detail.
speed	float	speed of misc entities, including moving buttons, the gun turret and the fly-by ships
style	int	type of entities (eg type of health box) also used as areaportal number (something to do with separating areas)
takedamgage	int	can the entity be damaged? : DAMAGE_NO-will not take damage DAMAGE_YES-will take damage DAMAGE_AIM-will take damage + weapons ‘auto-aim’ at it
target	char *	name of entity to trigger or move towards
target_ent	editc_t *	entity that platform/lift is moving towards
targetname	char *	name pointed to by target
team	char *	name of team which entity is part of
teamchain	edict_t *	the next entity in the team
teammaster	edict_t *	the master entity in the team
teleport_time	float	time when monster teleported into level
timestamp	float	Used by various functions to mark the spawn time of an entity or the start of a change (eg a light ramp) Used in trail to figure out how hold footprints are. Used by Viper, earthquakes , hurt triggers, dead hovers for time to explode.
touch_debounce_time	float	prevent touch function from being called again too quickly
viewheight	int	height above origin where eyes are found
velocity	vec3_t	current velocity.
volume	float	volume when playing above sound (set at level edit)
wait	float	time to wait before platform moves to the next point on its path
waterlevel	int	height of liquid : 0 – not in liquid 1 – feet in liquid 2 – on surface of liquid 3 – surrounded by liquid
watertype	int	type of water the entity is in (see the CONTENTS_* list at bottom of document)
yaw_speed	float	speed to turn towards ideal yaw
blocked	void (*self, *other)	function to call when movement is blocked by something? (optional)
die	void (*self, *inflicotr, *attacker, *damage, vec3_t point)	function to call when health <= 0 (MUST be present if the entity can take damage)
prethink	void (*ent)	function to call before each frame (optional)
pain	void (*self, *other, float kick, int damage)	function to call when hurt (optional)
think	void (*ent)	sets the function that should be called at time nextthink
touch	void (*self, *other, cplane_t *plane, csurface_t *surf)	function to call when it touches another entity/the world (optional)
use	void (*self, *other, *activator)	function to call when used (optional)

entity_state_t

attributes here are referenced by 'entity_name->s.attribute'

Name	Type	Notes
angles	vec3_t	angles of the entity :...angles[0] = pitch..angles[1] = yaw...angles[2] = roll (not really used)
frame	int	current frame
effects	int	special effects to apply to entity can be one or more of: EF_ROTATE-rotates (bonus items) EF_GIB-leave a blood trail EF_BLASTER-coloured light + trail EF_ROCKET-coloured light + trail EF_GRENADE-smoke trail? EF_HYPERBLASTER-light + trail EF_BFG-light + particles? EF_COLOR_SHELL-coloured glow around the entity EF_POWERSCREEN-green screen EF_ANIM01-automatically cycle between frames 0 and 1 at 2 hz EF_ANIM23-automatically cycle between frames 2 and 3 at 2 hz EF_ANIM_ALL-automatically cycle through all frames at 2hz EF_ANIM_ALLFAST-automatically cycle through all frames at 10hz EF_FLIES-flies effect EF_QUAD-quad damage 'glow' EF_PENT-invincibility 'glow'
event	int	effects that last one frame : EV_NONE-no effect EV_ITEM_RESPAWN-item respawning EV_FOOTSTEP-footstep noise EV_FALLSHORT-noise from small fall EV_MALE_FALL-noise from male falling EV_MALE_FALLFAR-noise from male falling a long way EV_FEMALE_FALL-noise from female falling EV_FEMALE_FALLFAR-noise from female falling a long way EV_PLAYER_TELEPORT-teleportation effects
modelindex	int	index of precached model
modelindex2	int	for when one modelindex just isn't enough ;). Used with the final 2-stage boss (oops, sorry if that's a spolier) and to show separate player weapons
modelindex3,4	int	not used
number	int	entity index number
old_origin	vec3_t	last position of entity (used in a few odd places)
origin	vec3_t	origin of the entity
renderfx	int	various drawing effects : RF_MINLIGHT-can always see the entity RF_VIEWERMODEL-don't let the player see this (himself) RF_WEAPONMODEL-only let the current player see this RF_FULLBRIGHT-entity always fully lit RF_DEPTHHACK-for view weapon Z crunching (?!) RF_TRANSLUCENT-entity is see through RF_FRAMELERP-? used by monsters and bodies RF_BEAM-laser beam RF_CUSTOMSKIN-skin is an index in image_precache RF_GLOW-pulse lighting for bonus items RF_SHELL_RED-red glow, when monsters are resurrecting RF_SHELL_GREEN-green glow, from power shield RF_SHELL_BLUE-blue glow, not currently used
skinnum	int	current skin number
solid	int	set by gi.linkentity, for client-side prediction
sound	int	to gaurentee shut-off of looping sounds

gclient_s

Used for holding data about a player.

Name	type	Notes
ps	player_state_t	Updates from the engine through pmove_state_t goes into this player_state’s viewangles. This attribute of gclient_t is known to the engine.
ping	int	Round trip time for network packets between server and client. This attribute of gclient_t is known to the engine.
ammo_index	int	index into inventory for ammo of current weapon
anim_duck	qboolean	player is ducking
anim_end	int	last frame in animation of entity
anim_priority	int	Is either: ANIM_BASIC-stand / run ANIM_WAVE-1 ANIM_JUMP-2 ANIM_PAIN-3 ANIM_ATTACK-4 ANIM_DEATH-5 Used to decide if animation steps to next animation frame.
anim_run	qboolean	set to true if client is running. A global variable xyspeed is set to the amount of client velocity in EndClientFrame. It is then used in SetClientFrame to decide wether anim_run should be set.
bobtime	float	seems to affects footsteps. Used mainly in walk cycles.
bonus_alpha	float	amount of blending in to perform for the active power up.0-no blending. 1-whatever color
breather_sound	int	
buttons	int	BUTTON_ATTACK 1 BUTTON_USE 2 BUTTON_ANY 128, any key
damage_alpha	float	amont of blending in for taking damage. 1-all red
damage_armor	int	dmage absorbed by armor
damage_blend	vec3_t	RGB tripplet of damage color
damage_blood	int	damage absorbed by health
damage_from	vec3_t	most recent origin of damage to client
damage_knockback	int	amount of damage to caclulate kick. see P_Damage_Feedback
damage_parmor	int	damage absorbed by power armor
enviro_framenum	float	frame number when env suit runs out of air
fall_time	float	used to pitch view after fall
fall_value	float	used to pitch view after fall
grenade_blew_up	qboolean	true if the player forgot to throw the grenade
grenade_time	float	timer for hand grenade
invincible_framenum	float	frame number when invincible runs out
kick_angles	vec3_t	weapon kicks. Used by engine to render view
kick_origin	vec3_t	Used by engine to render view.
killer_yaw	float	Tells direction to the client’s perpetrator. Used to update viewangles when player is dead.
latched_buttons	int	
machinegun_shots	int	
next_drown_time	float	
old_pmove	pmove_state_t	for detecting out of pmove changes
old_waterlevel	int	water level the player was in previously. used to queue sounds like jumping in and out of water.
oldbuttons	inte	
oldvelocity	vec3_t	
oldviewangles	vec3_t	
pers	client_persistant_t	
pickup_msg_time	float	
quad_framenum	float	
resp	client_respawn_t	
respawn_time	float	earliest time a player will be respawned
showhelp	qboolean	
showhelpicon	qboolean	not used
showinventory	qboolean	
showscores	qboolean	
silencer_shots	int	
v_angle	vec3_t	aiming direction. facing dir
v_dmg_pitch	float	max pitch applied by damage
v_dmg_roll	float	max pitch applied by damage
v_dmg_time	float	time when damage was incurred
weapon_sound	int	
weapon_thunk	qboolean	set in each server frame when weapon has thought
weaponstate	weaponstate_t	WEAPON_READY WEAPON_ACTIVATING WEAPON_DROPPING WEAPON_FIRING

player_state_t

Used by engine to track player. This struct is sent to client each server frame (100ms) to update the client with current movement. The pmove_state_t is sent every client frame (client frame rate) to predict movement. player_state_t, on the other hand, is movement.

Name	type	notes
blend	float [4]	RGB color blending for damage, BGA, and bonus items
fov	float	Field of view. can be set by cmd fov <degrees>
gunangles	vec3_t	The direction the gun is pointing. Calculated in SV_CalcGunOffset using viewangles.
gunframe	int	next frame when animating the gun.
gunindex	int	the model index for the gun. Retreived with gi.modelindex function in game_import_t.
gunoffset	vec3_t	Used in SV_CalcGunOffset
kick_angles	vec3_t	weapon and pain effects. Added to viewangles when rendering.
pmove	pmove_state_t	used for movement prediction
rdflags	int	render flags. used by engine when client is under water/slime/lava.
stats	short [MAX_STATS]	Used for displaying correct icon in stat bar at bottom of screen. For example the ARMOR_ICON will be set to point at correct bitmap and STA_TARMOR will be number of cells, if you activate the power armor. indexed with: STAT_HEALTH_ICON STAT_HEALTH STAT_AMMO_ICON STAT_AMMO STAT_ARMOR_ICON STAT_ARMOR STAT_SELECTED_ICON STAT_PICKUP_ICON STAT_PICKUP_STRING STAT_TIMER_ICON STAT_TIMER STAT_HELPICON STAT_SELECTED_ITEM STAT_LAYOUTS STAT_FRAGS STAT_FLASHES
viewangles	vec3_t	This is the direction the player is looking. Used by client to render the view.
viewoffset	vec3_t	position where the player has its eyes attached. Always within player bounding box. Calculated in SV_CalcViewOffset

pmove_state_t

This structure needs to be communicated bit-accurate from the server to the client to guarantee that prediction stays in sync, so no floats are used. Floats are multiplied by 8 before entered into this structure. If any part of the game code modifies this struct, it will result in a prediction error of some degree. This structure is sent with delta movement information to the client each client frame. This is how the client predicts where the player is and thus how to render the world.

Name	type	notes
delta_angles	short[3]	add to command angles to get view direction. Changed by spawns, rotating objects and teleporters
gravity	short	set according to global sv_gravity value in each call to ClientThink.
origin	short[3]	Set according to spawn point when putting client in server. Also set to enity_state_t .origin each ClientEndServerFrame.
pm_flags	byte	ducked, or teleporting
pm_time	byte	8ms units
pm_type	pmtype_t	movement type: PM_NORMAL, PM_SPECTATOR, PM_DEAD, // no acceleration or turning PM_GIB,// different bounding box PM_FREEZE
velocity	short[3]	set each ClientEndServerFrame to the entitys s.velocity

pmove_t

Used in ClientThink to convey information about the player’s state to the engine. The player entity’s player_state_t (ps) contains a pmove_state_t, which is copied right into this structure’s s attribute. ClientThink also takes a usercmd_t, which is copied into the cmd attribute. The entity’s origin and velocity is copied into the s attribute of this structure. The trace and pointcontents are setup and the structure is passed to the engine by gi.Pmove.

After that several parameters are extracted from this structure and put into the entity and its player state.

attribute	type	into/out of engine	notes
s	pmove_state_t	in/out	information from the players player_state_t and plmove_state_t
cmd	usercmd_t	in/(out maybe)	
snapinitial	qboolean	in	if s differs from client->old_pmove
trace	trace_t (*)(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)	in	
pointcontents	int(vec3_t point)	in	
numtouch	int	out	cardinality of touched objects
touchents	edict_t*[]	out	which entities the player has touched ?. Used to call that objects touch function
viewangles	vec3_t	out	
viewheight	vec3_t	out	
mins, maxs	vec3_t	out	bounding box size
groundentity	edict_t*	out	
watertype	int	out	
waterlevel	int	out	

usercmd_t

This one is passed from the engine to the game DLL in ClientThink. It contains information about the buttons the player pressed during the past client frame.

attribute	type	notes
msec	byte	
buttons	byte	Buttons pressed. mouse buttons goes here to
angles	short[3]	Used for respawn and teleporters somehow
forwardmove	short	Delta movement in forward direction. Not used in game DLL. Maybe used in engine
sidemove	short	Not used in game DLL.
upmove	short	Used in game DLL to decide if jump sound should be played.
impulse	byte	Not used in DLL. Perhaps obsolete. Remnant from Quake I.
lightlevel	byte	light level the player is standing on. saved into entity’s lighlevel attribute for use by monster AI when sighting player.

gitem_t

Referenced by 'entity_name->item.attribute'

Name	type	Notes
ammo	char *	type of ammo to use
classname	char *	name when spawning it
count_width	int	number of digits to display by icon
drop	void	function called when entity dropped
flags	int	type of pickup : IT_WEAPON, IT_AMMO, IT_ARMOR
icon	char *	filename of icon
info	void *	? unused
pickup	qboolean	function called when entity picked up
pickup_name	char *	displayed onscreen when item picked up
pickup_sound	char *	filename of sound to play when picked up
precaches	char *	string containing all models, sounds etc. needed by this item
quantity	int	ammo gained by item/ammo used per shot by item
tag	int	? unused
use	void	function called when entity used
view_model	char *	filename of model when being held
weaponthink	void	used for weapon animation and firing
world_model	char *	filename of model when item is sitting on level
world_model_flags	int	copied to 'ent->s.effects' (see s.effects for values)

monsterinfo_t

Referenced by 'entity_name->monsterinfo.attribute'

Name	Type	Notes
aiflags	int	Controls how the monster will behave : AI_STAND_GROUND-don't move AI_TEMP_STAND_GROUND-stand ground for a short time (don't get too close to enemy?) AI_SOUND_TARGET-look for source of a sound AI_LOST_SIGHT-just lost sight of player AI_PURSUIT_LAST_SEEN- AI_PURSUE_NEXT-able to get to last sighting point this frame AI_PURSUE_TEMP-continue to persue for a bit AI_HOLD_FRAME-entity will hold the same frame (eg machine gun firing) AI_GOOD_GUY-don't attack you (prisoners) AI_BRUTAL-keep attacking until enemy's health < -80 (usualy stop at 0) AI_NOSTEP-used by exploding box AI_DUKED-monster's (pathetic) attempts to evade your shots AI_COMBAT_POINT-ignores everything else until it reaches it's destination AI_MEDIC-healing something (stop when the 'enemy's health > 0 ) AI_RESURRECTING-it's ALIVE!!! (almost) show resurection effects
attack_finished	float	time when attack will be finished (prevents further attacks before monster is ready)
attack_state	int	attack method the monster is using : AS_STRAIGHT-moving forwards to firing position AS_SLIDING-moving sideways to firing position AS_MELEE-close range attack AS_MISSILE-long range attack
currentmove	mmove_t *	current set of frames cycling between
idle_time	float	time until which the idle animation should be played
last_sighting	vec3_t	last point where the player was seen
lefty	int	if and attack_state is AS_SLIDING, the monster will go left otherwise it'll go right. this is swapped each time
linkcount	int	changed by gi.linkentity?
nextframe	int	next frame to display
pausetime	float	time to stop at a particular path_corner
power_armor_power	int	energy the armor has left
power_armor_type	int	type of power armor:POWER_ARMOR_NONE, POWER_ARMOR_SCREEN, POWER_ARMOR_SHIELD2
saved_goal	vec3_t	if the monster can't get to where it wants to this frame, that position is saved here
scale	float	adjusts the running speed of the monster
search_time	float	time to keep looking for player
trail_time	float	timestamp of last section of player's trail found
attack	void	ranged attack (optional)
checkattack	qboolean	function called to see if the monster will attack this frame
dodge	void	!!INCOMING!! so just duck 'cos they wouldn't aim for your feet, would they? (optional)
idle	void	animation to 'spice up' the standing around (optional)
melee	void	close-up attack (optional)
run	void	has an enemy, getting ready to attack
search	void	searching for player animation (optional)
sight	void	just spotted someone (optional)
stand	void	function to call when just standing around
walk	void	moving towards movetarget

moveinfo_t

Used by star cruisers and moving objects like doors, elevators, moving platforms, etc.

You can find all its uses in g_func.c and g_misc.c

Name	Type	Notes
accel	float	
current_speed	float	
decel	float	
decel_distance	float	
dir	vec3_t	
distance	float	
end_angles	vec3_t	
end_origin	vec3_t	
endfunc	void (*)(edict_t*)	
move_speed	float	
next_speed	float	
remaining_distance	float	
sound_end	int	
sound_middle	int	
sound_start	int	
speed	float	
start_angles	vec3_t	
start_origin	vec3_t	
state	int	
wait	float	

Entity effects

Effects are handled on the client side of Quake2. Once set, the effects are active constantly on an entity. an entity that has effects will be sent to the client even if it has a zero index model.

EF_ROTATE	rotate around Z-axis
EF_GIB	red particle trail
EF_GREENGIB	green particle trail
EF_BLASTER	red light and yellow particle trail
EF_GRENADE	light greyish particle trail
EF_ROCKET	the rocket particle trail
EF_BLASTER	yellow particle trail
EF_HYPERBLASTER	long thin laser bolts
EF_BFG	green light and sirling green particles, not transparent unless RF_TRANSLUCENT is used on entity_state_t->renderfx
EF_COLOR_SHELL	enable RF_COLOR flags used [...]
EF_QUAD	blue aura
EF_PENT	red aura
EF_POWERSCREEN	green translucent hexagonal wall surrounding object
EF_FLIES	fly swarm
EF_TELEPORTER	yellow particle fountain
EF_FLAG1	red particle fountain
EF_FLAG2	blue particle fountain
EF_BOOMER	double oarange particle trail and red light
EF_ANIM01	animate every 5th server frame, frames 0 and 1
EF_ANIM23	animate frames 2 and 3
EF_ANIM_ALL	animate through all frames
EF_ANIM_ALLFAST	

Rendering flags

RF_VIEWMODEL	don’t draw through eyes, only mirrors. Prevents player from seing herself.
RF_WEAPONMODEL	only draw through eyes, not mirrors
RF_DEPTHHACK	for view weapon Z crunching
RF_MINLIGHT	always visible, despite complete darkness
RF_FULLBRIGHT	always fully lit up
RF_TRANSLUCENT	
RF_FRAMELERP	enables translucent appearance
RF_BEAM	laser bean effect. Drawn from entity_state_t ‘s old_origin to origin
RF_GLOW	pulse lightning for bonus items
RF_SHELL_RED	red shell added to object
RF_SHELL_BLUE	blue shell
RF_CUSTOMSKIN	unused. Allows dynamic skin swap.

Content types and masks used by edict_t.clipmask and edict_t.watertype

CONTENTS_SOLID	solid wall (not windows)
CONTENTS_WINDOW	windows
CONTENTS_AUX	?
CONTENTS_LAVA	err.. lava
CONTENTS_SLIME	slime
CONTENTS_WATER	water
CONTENTS_MIST	!!Unused!!
LAST_VISIBLE_CONTENTS	?
CONTENTS_AREAPORTAL	portals to conserve memory used in map designing, also dont allow sounds to pass //kami0
CONTENTS_PLAYERCLIP	brush which player may not pass through //kami0
CONTENTS_MONSTERCLIP	brush where monsters may not pass through //kami0
CONTENTS_CURRENT_0	flowing current moving at angle 0 //kami0
CONTENTS_CURRENT_90	flowing current moving at angle 90 //kami0
CONTENTS_CURRENT_180	flowing current moving at angle 180 //kami0
CONTENTS_CURRENT_270	flowing current moving at angle 270 //kami0
CONTENTS_CURRENT_UP	flowing current moving up //kami0
CONTENTS_CURRENT_DOWN	flowing current moving down //kami0
CONTENTS_ORIGIN	used for rotation, invisible //kami0
CONTENTS_MONSTER	non-solid monster?
CONTENTS_DEADMONSTER	dead monster
CONTENTS_DETAIL	brush that is not passed to vis - not in final BSP ?
CONTENTS_TRANSLUCENT	see through
CONTENTS_LADDER	ladder
MASK_ALL	-stop on anything
MASK_SOLID	-walls/windows
MASK_PLAYERSOLID	-anything you'd expect a player to stop on
MASK_DEADSOLID	-same as above, can go through monsters
MASK_MONSTERSOLID	-anything you'd expect a monster to stop on
MASK_WATER	-any liquid
MASK_OPAQUE	-walls and liquid except water (which is see-through)
MASK_SHOT	-walls, monsters and dead monsters
MASK_CURRENT	-flowing brush

Quake2 network protocol

This document is about the Quake2 network protocol from a quake dll perspective. That is, it lines up the principles and lists the opportunities of the network protocol for adding effects to the game.

The server communicates with the clients through network packets. Once broadcasted the clients will update their screens to reflect the changes made to the world.

Packets are created in the code by writing bytes, shorts, and positions to the game API and then submit the packet using the multicast or unicast function of the game API.

You can use, or may have to use, these functions when you want to display effects like sparks and explosions from your game DLL.

The multicast function

void	(*multicast) (vec3_t origin, multicast_t to);

to can be any of the following

MULTICAST_ALL,
MULTICAST_PHS, hearable sub-set relative to origin
MULTICAST_PVS, visible sub-set relative to origin
MULTICAST_ALL_R,
MULTICAST_PHS_R,
MULTICAST_PVS_R

The unicast function

void	(*unicast) (edict_t *ent, qboolean reliable);

ent is the “player” about to receive the network message. reliable is always true except when updating the deathmatch score board at the end of each server frame.

Packet types

Each packet always starts with a message type (BYTE), which can be one of the following:

svc_muzzleflash

svc_muzzleflash2

svc_temp_entity

svc_layout

svc_inventory

Depending on the type, the packet have different contents and format.

Player muzzle flashes

svc_muzzleflash

Has the following format:

gi.WriteByte (svc_muzzleflash);
gi.WriteShort (ent-g_edicts);
gi.WriteByte (MZ_BFG | is_silenced);
gi.multicast (ent->s.origin, MULTICAST_PVS);

Note that the server sends the memory offset in the edicts array.

muzzle flash type:

MZ_BLASTER	
MZ_MACHINEGUN	
MZ_SHOTGUN	
MZ_CHAINGUN1	
MZ_CHAINGUN2	
MZ_CHAINGUN3	
MZ_RAILGUN	
MZ_ROCKET	
MZ_GRENADE	
MZ_LOGIN	
MZ_LOGOUT	
MZ_RESPAWN	
MZ_BFG	
MZ_SSHOTGUN	
MZ_HYPERBLASTER	
MZ_ITEMRESPAWN	
MZ_BOOMERGUN	
MZ_SILENCED	Bit ORed with other flash types

Monster muzzle flashes

svc_muzzleflash2 is used when monsters are firing.

gi.WriteByte (svc_muzzleflash2);
gi.WriteShort (self - g_edicts);
gi.WriteByte (flashtype);
gi.multicast (start, MULTICAST_PVS);

where flashtype is one of the following:

MZ2_TANK_BLASTER_1 - 3	
MZ2_TANK_MACHINEGUN_1 - 19	
MZ2_TANK_ROCKET_1 - 3	
MZ2_INFANTRY_MACHINEGUN_1 - 13	
MZ2_SOLDIER_BLASTER_1	
MZ2_SOLDIER_BLASTER_2	
MZ2_SOLDIER_SHOTGUN_1 - 2	
MZ2_SOLDIER_MACHINEGUN_1	
MZ2_SOLDIER_MACHINEGUN_2	
MZ2_GUNNER_MACHINEGUN_1 - 8	
MZ2_GUNNER_GRENADE_1 - 4	
MZ2_CHICK_ROCKET_1	
MZ2_FLYER_BLASTER_1	
MZ2_FLYER_BLASTER_2	
MZ2_MEDIC_BLASTER_1	
MZ2_GLADIATOR_RAILGUN_1	
MZ2_HOVER_BLASTER_1	
MZ2_ACTOR_MACHINEGUN_1	
MZ2_SUPERTANK_MACHINEGUN_1 - 6	
MZ2_SUPERTANK_ROCKET_1	
MZ2_SUPERTANK_ROCKET_2	
MZ2_SUPERTANK_ROCKET_3	
MZ2_BOSS2_MACHINEGUN_L1 - L5	
MZ2_BOSS2_ROCKET_1 - 4	
MZ2_FLOAT_BLASTER_1	
MZ2_SOLDIER_BLASTER_3 - 8	
MZ2_SOLDIER_SHOTGUN_3 - 8	
MZ2_SOLDIER_MACHINEGUN_3 - 8	
MZ2_MAKRON_BFG	
MZ2_MAKRON_BLASTER_1	
.. MZ2_MAKRON_BLASTER_17	
MZ2_MAKRON_RAILGUN_1	
MZ2_JORG_MACHINEGUN_L1 - L6	
MZ2_JORG_MACHINEGUN_R1 - R6	
MZ2_JORG_BFG_1	
MZ2_BOSS2_MACHINEGUN_R1 - R5	

Layout

Help computer style text message. Used for help computer and death match score board.

gi.WriteByte (svc_layout);
gi.WriteString (string);
gi.unicast (ent, true); // or false when in ClientEndServerFrame()

Inventory

svc_inventory is used to list a persons inventory. It is a message from the server to a specific client.

gi.WriteByte (svc_inventory);
for (i=0 ; i<MAX_ITEMS ; i++)
gi.WriteShort (cl->pers.inventory[i]);
gi.unicast (ent, true);

Temporary entities

Is used for events like explosions, that appears, happens and dissapears in the world. Depending on the type of entity broadcasted, the network packet assumes different formats.

TE_GUNSHOT TE_SHOTGUN TE_BLOOD TE_GREENBLOOD TE_SPARKS TE_BULLET_SPARKS TE_BLASTER TE_SCREEN_SPARKS TE_SHIELD_SPARKS	gi.WriteByte (svc_temp_entity); gi.WriteByte (type); gi.WritePosition (endpos); gi.WriteDir (tr.plane.normal / origin); gi.multicast (tr.endpos, MULTICAST_PVS);
TE_RAILTRAIL TE_BUBBLETRAIL TE_BFG_LASER TE_PLASMATRAIL – random green arcs	gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_RAILTRAIL); gi.WritePosition (start); gi.WritePosition (tr.endpos); gi.multicast (self->s.origin, MULTICAST_PHS);
TE_EXPLOSION1 TE_EXPLOSION2 TE_ROCKET_EXPLOSION TE_GRENADE_EXPLOSION TE_ROCKET_EXPLOSION_WATER TE_GRENADE_EXPLOSION_WATER TE_BFG_EXPLOSION TE_BFG_BIGEXPLOSION TE_BOSSTPORT	gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_EXPLOSION1); gi.WritePosition (origin); gi.multicast (origin, MULTICAST_PHS);
TE_SPLASH, where color is any of the following: SPLASH_UNKNOWN 0 SPLASH_SPARKS 1 SPLASH_BLUE_WATER 2 SPLASH_BROWN_WATER 3 SPLASH_SLIME 4 SPLASH_LAVA 5 SPLASH_BLOOD 6	gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (self->count); // pixels in the splash gi.WritePosition (self->s.origin); gi.WriteDir (self->movedir); gi.WriteByte (color); gi.multicast (self->s.origin, MULTICAST_PVS);
TE_LASER_SPARKS, TE_WELDING_SPARKS	gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_LASER_SPARKS); gi.WriteByte (count); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (self->s.skinnum); gi.multicast (tr.endpos, MULTICAST_PVS);
TE_PARASITE_ATTACK TE_MEDIC_CABLE_ATTACK TE_GRAPPLE_CABLE Note that it sends its memory offset in the g_edicts list. models used from PAK: /models/monsters/parasite/segment/tris.md2 /models/ctf/segment/tris.md2	gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_PARASITE_ATTACK); gi.WriteShort (self - g_edicts); gi.WritePosition (start); gi.WritePosition (end); gi.multicast (self->s.origin, MULTICAST_PVS);

2

Quake II files

Olle Källander

1999-05-11T21:50:00