The Quake 2 Game DLL Subsystem written by the Q2DP Version 0.4 of 19. March 1998 This is supposed to be a reference linking into the Game Subsystem source, annotating the source code, and explaining the interaction between server process and game subsystem. This should be the refer­ ence for all MOD authors. ______________________________________________________________________ Table of Contents 1. Introduction 1.1 About the Quake 2 Documentation Project 1.2 The Open Game Architecture 2. Compilation, Installation, and Use 2.1 Minimal DLL 2.2 DM-only DLL 2.3 Switching the DLL on the fly 2.4 Startup Messages 2.5 Compiling as C++ 2.5.1 Builtin boolean 2.5.2 DLL internal prototype mismatch 2.5.3 Use of reserved keywords 2.5.4 Name Mangling and missing symbols 2.6 Interfacing Java 3. The Quake 2 Main Engine 3.1 Memory Manangement 3.1.1 Zone Memory Allocation 3.1.2 Hunk Memory Allocation 3.2 Main Engine Sound System 3.2.1 Functions 3.2.2 Variables 3.2.3 CD subsystem 3.3 GUI/Menu 3.3.1 Functions 3.3.2 M Variables 3.3.3 MENU variables 3.4 Networking 3.4.1 NET/UDP functions 3.4.2 SVC[lowbar]DirectConnect 3.4.3 SVC message types 3.5 Client subsystem 3.6 Rendering Variables 3.7 Server subsystem 3.7.1 SV Functions and Variables 4. Miscellaneous 4.1 Managing the Game DLL 5. The Quake 2 Game DLL 5.1 Releases and versions 5.1.1 Changes from v3.05 to v3.14 6. Interfacing the Game Subsystem 6.1 Game Subsystem API 6.1.1 Exporting/retrieving the Game API 6.1.2 Import from Main Engine (Server) 6.1.2.1 Text output Commands 6.1.2.1.1 bprintf 6.1.2.1.2 dprintf 6.1.2.1.3 cprintf 6.1.2.1.4 centerprintf 6.1.2.2 Audio Commands 6.1.2.2.1 soundindex 6.1.2.2.2 sound 6.1.2.2.3 positioned[lowbar]sound 6.1.2.3 Model Stuff 6.1.2.3.1 modelindex 6.1.2.3.2 setmodel 6.1.2.4 Icon Stuff 6.1.2.4.1 imageindex 6.1.2.5 Movement clipping 6.1.2.5.1 trace 6.1.2.5.2 AreasConnected 6.1.2.5.3 pointcontents 6.1.2.5.4 SetAreaPortalState 6.1.2.5.5 inPHS 6.1.2.5.6 inPVS 6.1.2.5.7 linkentity 6.1.2.5.8 unlinkentity 6.1.2.6 Console Stuff 6.1.2.6.1 argc 6.1.2.6.2 args 6.1.2.6.3 argv 6.1.2.6.4 AddCommandString 6.1.2.6.5 cvar 6.1.2.6.6 cvar[lowbar]set 6.1.2.6.7 cvar[lowbar]forceset 6.1.2.7 Networking 6.1.2.7.1 unicast 6.1.2.7.2 multicast 6.1.2.7.3 Write functions 6.1.2.8 Client Side Prediction 6.1.2.8.1 Pmove 6.1.2.9 Misc. 6.1.2.9.1 configstring 6.1.2.9.2 DebugGraph 6.1.2.9.3 error 6.1.2.9.4 TagMalloc 6.1.2.9.5 FreeTags 6.1.2.9.6 TagFree 6.1.3 Export to Server 6.1.4 Compile Time Interface 6.1.4.1 Server Known Data Structs 6.1.4.2 Server Known Functions 6.1.4.2.1 GetGameApi 6.1.4.3 Server Known Values/Variables 6.1.4.3.1 FRAMETIME 6.1.4.3.2 Version 6.2 Server-visible Data Structures 6.2.1 Limitations for Modification Design 7. Edicts and Entities 7.1 Spawn function LUT 7.2 The Server - Game Subsystem interface 7.3 Spawning during game 7.4 Freeing during game. 7.5 Linking entities 7.6 OOP view of spawning 8. Physics and Simulation 8.1 Rotation and Angles in Q2 8.1.1 Basics 8.1.2 Global and Entity Parameters 8.1.3 Input Event Handling 8.1.4 View Model 8.1.4.1 Step 1: User Commands and clc[lowbar]move 8.1.4.2 Step 2: ClientThink - convert, apply, and store 8.1.4.3 Step 3: Update Player State 8.1.4.3.1 Aiming Angles 8.1.4.3.2 Kick Angles 8.1.4.3.3 Delta Angles 8.1.4.4 Step 4: Server Frame and svc[lowbar]playerinfo 8.1.4.5 Step 5: Client Side Interpolation 8.1.4.6 Spawning Player 8.1.4.7 Teleporting Player 8.1.4.8 Dead Player 8.1.4.9 Player Intermission View 8.1.5 Object Orientation 8.1.5.1 NPC Monsters and objects 8.1.5.2 Weapon View Model orientation 8.1.6 How Do I Rotate The View? 8.1.7 How do I Rotate An Object? 8.1.8 How do I rotate a weapon? 8.1.9 Miscellaneous 8.1.10 Not Done 9. Configuration and Parameters 9.1 Configstrings 9.2 Console Variables 9.3 Client Console Commands 9.4 Server Console Commands 10. Savegame/loadgame 11. Annotated Module Overview 11.1 The API Header 11.2 The Quake2 DLL Shared Code 11.3 The Game (G) Modules 11.4 The Play/Client (P) Modules 11.5 The Player 11.6 The Miscellaneous (M) Monster Modules 11.7 The Monsters 11.8 The Bosses 12. DLL Source as HTML ______________________________________________________________________ 1. Introduction This is the document the Quake 2 DLL, that is, the source of the Game Subsystem as released by id Software, Inc., and its interaction with the server process of the game. 1.1. About the Quake 2 Documentation Project There is a top level document describing the Q2DP that is mandatory to be distributed along with this document, which is part of the Q2DP collection. See the Q2DP main document for all details on Authors and Contributors, Contacts, Corrections, Submissions, Trademarks and Acknowledgments, Distribution Policy and Copyright. If the top level document is missing, please contact the current maintainer of the Q2DP project, Bernd Kreimeier (bk@gamers.org), as soon as possible. You will find the most recent version of this document at www.gamers.org/dEngine/quake2/Q2DP/. See the ChangeLog for most recent changes to the Q2DP collection, and the Q2DP Supplement for the latest contributions and errata that have not yet been processed. We rely on you, the reader and fellow developer, to make this collection useful. If you have any suggestions, corrections, or comments, please send them to the current maintainer, Bernd Kreimeier (bk@gamers.org). For details please read the section on how to submit a contribution in the Q2DP main document. 1.2. The Open Game Architecture Quake 2 was designed in a way that in lack of an official name will be called Open Game Architecture throughout this document. The design was based on the experiences with the Quake/QuakeWorld design, which was in turn influenced by the achievements and failures in creating home brew modifications to the game DOOM. Most notably the deficiencies of the QuakeC scripting language influenced the current design. The Q2 architecture uses a DLL (dynamically linked library), which is loaded at runtime, and can even be switched at runtime, like the refresh libraries. 2. Compilation, Installation, and Use This section ??? will cover details of compiling the sources for C and C++, common pitfalls, where to install the resulting file, troubleshooting and verifying the game is using the DLL, and related issues. 2.1. Minimal DLL 2.2. DM-only DLL 2.3. Switching the DLL on the fly For some reason the +set game "directory" from the command line appears to load the DLL for the demo that plays. The variable game is now a latched variable, but is no longer read-only, so you can set it from the console and then start the game ???server. If you want to have a shortcut that jumps straight into your game all you have to do is put a solitary plus sign at the end of your command line: ______________________________________________________________________ quake2.exe +set game mygame +set deathmatch 1 +map q2dm1 + ______________________________________________________________________ 2.4. Startup Messages To be sure that the engine actually uses yur modified DLL, there are several ways. One is to add a gi.dprintf to g_main.c::GetGameAPI immediately after the functions are imported. This will be displayed on the shell/console where the application was started. This works with X11, but does not work well with fullscreen modes. In addition, depending on the amount of Q2 and your DEBUG messages, the startup lines will ge gone soon. You can also switch the DLL on the fly (see above). In that case, a message with gi.centerprintf might be preferable, emitted from InitGame???. Display on request command??? 2.5. Compiling as C++ 2.5.1. Builtin boolean A common mistake is replacing qboolean with the C++ type bool. As the boolean values are used in functions and structs visible to the server, size is critical, and the default for the original enum was int, thus define the type simply as integer for C++ compilation. Conversion of the predefined false and true will take care of the rest. 2.5.2. DLL internal prototype mismatch You have to change two local prototypes: ______________________________________________________________________ g_turret.c (turret_driver_die): the local prototype infantry_die is missing the final vec3_t point parameter. The declaration is in m_infantry.c and never checked against the prototype. * p_client.c (SP_info_player_intermission): this was implemented as func(void), changed to have a proper edict_t* self. The prototype is in g_spawn.c, as all the actor method prototypes, and never checked against the implementation. ______________________________________________________________________ 2.5.3. Use of reserved keywords If you are writing a mod, pleas refrain from using C++ keywords like new in your source. There is one fix in the id sources: in g_ai.c::ai_run change new to newgoal. 2.5.4. Name Mangling and missing symbols There is a problem with the name mangling. As the main engine imports only function pointers, this affects only one function known by name to the server process: the GetGameAPI function. ______________________________________________________________________ nm -D baseq2/gamei386.so | grep GetGame C++ : 0003b3c0 T GetGameAPI__FP13game_import_t C : 00037be0 T GetGameAPI ______________________________________________________________________ and you will find only the latter in the main engine. This is easily fixed by telling the compiler to apply C name mangling/symbol genera­ tion to that function, by ______________________________________________________________________ #ifdef CPLUSPLUS extern "C" #endif game_export_t *GetGameAPI (game_import_t *import) ______________________________________________________________________ which allows the source to be compiled both as C and C++. 2.6. Interfacing Java 3. The Quake 2 Main Engine There is only limited knowledge about the main engine, which basically serves as client and, if necessary (e.g. in single player) as server, or, alternatively, as dedicated server. It is believed that the main engine, if run as client only, and the refresh DLL's do not require the Game DLL at runtime. There is a certain amount of overlap between the refresh libraries and the Game DLL, which indicates either copies of sources, or compile time access to the GameDLL. See the documentation of the Rendering/Refresh Libraries for a list of shared functions. The following description is based on strings as found in the main executable, and guesses. 3.1. Memory Manangement 3.1.1. Zone Memory Allocation According to John Carmack, the Zone Memory allocation as used in DOOM was the only module that had a chance of being used for Quake. See the DOOM source, z_zone.c, for details of the previous implementation. · Z_Free · Z_Malloc · z_stats (variable) 3.1.2. Hunk Memory Allocation · Hunk_Alloc · Hunk_End · Hunk_Free 3.2. Main Engine Sound System 3.2.1. Functions · S_FindName · S_PickChannel · S_StartLocalSound · S_Update_ 3.2.2. Variables · s_volume · s_khz · s_loadas8bit · s_mixahead · s_show · s_testsound · s_primary 3.2.3. CD subsystem · CDAudio_Init 3.3. GUI/Menu 3.3.1. Functions · M_PushMenu · M_PopMenu 3.3.2. M Variables ______________________________________________________________________ m_cursor m_main_game m_main_multiplayer m_main_options m_main_video m_main_quit m_main_plaque m_main_logo m_banner_multiplayer m_banner_game m_pitch m_yaw m_forward m_side ______________________________________________________________________ 3.3.3. MENU variables ______________________________________________________________________ menu_main menu_game menu_loadgame menu_savegame menu_joinserver menu_addressbook menu_startserver menu_dmoptions menu_playerconfig menu_credits menu_multiplayer menu_video menu_options menu_keys menu_quit ______________________________________________________________________ 3.4. Networking 3.4.1. NET/UDP functions · NET_GetPacket · NET_SendPacket · UDP_OpenSocket 3.4.2. SVC_DirectConnect 3.4.3. SVC message types ______________________________________________________________________ svc_frame svc_deltapacketentities svc_packetentities svc_playerinfo svc_download svc_centerprint svc_spawnbaseline svc_configstring svc_serverdata svc_stufftext svc_print svc_sound svc_reconnect svc_disconnect svc_nop svc_inventory svc_layout svc_temp_entity svc_muzzlflash2 svc_muzzleflash svc_bad svc_lightstyle ______________________________________________________________________ 3.5. Client subsystem · CL_ParseStartSoundPacket · CL_ParseServerMessage · CL_ParseTEnt · CL_ParsePacketEntities · CL_ParseFrame · CL_GetEntitySoundOrigin · CL_ParseMuzzleFlash · CL_ParseMuzzleFlash2 Variables. ______________________________________________________________________ cl_testblend cl_testparticles cl_testentities cl_stats cl_stereo_separation cl_stereo cl_blend cl_lights cl_particles cl_entities cl_gun cl_footsteps cl_noskins cl_autoskins cl_predict cl_maxfps cl_upspeed cl_forwardspeed cl_sidespeed cl_yawspeed cl_pitchspeed cl_anglespeedkey cl_run cl_shownet cl_showmiss cl_timeout cl_testlights ______________________________________________________________________ 3.6. Rendering Variables · r_lightlevel · r_drawflat · r_fullbright · r_drawworld 3.7. Server subsystem 3.7.1. SV Functions and Variables SV_Multicast SV_StartSound SV_StartSound SV_StartSound SV_SendClientMessages SV_Configstrings_f SV_Baselines_f SV_Begin_f SV_ReadClientMessage SV_AreaEdicts SV_FatPVS SV_ReadPackets sv_enforcetime sv_noreload 4. Miscellaneous ______________________________________________________________________ rcon_password rcon_address scr_conspeed scr_showturtle scr_showpause scr_centertime scr_printspeed U_REMOVE ALIAS_LOOP_COUNT in_joystick Cbuf_AddText Cbuf_ExecuteText Cmd_AddCommand Cmd_RemoveCommand MOD_LoadBmodel Mod_LoadBrushModel CM_InlineModel CM_LeafContents CM_LeafCluster CM_LeafArea CMSF_ReadDir SZ_GetSpace COM_AddParm Com_sprintf PackFile FindFile FS_Read CopyFile PF_setmodel SpawnServer Sys_BeginFind Sys_Printf Sys_GetGameAPI Sys_UnloadingGame ______________________________________________________________________ 4.1. Managing the Game DLL The basic sequence is as follows: 1. LoadLibrary is called 2. GetGameAPI is called, which sets function pointers There are two related functions: · Sys_GetGameAPI · Sys_UnloadingGame Once the game is loaded, the networking is started: · NET_GetPacket · NET_SendPacket · UDP_OpenSocket Finally, the initial POV is determined. · BoxOnPlaneSide · InitialSnapPosition 5. The Quake 2 Game DLL The game library is used by the server process to run the simulation (authorative). The server notifies the client of important events - primarly collision detection, and collision handling related (movement clipping). User input is also executed on the server? The server transmits full state data only 10 times per second. It also transmits smaller updates of important state information (movement) at a higher rate. In addition, the client does movement prediction which extrapolates Last Known Good till the next update arrives, to compensate lag and latency. Certain effects that have no significance for events, like generation and animation of particles, are generated and controlled by the client exclusively once the event notification is arrived. 5.1. Releases and versions There were two source release: · Initial Source Code release as of Q2 v3.05 · Point Source Code release as of Q2 3.14 (Linux 3.14a) The second release was the first ever source release by id Software with a license.txt file and an installer. ???Filenames and dates, checksums?. Notes: the point release was OS-dependend (Win32 project files not in Lnux release, Linux Makefile not in Win32 release). 5.1.1. Changes from v3.05 to v3.14 The initial recursive diff is about a 100K. I made a short summary of 30K in a very raw form. The most important differences seem to be: · "means of death" (MOD_) defines in damage handling and fire functions, death messages · female vs. male player have less special handling now (model, sexed sounds) · damage values changed for DM vs. Coop. · no monster pain animation in nightmare mode · a lot of special handling to compensate map bugs · new sv_ ServerCommand function imported, new module g_svcmds.c · dropping, timing for weapons/quad/powerups · FOV flags and handling, FOV effects · using PHS instead of PVS (bug) · misc. bug fixes and typos, obsolete header removed · reworked friendly fire handling · reworked inventory handling · reworked body queue · reworked help, scoreboard · cooperative mode, exits etc. · inline assembly BoxOnPlaneSide · teleport splashs and other EV/EF/TE · new and removed c_vars · changed autosave handling · changed spawn spot handling · validate info strings · IP banning and passwords · missing PMF flags added, including PMF_NO_PREDICTION Downward/upward compatibility is not really practical, as several functions have additional or changed parameter lists now. For this reason, a list of new functions is not worth the trouble, too. Using special spawn filter function to fix map specific bugs is a rather strange approach to a problem that could be fixed with patches. Map patches have been done for DOOM already. 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 6.1.1. 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. 6.1.2. 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. 6.1.2.1. Text output Commands 6.1.2.1.1. bprintf The gi.bprintf (print_level, text, variables) function 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 6.1.2.1.2. dprintf The gi.dprintf (text, variables) function emits debug messages. Debug cvar allows you to view these. 6.1.2.1.3. cprintf 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). 6.1.2.1.4. centerprintf The gi.centerprintf (entity, text, variables) function sends message to one entity and displays message in centre of screen. 6.1.2.2. Audio Commands 6.1.2.2.1. soundindex 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". 6.1.2.2.2. sound 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: · CHAN_AUTO, new sound - doesn't overwrite any others · CHAN_WEAPON, weapon sound · CHAN_VOICE, sound made by entities · CHAN_ITEM, invulnerability, quad damage, etc. · CHAN_BODY, footsteps These can be OR'ed with optional flags: · CHAN_NO_PHS_ADD, everyone hears this (same as ATTN_NONE) · CHAN_RELIABLE, important sound - make sure it gets there The attenuation control parameter, which hints on how to decrease the sound volume by distance, can be: · ATTN_NONE, heard everywhere · ATTN_NORM, carries a long way · ATTN_IDLE, doesn't carry as far??? · ATTN_STATIC, doesn't go very far at all 6.1.2.2.3. positioned_sound 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. 6.1.2.3. Model Stuff 6.1.2.3.1. modelindex 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. 6.1.2.3.2. setmodel The gi.setmodel (entity, model_filename) sets the entities' model to model_filename. 6.1.2.4. Icon Stuff 6.1.2.4.1. imageindex Another cache/retrieval function, gi.imageindex (image_filename). Is used to precache the icon during initialisation, and reference it thereafter. 6.1.2.5. Movement clipping 6.1.2.5.1. trace 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 · qboolean allsolid - if true, entire trace was in a wall · qboolean startsolid - if true, trace started in a wall · float fraction - of trace completed (is 1.0 if totally completed) · vec3_t endpos - point where trace ended · cplane_t plane - plane (gives surface normal) at hitpoint · csurface_t surface - surface hit · int contents - contents of point at end of trace (see gi.pointcontents for values) · edict_t* ent - entity hit by trace, if any??? The mask can be one (or more) of contents values (again see gi.pointcontents) or can be one of: · MASK_ALL, stop on anything · MASK_SOLID, solid wall · MASK_PLAYERSOLIDm 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 through) · MASK_SHOT, anything hit by weapons · MASK_CURRENT, current contents??? 6.1.2.5.2. AreasConnected 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. 6.1.2.5.3. pointcontents gi.pointcontents (point) returns the content of the given point, will be one of: · CONTENTS_SOLID, solid wall (not windows, sky???) · CONTENTS_WINDOW, windows · CONTENTS_AUX ??? · CONTENTS_LAVA, err.. lava · CONTENTS_SLIME, slime · CONTENTS_WATER, water · CONTENTS_MIST, fog volume, !!Unused!! · LAST_VISIBLE_CONTENTS, current??? · CONTENTS_AREAPORTAL - defines an area portal · CONTENTS_PLAYERCLIP, brush which player may not pass through · CONTENTS_MONSTERCLIP, brush where monsters may not pass through · CONTENTS_CURRENT_0, flowing current moving at angle 0 · CONTENTS_CURRENT_90, flowing current moving at angle 90 · CONTENTS_CURRENT_180, flowing current moving at angle 180 · CONTENTS_CURRENT_270, flowing current moving at angle 270 · CONTENTS_CURRENT_UP, flowing current moving up · CONTENTS_CURRENT_DOWN, flowing current moving down · CONTENTS_ORIGIN, used for rotation, invisible · CONTENTS_MONSTER, non-solid monster??? · CONTENTS_DEADMONSTER, dead monster · CONTENTS_DETAIL, brush that is not passed to vis, as it will probably not be an occluder. It is in final BSP, but may not be spit??? · CONTENTS_TRANSLUCENT, see through · CONTENTS_LADDER, ladder (special gravity handling) sect4>BoxEdicts 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). · edict_t *varname[] entity_list · maxcount - number of entities to give · areatype - type of entity, can be · AREA_SOLID, find solids · AREA_TRIGGERS, find triggers The function returns number of entities found. 6.1.2.5.4. SetAreaPortalState 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). 6.1.2.5.5. inPHS 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. 6.1.2.5.6. inPVS 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). 6.1.2.5.7. linkentity 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. 6.1.2.5.8. unlinkentity gi.unlinkentity stops entity from interacting with the world, and must be done before deleting it. 6.1.2.6. Console Stuff 6.1.2.6.1. argc gi.argc() returns the number of arguments on the console. 6.1.2.6.2. args Iterator gi.args() returns the next entry on the console as a string. 6.1.2.6.3. argv gi.argv(index) returns the argument number index from the console (command is arg 0). 6.1.2.6.4. AddCommandString gi.AddCommandString(text) adds a command to the server console, as if it had been typed. 6.1.2.6.5. cvar 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 Teh function returns the variable just set. Note that this is only used within the initialisation function. 6.1.2.6.6. cvar_set gi.cvar_set(variable_name, value) sets the variable to value, returns the ???variable just set. 6.1.2.6.7. cvar_forceset gi.cvar_forceset(variable_name, value) sets the variable to value. It returns the variable just set, and ???forces the value setting. 6.1.2.7. Networking 6.1.2.7.1. unicast 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. 6.1.2.7.2. multicast gi.multicast(origin, to) sends prepared data (including origin) to : · MULTICAST_ALL, send message to all · MULTICAST_PHS, send message to all in PHS · MULTICAST_PVS, send message to all in PVS · MULTICAST_ALL_R, send to all, reliable · MULTICAST_PHS_R, send to PHS, reliable · MULTICAST_PVS_R, send to PVS, reliable As some info sent to (nearly all) clients it might as well be broadcast on IP level??? 6.1.2.7.3. 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. · gi.WriteChar(char) · gi.WriteByte(byte) · gi.WriteShort(short) · gi.WriteLong(long) · gi.WriteString(string) · gi.WritePosition(position) · gi.WriteDir(direction) · gi.WriteAngle(angle) 6.1.2.8. Client Side Prediction 6.1.2.8.1. Pmove This is one of he most important functions, gi.Pmove(pmove). It moves the player, according to information given in varaible pmove. 6.1.2.9. Misc. 6.1.2.9.1. configstring gi.configstring (index, string) serves as a general means of communication from server to all clients. The index parameter has the values: · CS_NAME, level name · CS_CDTRACK, cd sound track · CS_SKY, sky picture file name · CS_SKYAXIS, line around which sky rotates · CS_SKYROTATE, angular velocity for sky rotation · CS_STATUSBAR, either 'dm_statusbar' or 'single_statusbar' · CS_MAPCHECKSUM, appears to be unused. Intended for detecting 'cheater' maps (e.g. by CRC check), that is, maps with modified lightmaps and textures. This could also be used for identifying the registered PACK (POP - Proof Of Purchase), or to recognize out of date map revisions. · CS_MODELS, start index in config string table, for strings referring to BSP and MD2 files. · CS_SOUNDS, start index for sounds, WAV files. · CS_IMAGES, start index for images, e.g. PCX files. · CS_LIGHTS, send message giving the script that describes client side processed changes 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 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 adja­ cent configstring slots are used, and only the last one is \0 termi­ nated. 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. 6.1.2.9.2. DebugGraph gi.DebugGraph (value, colour) draws a graph??? Doesn't seem to do anything try turning debug mode on, a cvar. 6.1.2.9.3. error Error exit, gi.error (text, variables), shuts down the game and displays the error message. 6.1.2.9.4. TagMalloc 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. 6.1.2.9.5. FreeTags Clean up the pool/zone memory, release all allocated chunks that are labelled as/below the given tag/priority. Function is gi.FreeTags(tag). 6.1.2.9.6. TagFree The gi.TagFree(block) function does memory (de)allocation, is not used anywhere in code? 6.1.3. 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. 6.1.4. 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: · change the function pointers which are exported and effectively used (not everything exported might actually be called, there might be leftovers). · change the data in server-visible (parts of) the data structures. Everything else will be ignored by server and client. 6.1.4.1. Server Known Data Structs NOT DONE??? 6.1.4.2. Server Known Functions 6.1.4.2.1. GetGameApi Possibly the most important function, explained separately. See ???there. 6.1.4.3. Server Known Values/Variables 6.1.4.3.1. FRAMETIME As FRAMETIME is not exported to the server except at compiletime, changing it is not possible. 6.1.4.3.2. Version 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: ______________________________________________________________________ q_shared.h::entity_state_t q_shared.h::player_state_t game.h::gclient_t, as is game.h::edict_s, as is q_shared.h::pmove_state_t, as is. ______________________________________________________________________ Variables include: global. 6.2.1. Limitations for Modification Design The following observations have to be made: · the server can not be changed · the client can not be changed · the compile time visible parts of the Game Subsystem can not be changed when used, and changes will be ignored else · the Game Subsystem can hook modifications into the server visible functions which are called by the Server There are several restriction which should be noted in particular: · CSP (pmove) can not be changed · FRAMETIME can not be changed 7. Edicts and Entities 7.1. Spawn function LUT In the g_spawn module you will find a lookup like this: ______________________________________________________________________ typedef struct { char *name; void (*spawn)(edict_t *ent); } spawn_t; ______________________________________________________________________ followed by a list of void SP_* (edict_t *self) function prototypes which are implemented all over the G moldules. For example, SP_mon­ ster_* you will typically find at the end of the respective m_*.c mod­ ule implementing the monster in question. The spawn_t spawns[] Lookup Table in the same file assigns strings (as used in MAP files) to spawn function pointers. The strings are copied from the MAP file into the BSP file during processing the level, and the main engine will simply use this LUT to retrieve a function pointer for a string. It will hand the spawn function an already allocated edict_t, which is filled in by the spawn function. The reason why the spawn function is usually at the end of a module is that it stores all pointers to all other functions in the edict. There are no global protoypes for any of these, not even for the spawn functions. 7.2. The Server - Game Subsystem interface In game.h::game_export_t we find that the edict array is the only global variable currently shared between game and server. The edict array is allocated in the game dll, so it can vary in size from one game to another. The size has to be fixed when ge->Init() is called, which is by default g_save.c::InitGame() (the protoype is in g_main.c, where the pointer is set). The function is called when the dll is first loaded, which only happens when a new game is begun. We have the following global variables exported: ______________________________________________________________________ struct edict_s* edicts; int edict_size; // current number, <= max_edicts int num_edicts; int max_edicts; ______________________________________________________________________ and the Q2 DLL InitGame function handles them in the following way: ______________________________________________________________________ // initialize all entities for this game game.maxentities = maxentities->value; if (maxclients->value * 8 > game.maxentities) game.maxentities = maxclients->value * 8; g_edicts = gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME); globals.edicts = g_edicts; globals.max_edicts = game.maxentities; ... globals.num_edicts = game.maxclients+1; ______________________________________________________________________ The DLL global variable game_local_t game that is referred to here is previously filled by lookin up several CVARS, e.g. ______________________________________________________________________ maxentities = gi.cvar ("maxentities", "1024", CVAR_LATCH); ______________________________________________________________________ 7.3. Spawning during game If you consider item dropping, you will recognize that permanent entities can be created at runtime, as long as the fixed size edict table has empty slots. Take a look at g_item.c, namely: ______________________________________________________________________ edict_t *Drop_Item (edict_t *ent, gitem_t *item) { edict_t *dropped; vec3_t forward, right; vec3_t offset; dropped = G_Spawn(); ... } ______________________________________________________________________ In g_utils.c we find the edict_t *G_Spawn (void) function, which either finds a free edict, or allocates a new one. The comment advises us to try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into some­ thing else instead of being removed and recreated, which can cause interpolated angles and bad trails. This morphing effect might be desirable under some circumstances, though. 7.4. Freeing during game. The corresponding g_utils.c::G_FreeEdict() can be called to reset an edict. As the spawn function, this does not truly allocate/de-allocate - basically, the pair of functions implements a pool allocator using the edict array as a pool. These functions also keep you from trying to use entity 0 (also known as worldspawn) and other Really Bad Things. 7.5. Linking entities When you found a free entity and filled its fields with the proper values, you have to call gi.linkentity, a function imported from the main engine (server side), and opaque to the Game Subsystem. It will exist in the game until gi.unlinkentity is called. As the comments in game.h::game_import_t point out, an entity will never be sent to a client or used for collision detection if it is not passed to linkentity. Also, if the size, position, or solidity changes, it must be relinked. 7.6. OOP view of spawning The spawn function corresponds to a constructor, but as we do not have dynamic allocation, and use a fixed pool of edicts instead, it simply serves as filling in the memory lot. All monsters share the same edict structure (in fact, triggers, items, targets, everything does). The function pointers, e.g. ______________________________________________________________________ self->monsterinfo.stand = berserk_stand; self->monsterinfo.walk = berserk_walk; self->monsterinfo.run = berserk_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = NULL; self->monsterinfo.melee = berserk_melee; self->monsterinfo.sight = berserk_sight; self->monsterinfo.search = berserk_search; ______________________________________________________________________ can be considered member functions. In C++, the actual pointers would be stored in a VTABLE, and the object itself would only store a pointer to that table, which introduces another indirection. For vir­ tual functions, the situation would be identical to the one above/in Q2. 8. Physics and Simulation 8.1. Rotation and Angles in Q2 8.1.1. Basics To describe an orientation in 3D, three parameters are needed. In Q2, these are a yaw, pitch, and roll Euler angles, usually stored as float, in the range [0..360[ degrees. They are converted to 16bit and 8bit representations for network transmission. For temporary entities, it seems that orientation is stored in a single byte, with 162 possible orientations precalculated and stored as a normalised vector in a table that can be found in utils3/qdata/anorms.h. These are also normals used in Gouraud shading the MD2 triangles. Pitch, yaw, and roll are decoded as in the q_shared.h::AngleVectors function. If you reverse the math in that function, you will see that you apply yaw (rotation in XY plane), then pitch (look up/down), then roll (around LOS). That is, if you are facing down the +X axis with your axes aligned to the world axes, and you want to orient yourself with yaw, pitch, and roll, you turn around your +Z according to your yaw, you turn around your (new) +Y according to your pitch, then you turn around your (new again) +X according to your roll. This is why negative pitch means up - this is what makes sense if +Y is to your left, and you use the right hand rule to determine which way is positive. In theory, we would need two of these angle sets per entity: the orientation of the model/ object as such, as seen by others, and the direction of view/LOS. The direction of movement forward/backward is given by the forward direction of the model, strafing is just the left/right vector. However, Q2 uses a lot more angles internally, with several effects changing them, while at the same time not providing the full 3 degrees of freedom for view and orientation. 8.1.2. Global and Entity Parameters See e.g. p_view.c::SV_CalcRoll and p_view.c::SV_CalcViewOffset: · sv_rollangle · sv_rollspeed · run_pitch · run_roll · bob_pitch · bob_roll There are also view pitching time parameters, viewpoint offsets, bob_up shift, and more involved. Per entity, ideal_yaw and yaw_speed is used to handle turns towards a target. There are also angular velocities, and rotational friction, that affect all or some of the following. 8.1.3. Input Event Handling During a game, as a client, you turn yourself using your mouse, adjusting your pitch and yaw. You also define six keys to control forward, side, up movements. All inputs (button keypress events, mouse klick events aka mickeys) are interpreted by the client. This is different from DOOM, where the raw input data was transmitted to the other peers. Your current pitch, yaw, and zero roll, plus your forward, side, and up velocities according to the keys pressed and the cl_(forward|side|up)speed parameters, are placed into every usercmd_t sent to the server. The entire process (getting events, processing them, applying the result to the view and transmitting to to server) is done on every client side display refresh. There are no float values in the protocol. The angles are downsampled to 8 or 16 bit representing full 360 degrees. On the server side, this usercmd_t is interpreted by gi.Pmove. Pmove treats forward, side, and up differently depending on whether or not you are on the ground (as opposed to flymode and swimming). Theoretically you can define client-relative forward, side, and up vectors using the AngleVectors function, which would set the direction of motion along the LOS. However, Pmove does not use these angles, or the related vectors, directly. If you are not in flymode/swimming, but on ground, or jumping, Pmove projects the client's forward and side vectors onto the XY plane. Then it scales the projected forward vector so its length equals the client's forward velocity, and it scales the projected side vector so its length equals the client's side velocity. Then the velocity is clamped to sv_maxspeed (per axis, which gives anisotropic results up to 70% off). Finally the +Z axis is caled according to the client's up velocity. You get three vectors which describe the motion of the client. When in fly mode or underwater, motion is very screwy. One vector is the client's forward vector, scaled by the client's forward velocity. The second vector is the client's side vector, scaled by the client's side velocity. The third vector is the +Z axis, scaled by the client's up velocity. It's screwy because, if you're looking straight up, you only have one DOF in the XY plane, because the client's forward vector is parallel to the +Z axis. 8.1.4. View Model 8.1.4.1. Step 1: User Commands and clc_move The source of all angles is the user input. For each screen refresh, the client gets the input events buffered by the OS, processes the mouse and cursor events, handles mouse sensitivity, and does the effective rotation of view and object. The server only gets the overall result. The client sends the current 16bit angles to the server, as Pitch/Yaw/Roll, per refresh, in a clc_move message (that also contains the last, and last but one set already send, to cover network transmission errors). The server converts the incoming data into a In usercmd_s record, which contains: · short angles[3] This is view orientation, and does not define the direction of movement. 8.1.4.2. Step 2: ClientThink - convert, apply, and store In clc_move packets, there are three usercmd entries. The server calls ClientThink when it receives a clc_move, handing it all usercmd records not processed so far in the right sequence. The ClientThink function is the part of the server side client subsystem that has to make sure the server processes the user inputs exactly as the client (that is, it tries to stay as close as possible, as client to server latency might delay user inputs into another server frame). The function converts the 16 bit angles sent over to float and stores them in client_respawn_t as cmd_angles. ______________________________________________________________________ p_client.c::ClientThink client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); ______________________________________________________________________ The data received from the client is also referenced in the pmove_state_t before the server side emulation of the client side POV movement is called: ______________________________________________________________________ pm.cmd = *ucmd; gi.Pmove ( pm ); ______________________________________________________________________ This function call updates the viewangles in the record in the same way it did on the client for this particular frame. For a player that is alive (no killer_yaw fixed view, see separate discussion below), the new view angles are subsequently copied into the aiming angles, and into the playerstate viewangles. ______________________________________________________________________ VectorCopy (pm.viewangles, client->v_angle); VectorCopy (pm.viewangles, client->ps.viewangles); ______________________________________________________________________ From the client data, the information has thus moved on to the player avatar data in player_state_t. There are also delta angles offsetting the view, in the pmove_state_t contained in the player_state_t, as · short delta_angles[3] ???Does pmove change the delta_angles, too? How??? 8.1.4.3. Step 3: Update Player State The overall state data is scattered in client/player/ entity structs. On the topmost level, the player avatar is an entity like any other, stored in an edict_t. There we find an optional gclient_t pointer, which in turn refers to the client. The gclient_t struct contains the client_respawn_t record discussed above in Step 2, and the player_state_t we talked about in Step 3. In player_state_t we find: · vec3_t viewangles · vec3_t kick_angles · vec3_t gunangles The gunangles are Model View (weapons) and discussed separately. In gclient_t we find · vec3_t v_angle The changes the server applies to the angles can only become effective at a 10Hz rate related to the FRAMETIME interval. Unfortunately, the manipulations are rather confusing???. 8.1.4.3.1. Aiming Angles In gclient_t entry · vec3_t v_angle is annotated as aiming direction. Consequently, v_angle is used in p_weapon.c, in the various weapon_Fire functions. This has thus to be the true direction the player avatar is facing. Now, in p_view_c::ClientEndServerFrame, which is called for each player at the end of the server frame (before network transmission), and also right after spawning, the model angles are set from view angles so that other players in the world can tell which direction this particular avatar is looking: ______________________________________________________________________ if (ent->client->v_angle[PITCH] > 180) ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH])/3; else ent->s.angles[PITCH] = ent->client->v_angle[PITCH]/3; ent->s.angles[YAW] = ent->client->v_angle[YAW]; ent->s.angles[ROLL] = 0; // bogus ent->s.angles[ROLL] = SV_CalcRoll(ent->s.angles, ent->velocity)*4; ______________________________________________________________________ In other words - the aiming angle feeds back on the model orientation in the world. This is what you perceive as the slight tilt/nod of the player avatar when looking up or down - only 33 percent of the actual angle. In consequence, client's view orientation and the client's entity's orientation are not identical - if the client is looking straight up (90 degree pitch), the avatar/player entity is tipped back only 30 degrees. 8.1.4.3.2. Kick Angles In the per player record gclient_s, you will find · vec3_t kick_angles - damage, fall, weapon kicks, added to angles · float v_dmg_roll - to calculate kick_angles · float v_dmg_pitch - to calculate kick_angles Note: no v_dmg_yaw is there. There are two functions handling the v_dmg data: ______________________________________________________________________ p_weapon.c::weapon_bfg_fire // make a big pitch kick with an inverse fall ent->client->v_dmg_pitch = -40; ent->client->v_dmg_roll = crandom()*8;. p_view.c::P_DamageFeedback // calculate view angle kicks client->v_dmg_roll = kick*side*0.3; client->v_dmg_pitch = kick*side*0.3; ______________________________________________________________________ The resulting kick_angles are calculated in p_view.c::SV_CalcViewOffset, for a living player only: ______________________________________________________________________ // base angles - we rely on pointers here angles = ent->client->ps.kick_angles; // add angles based on weapon kick VectorCopy (ent->client->kick_angles, angles); // add angles based on damage kick angles[PITCH] += ratio * ent->client->v_dmg_pitch; angles[ROLL] += ratio * ent->client->v_dmg_roll; // add pitch based on fall kick angles[PITCH] += ratio * ent->client->fall_value; // add angles based on velocity angles[PITCH] += delta*run_pitch->value; angles[ROLL] += delta*run_roll->value; // add angles based on bob angles[PITCH] += delta; angles[ROLL] += delta; ______________________________________________________________________ Flags like DAMAGE_NO_KNOCKBACK control the effects on velocity and view angles. In the original source, no disabling of the latter was possible (no DAMAGE_NO_KICKANGLE whatsoever). 8.1.4.3.3. Delta Angles Remember delta angles in the pmove_state_t contained in the player_state_t, as · short delta_angles[3] These hold the angle difference between movement and view direction. You have to add the delta angles to the command angles to get the actual view direction. They are an offset to allow LOS changes while keeping the direction of movement. There are only two special cases (spwaning, teleporting) in which the server manipulates these. Ever changed??? 8.1.4.4. Step 4: Server Frame and svc_playerinfo Ultimately, the manipulated data has to be converted into an svc_playerinfo message, which follows the svc_spawnbaseline message. This packet contains: · short delta_angles[3] · vec3_t viewangles · vec3_t kick_angles · vec3_t gunangles The gunangles are Model View (weapons) related and discussed separately, they are simply derived from the player view and do not affect the POV. The player_state_t record corresponds directly to what is sent over the network, with the only exception that the floats are converted to chars/shorts. Player info (that sets the view and movement directions) is usually 16bit and more detailed than entity orientation (for NPC, monsters, objects). 8.1.4.5. Step 5: Client Side Interpolation The client smoothes out orientation changes how???. 8.1.4.6. Spawning Player A player is spawned by p_client.c::PutClientInServer by inheriting the angles from the spawn spot. ______________________________________________________________________ SelectSpawnPoint (ent, spawn_origin, spawn_angles); client->ps.pmove.delta_angles[i] = ANGLE2SHORT(spawn_angles[i] - client->resp.cmd_angles[i]); ent->s.angles[PITCH] = 0; ent->s.angles[YAW] = spawn_angles[YAW]; ent->s.angles[ROLL] = 0; VectorCopy (ent->s.angles, client->ps.viewangles); VectorCopy (ent->s.angles, client->v_angle); ______________________________________________________________________ Obviously, spawn spots set pitch and roll angles to zero implicitely or explicitely. The entitity_state_t object orientation angle copies only the yaw angle. The view angles and the aiming angles are set to the same values, but the persistent information in delta_angles is used to offset the view. This is one of only two cases of explicit manipulation of delta_angles by the server. 8.1.4.7. Teleporting Player the delta angles are also affected by teleports, see g_misc.c::teleporter_touch. ______________________________________________________________________ other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]); VectorClear (other->s.angles); VectorClear (other->client->ps.viewangles); VectorClear (other->client->v_angle); ______________________________________________________________________ Here, the orientation, view and aiming angle are all set to default. The delta angles are modified by offsetting against the orientation set by the destination spot. This is the other of only two cases of explicit manipulation of delta_angles by the server. 8.1.4.8. Dead Player The POV view angle handling is simplified a lot when the player is dead. In p_view.c::SV_CalcViewOffset, fixed viewangles are set. ______________________________________________________________________ ent->client->ps.viewangles[ROLL] = 40; ent->client->ps.viewangles[PITCH] = -15; ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; ______________________________________________________________________ The same code is duplicated in p_client.c::ClientThink. This indicates that player_state_t.viewangles sets the view angles in authorative 10Hz scv_frame updates. 8.1.4.9. Player Intermission View Another fixed view, handled client side in intermission mode, see level_locals_t, and therein. · vec3_t intermission_angle 8.1.5. Object Orientation The player orientation is tied to the view, and discussed within the View Model section. The same holds for in view models like weapons. The other objects do not have a view reference, and are not directly tied to one, so the basic handling of rotation of objects can be found here. 8.1.5.1. NPC Monsters and objects In edict_t, we find an optional gclient_t pointer, which in turn refers to the client and player state data and records. This is NULL for all NPC, so all angle information in those structs can safely be ignored. View angles/FOV are not taken into account when calculating AI (see g_ai.c::visible, which simply traces and returns true if an obscured LOS exists even when the monster has turned its back on the player). The overall movement direction is used for an infront test though, which effectively backface culls to a 180 degree field of vision (halfspace). All monster angles are thus model orientation angles. In edict_s we find: · vec3_t avelocity - angular velocity of ???model · vec3_t move_angles - ???only g_turret.c (and g_save.c) · float yaw_speed - ??? g_monster.c, m_move.c · float ideal_yaw - ??? g_ai.c, g_misc.c, g_monster.c, g_turret.c, m_ actor.c, m_boss2/31/32.c, m_move. sets self->s.angles[YAW] The "angle" entry is an unused Q1 leftover and can be ignored. In entity_state_s (which is contained in edict_s) we find · vec3_t angles - orientation. This is the only part of the edict_s struct visible to the server, thus it lists what gets transmitted to the client. These are converted into svc_packetentities (i.e. svc_spawnbaseline update) messages. Here, only 3 bytes are used per Euler angle. For temporary entities, a LUT of 162 normal vectors is used restricting orientations even further. 8.1.5.2. Weapon View Model orientation Yet another special treatment, this time for weapons as visible from the player's POV. In player_state_t of all places, we find: · vec3_t gunangles; Is affected by bobbing (p_view.c::SV_CalcGunOffset): ______________________________________________________________________ ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005; ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01; ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005; ______________________________________________________________________ and some more bobcycle handling. Ultimately, they hey are calculated relative to ______________________________________________________________________ delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i] // Roll gun on yaw changes. if (i == YAW) ent->client->ps.gunangles[ROLL] += 0.1*delta; // Lag weapon behind on turns??? ent->client->ps.gunangles[i] += 0.2 * delta; ______________________________________________________________________ Weapon angles are not affected by handedness, as mirroring of the weapon model is done completely client side (see Brian Hook's explana­ tion of the mirror transformation matrix used???). 8.1.6. How Do I Rotate The View? Not done. 8.1.7. How do I Rotate An Object? Not done. Change entity_states_t.angles. 8.1.8. How do I rotate a weapon? Not done. Change player_state_t.gunangles. 8.1.9. Miscellaneous Chase camera, LookAt functions - not done. See OpenGL GLU, and quaternion interpolation for smooth cameras, with tilt control and multiflips. 8.1.10. Not Done ______________________________________________________________________ vec3_t oldviewangles; pmove_t; // View/LOS angles (clamped). vec3_t viewangles; ______________________________________________________________________ 9. Configuration and Parameters 9.1. Configstrings 9.2. Console Variables 9.3. Client Console Commands To add a console command, you need to modify g_cmds.c::ClientCommand, which is the dispatcher for all commands. A console command is required, even if you want to just bind the command to a key (how???). By default, these commands get only the entity of the issuing client as a reference. You can implement wrappers to call a single function with various parameters (see inventory cycling functions). It is usually a good idea to implement the command as a toggle to switch a mode on/off without adding more overhead. 9.4. Server Console Commands New to 3.14. Use???. 10. Savegame/loadgame Persistence. Basically using a fixed link location provided us with an easy solution to an otherwise ugly problem: the game entity struct contains a fair number of pointers. John Cash: We know the "proper" was to handle it is with function name tables and marshalling all the pointers and all that jazz. I can explain this more later. Please don't waste a lot of time dwelling on it right now. Zoid sent me a "WTF?" email as soon as he saw it too ;-) Followup??? 11. Annotated Module Overview 11.1. The API Header There is a limited set of headers/functions visible to both server and client. GAME_INCLUDE determines whether certain definitions are provided short (server) or full (client). Header Module Purpose game.h n/a Game API information visible to serverand client. Game API Header Annotation: defines SVF flags, the edict SOLID flags, gclient_t and edict_s (short versions). Most importantly, it also declares game_export_t *GetGameApi (game_import_t *import); along with the game_export_t and game_import_t data structures. Leftovers: a linked list, MAX_ENT_CLUSTERS. In summary, the game library requests the functions from the main engines whose pointers are in game_import_t (and thus cannot be changed within the scope of the game libary). This includes: · special messages (on HUD, in OS shell, as sound) · server side configuration strings (transmitted to clients on joining) · "index for name" LUT's for models, sounds, images (created when spawning, readonly during game) · collision detection (notably the trace function, entity linking, BoxEdicts, and pmove) · network messaging · memory allocation · console variable interaction · ClientCommand and console command parameter checking, forwarding of commands to the server console process The functions within the scope of the game library and set in game_export_t (which thus can be changed within the scope of the game libary) include: · Init/Shutdown per game start (not level) · SpawnEntities per level start · Read/WriteGame for cross level persistency · Read/WriteLevel for client spawn spot persistency · Client functions like Connect/Disconnect · RunFrame, main server simulation loop. It also stores global variables shared between client and server, which can thus change from game to game. This is just the list of edicts, the current and maximum number of edicts, and edict_size, all of which are set by the InitGame function call. 11.2. The Quake2 DLL Shared Code This code is independend of the game library as such, and also reference to by other programs, like server, ???development tools, editor, and preprocessors. Header Module Purpose q_shared.h q_shared.c Code shared between Q2 programs. Shared Modules Annotation: The only system/libc includes are here in the header file, as is the usual stuff (NULL, boolean). Loads of defines (not necessarily in this order): · entity_state_t as provided by the server about all visible entities the client has to render · player_state_t as provided 10 times a second, as a basis for the relative information found in pmove_state_t which is done dependend on client frame rate (that is, client generated?) · trace_t returned by movement clipping, sweeping a bounding box through the world (clip) BSP · client side prediction pmove_state_t struct, and the pmove_t struct with the trace function pointer · PITCH, YAW, ROLL for angle (vectors?) · ANGLE2SHORT conversion used in network transmission · per level limits · string sizes · multicast enum · mathlib · vector types and macros, functions · Sys and COM (file/directory interface) · handling endianess · system timer · low level memory allocation · Cvar (console variables) · collision detection (hierarchy of content flags) · surface attributes (sky, warping) · plane data structures (and interface to assembler) · cmodel data structure · user commands, button codes · the entity_state_t effects, like: · EF (client side animation) · RF (client side rendering flags) · RDF (refresh flags for player_state_t, like being under water, affecting entire view) · MZ (muzzle flashes) and the reference to muzzle flash offset table · temporary entity messages (explosions, sparks) · sound channels · STAT (player statistics) and DF (deathmatch) flags · CS (configuration strings send by server) · EV (event types), entity_event_t Of particular interest is the pmtype_t classification to be take into account when processing pmove_state_t by client side movement prediction. The following attributes are there: ______________________________________________________________________ // can accelerate and turn PM_NORMAL, PM_SPECTATOR, // no acceleration or turning PM_DEAD, PM_GIB, // different bounding box PM_FREEZE ______________________________________________________________________ In addition, there are pmove->pm_flags, namely PMF_DUCKED and PMF_JUMP_HELD. 11.3. The Game (G) Modules The bulk of behaviour animation code and physics simulation, with only a single header file to boot. Annotation: the single header file contains stuff so various as: · most important, various client and the edict_s structs · the move_info_t struct, frame and monster info stuff · Timing (frame, view changes) · svc, SPAWN, FL, TAG, DEAD, RANGE, GIB, AI, AS, ARMOR, POWER, SFL, PNOISE, IT, ANIM flags · handedness Header Module Purpose g_local.h n/a Included by all game library modules.Local definitions/only header for all game modules. n/a g_main.c GetGameApi, RunFrame n/a g_phys.c Physics n/a g_cmds.c Console Commands? n/a g_func.c Functions? n/a g_utils.c Utility functions? n/a g_misc.c Miscellaneous n/a g_weapon.c Weapons n/a g_items.c Items, artifacts, powerups. n/a g_combat.c Combat? n/a g_target.c Targetting? n/a g_trigger.c Trigger objects, events. n/a g_spawn.c Spawning? n/a g_ai.c Enemy AI? n/a g_monster.c Enemy animation base code? n/a g_turret.c Turret (composite, rotating objects). n/a g_save.c Savegames, GameInit. Game (G) Modules · WEAPON, AMMO, DAMAGE, MOVETYPES enums · gitem_t struct · game_locals_t and level_locals_t for savegames · the spawn_temp_t struct to hold editor/map info not in the edcits (e.g. sky rotation). There is only one extern spawn_temp_t st in g_main.c, and (mostly read) access is only done in g_func.c. · loads of global variables using within game library (e.g. cvar_t's) · fields (savegame) · prototypes for the various functions defined in all the modules 11.4. The Play/Client (P) Modules Header Module Purpose g_local.h n/a Local definitions for game module,does also cover these. n/a p_client.c Player spawn spots, intermission view,player death and pain, body queue,client-server join. n/a p_hud.c Intermission, scoreboard, help,stats navgational aids? n/a p_trail.c Player trail, via points. n/a p_view.c Roll, damage indicator, view offset,gun offset, display blend in water etc.,falling damage, environment dependendplayer sounds etc., client effects.Offsets? n/a p_weapon.c Player noise objects for AI, weapon use,switch, drop, fire, hyperblaster rotationanimation. Play (P) Modules Annotation: None. 11.5. The Player Header Module Purpose m_player.h n/a player_x frames. m_actor.h n/a player_y frames. n/a m_actor.c Player avatar code. The Player Annotation: The m_player.h header is included in P (client, view, weapon) and G (commands) modules, but not in the actual m_actor.c implementation. The m_actor.h is used only there. Note that player_x and player_y actually correspond to the male/female models. The former has only 197 frames listed (and was created by another tool, qdata, only used for Tank and Iron Maiden, too), the latter has 480 frames listed, and was created by ModelGen as almost all the other monsters. The qdata tool was in the utils3 source release. Note that players/male/tris.md2 is assigned by default. Bounding box dimensions are set in SP_misc_actor, question is whether this could be changed at runtime. While there is loading of gender specific sounds (in g_spawn.c), and switching the sound directory accordingly (in p_client.c), I fail to see where the model is patched for female players. It might not happen at all in single player. It seems to be done outside the game library in deathmatch. 11.6. The Miscellaneous (M) Monster Modules Actually, these are shared/basic monster modules. Header Module Purpose g_local.h n/a Local definitions for game module,does also cover these. n/a m_move.c Monster movement code, SV and Mfunctions, step, chase. n/a m_flash.c Included in both in server and client code,LUT of muzzle offsets per monster. Misc. (M) Monster Modules Annotation: Don't think that these are tightly connected to the monster modules. There are only four direct calls in those modules: · m_mutant.c: M_CheckBottom · m_mutant.c: M_FlyCheck · m_infantry.c: M_FlyCheck · m_medic.c: M_CheckAttack The movement functions are mainly used in g_ai.c. The SV functions are actually used within the m_move.c, and there are a lot more in P and G modules (SV probably being a generic Server function prefix). As for the muzzle flashes, the server uses the offset for shot spawn/tracing, the client for rendering the muzzle flashes. The file is just a single LUT, monster_flash_offset[]. As it happens, this is used by any monster module for a shooting monster, in a ______________________________________________________________________ G_ProjectSource ( self->s.origin, monster_flash_offset[flash_number], forward, right, start); ______________________________________________________________________ call, a function defined in g_utils.c and used in AI and item code (drop item?), too. 11.7. The Monsters Annotation: In all cases the header file is automatically generated und lists the animation frames. As with the m_player.h header, qdata was used only for Tank and Iron Maiden. All other monster header files Header Module Purpose m_berserk.h m_berserk.c Berserker (walker) m_brain.h m_brain.c Brain (walker, tentacles) m_chick.h m_chick.c Iron Maiden/Female Cyborg (walker) m_flipper.h m_flipper.c Barracuda Shark (swimmer) m_float.h m_float.c Technician (floater, rigid body)? m_flyer.h m_flyer.c Flyer (floater, rigid body) m_gladiator.h m_gladiator.c Gladiator (bird walker) m_gunner.h m_gunner.c Gunner (walker) m_hover.h m_hover.c Icarus/Hover (floater) m_infantry.h m_infantry.c Enforcer (walker)? m_insane.h m_insane.c Insane Player (walker)? m_medic.h m_medic.c Medic (bird walker) m_mutant.h m_mutant.c Mutant (dog walker) m_parasite.h m_parasite.c Parasite (dog walker) m_soldier.h m_soldier.c Light/Machinegun/Shotgun Guard (all walker) m_supertank.h m_supertank.c Tank Commander (walker) m_tank.h m_tank.c Tank (walker) Monster Modules were generated by ModelGen, supposedly the previous tool superseeded by qdata, which was in the utils3 source release. In each case, the header file contains two comments added by the tool, a lot of FRAME defines for frame indices, and a final MODEL_SCALE, which is 1.0 except for the Gunner (1.15) and Guards/Soldier (1.2). The scale is assigned to self->monsterinfo.scale for each monster (SP functions). The modelindex and bounding boxes are also assigned for each monster in the same function call. 11.8. The Bosses In one case, the code provides an example of a composite. Header Module Purpose m_boss2.h m_boss2.c Boss2 n/a m_boss3.c Makron/Jorg composite, teleports. m_boss31.h m_boss31.c Jorg m_boss32.h m_boss32.c Makron - The Final Boss Boss Monster Modules Annotation: the file m_rider.h is not used, and is a leftover of the Boss3 development. All Boss3, Boss31 and Boss32 use the same models/monsters/boss3/rider/tris.md2 representation. The Boss31 also uses models/monsters/boss3/jorg/tris.md2 on the second modelindex. The headers are generated by ModelGen, see remarks in the monsters section above. Same structure. 12. DLL Source as HTML Using javadoc style comments and the doc++ tool (see Q2DP main document for tools and technology), there is an automatically created HTML reference of the Game Subsystem source available. Please refer to the table of contents and the top level document for details.