2016-03-27 11:49:47 +02:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Copyright ( C ) 1999 - 2005 Id Software , Inc .
This file is part of Quake III Arena source code .
Quake III Arena source code is free software ; you can redistribute it
and / or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 of the License ,
or ( at your option ) any later version .
Quake III Arena source code is distributed in the hope that it will be
useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Quake III Arena source code ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
//
# include "g_local.h"
2023-04-29 21:56:38 +02:00
# include "g_spawn.h"
# include "g_phys.h"
2016-03-27 11:49:47 +02:00
# include "debuglines.h"
# include "entity.h"
# include "gamecmds.h"
# include "dm_manager.h"
# include "player.h"
2023-01-29 22:57:04 +01:00
# include "scriptmaster.h"
2023-04-29 21:56:38 +02:00
# include "scriptexception.h"
2023-01-29 22:57:04 +01:00
# include "lightstyleclass.h"
2016-03-27 11:49:47 +02:00
# include "lodthing.h"
# include "viewthing.h"
# include "playerbot.h"
2023-01-30 00:23:47 +01:00
# include <tiki.h>
2016-03-27 11:49:47 +02:00
# ifdef WIN32
2023-08-15 01:40:06 +02:00
# include <intrin.h>
2016-03-27 11:49:47 +02:00
# endif
2023-08-15 01:40:06 +02:00
# define SAVEGAME_VERSION 80
2016-03-27 11:49:47 +02:00
# define PERSISTANT_VERSION 2
2023-08-15 01:40:06 +02:00
static char G_ErrorMessage [ 4096 ] ;
profGame_t G_profStruct ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
qboolean LoadingSavegame = false ;
qboolean LoadingServer = false ;
Archiver * currentArc = NULL ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
game_export_t globals ;
game_import_t gi ;
2016-03-27 11:49:47 +02:00
gentity_t active_edicts ;
gentity_t free_edicts ;
2023-08-15 01:40:06 +02:00
int sv_numtraces = 0 ;
int sv_numpmtraces = 0 ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
int g_protocol = 0 ;
gentity_t * g_entities ;
qboolean g_iInThinks = 0 ;
qboolean g_bBeforeThinks = qfalse ;
static float g_fMsecPerClock = 0 ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
usercmd_t * current_ucmd ;
usereyes_t * current_eyeinfo ;
Player * g_pPlayer ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
gclient_t g_clients [ MAX_CLIENTS ] ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
void ( * SV_Error ) ( int type , const char * fmt , . . . ) ;
void * ( * SV_Malloc ) ( int size ) ;
void ( * SV_Free ) ( void * ptr ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:27:35 +02:00
qboolean LevelArchiveValid ( Archiver & arc ) ;
2023-08-15 01:40:06 +02:00
void ClosePlayerLogFile ( void ) ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
void QDECL G_Printf ( const char * fmt , . . . )
{
va_list argptr ;
char text [ 1024 ] ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
va_start ( argptr , fmt ) ;
vsprintf ( text , fmt , argptr ) ;
va_end ( argptr ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
gi . Printf ( text ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void QDECL G_Error ( const char * fmt , . . . )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
va_list argptr ;
char text [ 1024 ] ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
va_start ( argptr , fmt ) ;
vsprintf ( text , fmt , argptr ) ;
va_end ( argptr ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
gi . Error ( ERR_DROP , text ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void QDECL G_Error ( int type , const char * fmt , . . . )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
va_list argptr ;
char text [ 1024 ] ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
va_start ( argptr , fmt ) ;
vsprintf ( text , fmt , argptr ) ;
va_end ( argptr ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// need to manually crash otherwise visual studio fuck up with the stack pointer...
//*( int * )0 = 0;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
assert ( ! text ) ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = =
G_ExitWithError
Calls the server ' s error function with the last error that occurred .
Should only be called after an exception .
= = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_ExitWithError ( const char * error )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
//ServerError( ERR_DROP, error );
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
Q_strncpyz ( G_ErrorMessage , error , sizeof ( G_ErrorMessage ) ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . errorMessage = G_ErrorMessage ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_RemapTeamShaders ( void )
{
2016-03-27 11:49:47 +02:00
# ifdef MISSIONPACK
2023-08-15 01:40:06 +02:00
char string [ 1024 ] ;
float f = level . time * 0.001 ;
Com_sprintf ( string , sizeof ( string ) , " team_icon/%s_red " , g_redteam . string ) ;
AddRemap ( " textures/ctf2/redteam01 " , string , f ) ;
AddRemap ( " textures/ctf2/redteam02 " , string , f ) ;
Com_sprintf ( string , sizeof ( string ) , " team_icon/%s_blue " , g_blueteam . string ) ;
AddRemap ( " textures/ctf2/blueteam01 " , string , f ) ;
AddRemap ( " textures/ctf2/blueteam02 " , string , f ) ;
gi . setConfigstring ( CS_SHADERSTATE , BuildShaderStateConfig ( ) ) ;
2016-03-27 11:49:47 +02:00
# endif
}
2023-08-15 01:40:06 +02:00
void G_SetFogInfo ( int cull , float distance , vec3_t farplane_color )
{
// cg.farplane_cull cg.farplane_distance cg.farplane_color[3]
// gi.SetConfigstring(CS_FOGINFO,va("%i %f %f %f %f",0,4096.f,1.f,1.f,1.f));
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_AllocGameData ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
int i ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// de-allocate from previous level
G_DeAllocGameData ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// Initialize debug lines
G_AllocDebugLines ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// Initialize debug strings
G_AllocDebugStrings ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// initialize all entities for this game
game . maxentities = maxentities - > integer ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
g_entities = ( gentity_t * ) gi . Malloc ( game . maxentities * sizeof ( g_entities [ 0 ] ) ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// clear out the entities
memset ( g_entities , 0 , game . maxentities * sizeof ( g_entities [ 0 ] ) ) ;
globals . gentities = g_entities ;
globals . max_entities = game . maxentities ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// Add all the edicts to the free list
LL_Reset ( & free_edicts , next , prev ) ;
LL_Reset ( & active_edicts , next , prev ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
for ( i = 0 ; i < game . maxentities ; i + + ) {
LL_Add ( & free_edicts , & g_entities [ i ] , next , prev ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// initialize all clients for this game
game . clients = ( gclient_t * ) gi . Malloc ( game . maxclients * sizeof ( game . clients [ 0 ] ) ) ;
memset ( game . clients , 0 , game . maxclients * sizeof ( game . clients [ 0 ] ) ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
for ( i = 0 ; i < game . maxclients ; i + + ) {
// set client fields on player ents
g_entities [ i ] . client = game . clients + i ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
G_InitClientPersistant ( & game . clients [ i ] ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . num_entities = game . maxclients ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// Tell the server about our data
gi . LocateGameData (
g_entities , globals . num_entities , sizeof ( gentity_t ) , & game . clients [ 0 ] . ps , sizeof ( game . clients [ 0 ] )
) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_DeAllocGameData ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
// Initialize debug lines
G_DeAllocDebugLines ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// free up the entities
if ( g_entities ) {
gi . Free ( g_entities ) ;
g_entities = NULL ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// free up the clients
if ( game . clients ) {
gi . Free ( game . clients ) ;
game . clients = NULL ;
}
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = =
G_InitGame
= = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_InitGame ( int levelTime , int randomSeed )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
G_Printf ( " ==== InitGame ==== \n " ) ;
G_Printf ( " gamename: %s \n " , GAMEVERSION ) ;
G_Printf ( " gamedate: %s \n " , __DATE__ ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
g_protocol = gi . Cvar_Get ( " com_protocol " , " " , 0 ) - > integer ;
2023-07-02 19:17:15 +02:00
2023-08-15 01:40:06 +02:00
srand ( randomSeed ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
CVAR_Init ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
game . Vars ( ) - > ClearList ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// set some level globals
level . svsStartTime = levelTime ;
level . specialgame = sv_specialgame - > integer ? true : false ;
if ( level . specialgame ) {
gi . cvar_set ( " protocol " , " 9 " ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
G_InitConsoleCommands ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
Director . Reset ( ) ;
Actor : : Init ( ) ;
PlayerBot : : Init ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
sv_numtraces = 0 ;
sv_numpmtraces = 0 ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( developer - > integer & & ! g_gametype - > integer ) {
Viewmodel . Init ( ) ;
LODModel . Init ( ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
game . maxentities = maxentities - > integer ;
if ( game . maxclients * 8 > maxentities - > integer ) {
game . maxentities = game . maxclients * 8 ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
game . maxclients = maxclients - > integer + maxbots - > integer ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
L_InitEvents ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
G_AllocGameData ( ) ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = =
G_SpawnEntities
= = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_SpawnEntities ( char * entities , int svsTime )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
level . SpawnEntities ( entities , svsTime ) ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = = =
G_ShutdownGame
= = = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_ShutdownGame ( )
{
gi . Printf ( " ==== ShutdownGame ==== \n " ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// write all the client session data so we can get it back
G_WriteSessionData ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
level . CleanUp ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
G_DeAllocDebugLines ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
/*
2016-03-27 11:49:47 +02:00
if ( g_entities )
{
gi . Free ( g_entities ) ;
g_entities = NULL ;
}
*/
2023-08-15 01:40:06 +02:00
if ( game . clients ) {
gi . Free ( game . clients ) ;
game . clients = NULL ;
}
2016-03-27 11:49:47 +02:00
}
//===================================================================
2023-08-15 01:40:06 +02:00
void QDECL Com_Error ( int level , const char * error , . . . )
{
va_list argptr ;
char text [ 1024 ] ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
va_start ( argptr , error ) ;
vsprintf ( text , error , argptr ) ;
va_end ( argptr ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
G_Error ( " %s " , text ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void QDECL Com_Printf ( const char * msg , . . . )
{
va_list argptr ;
char text [ 1024 ] ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
va_start ( argptr , msg ) ;
vsprintf ( text , msg , argptr ) ;
va_end ( argptr ) ;
2016-03-27 11:49:47 +02:00
2023-08-16 02:43:22 +02:00
gi . DPrintf ( " %s " , text ) ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = =
G_Precache
Calls precache scripts
= = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_Precache ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
level . Precache ( ) ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = =
G_Precache
Called when server finished initializating
= = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_ServerSpawned ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
level . ServerSpawned ( ) ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = =
G_AddGEntity
= = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_AddGEntity ( gentity_t * edict , qboolean showentnums )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
unsigned long long start , end ;
Entity * ent = edict - > entity ;
if ( g_timeents - > integer ) {
start = clock ( ) ;
G_RunEntity ( ent ) ;
end = clock ( ) ;
gi . DebugPrintf (
" %d: <%s> '%s'(%d) : %d clocks, %.1f msec \n " ,
level . framenum ,
ent - > getClassname ( ) ,
ent - > targetname . c_str ( ) ,
end - start ,
g_fMsecPerClock
) ;
} else {
G_RunEntity ( ent ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// remove the entity in case of invalid server flags
if ( ( edict - > r . svFlags & SVF_NOTSINGLECLIENT ) & & ( edict - > r . svFlags & SVF_CAPSULE ) ) {
ent - > PostEvent ( EV_Remove , 0 ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( showentnums ) {
G_DrawDebugNumber ( ent - > origin , ent - > entnum , 2.0f , 1.0f , 1.0f , 0.0f ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( g_entinfo - > integer
& & ( g_pPlayer & & ( edict - > r . lastNetTime > = level . inttime - 200 | | ent - > IsSubclassOfPlayer ( ) ) ) ) {
float fDist = ( g_pPlayer - > centroid - g_pPlayer - > EyePosition ( ) ) . length ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( fDist ! = 0.0f ) {
float fDot = _DotProduct ( g_vEyeDir , ( g_pPlayer - > centroid - g_pPlayer - > EyePosition ( ) ) ) ;
ent - > ShowInfo ( 0 , fDist ) ;
}
}
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = =
G_RunFrame
Advances the non - player objects in the world
= = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_RunFrame ( int levelTime , int frameTime )
{
gentity_t * edict ;
int num ;
qboolean showentnums ;
unsigned long long start ;
unsigned long long end ;
int i ;
static int processed [ MAX_GENTITIES ] = { 0 } ;
static int processedFrameID = 0 ;
try {
2023-07-30 14:27:15 +02:00
g_iInThinks = 0 ;
2023-08-15 01:40:06 +02:00
if ( g_showmem - > integer ) {
DisplayMemoryUsage ( ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// exit intermissions
if ( level . exitintermission ) {
if ( level . nextmap ! = level . current_map ) {
G_ExitLevel ( ) ;
} else {
G_RestartLevelWithDelay ( 0.1f ) ;
level . nextmap = " " ;
level . intermissiontime = 0 ;
level . exitintermission = qfalse ;
}
return ;
}
level . setFrametime ( frameTime ) ;
level . setTime ( levelTime ) ;
if ( level . intermissiontime | | level . died_already ) {
L_ProcessPendingEvents ( ) ;
for ( i = 0 , edict = g_entities ; i < game . maxclients ; i + + , edict + + ) {
if ( ! edict - > inuse | | ! edict - > client | | ! edict - > entity ) {
continue ;
}
edict - > entity - > CalcBlend ( ) ;
}
if ( g_gametype - > integer & & g_maxintermission - > value ! = 0.0f ) {
if ( level . time - level . intermissiontime > g_maxintermission - > value ) {
level . exitintermission = true ;
}
}
return ;
}
if ( g_scripttrace - > integer ) {
gi . DPrintf2 ( " ====SERVER FRAME========================================================================== \n " ) ;
}
g_bBeforeThinks = true ;
Director . iPaused = - 1 ;
// Process most of the events before the physics are run
// so that we can affect the physics immediately
L_ProcessPendingEvents ( ) ;
Director . iPaused = 1 ;
Director . SetTime ( level . inttime ) ;
//
// treat each object in turn
//
for ( edict = active_edicts . next , num = 0 ; edict ! = & active_edicts ; edict = edict - > next , num + + ) {
assert ( edict ) ;
assert ( edict - > inuse ) ;
assert ( edict - > entity ) ;
Actor * actor = ( Actor * ) edict - > entity ;
if ( actor - > IsSubclassOfActor ( ) ) {
actor - > m_bUpdateAnimDoneFlags = 0 ;
if ( actor - > m_bAnimating ) {
actor - > PreAnimate ( ) ;
}
}
}
g_iInThinks + + ;
Director . Unpause ( ) ;
g_iInThinks - - ;
// Process any pending events that got posted during the script code
L_ProcessPendingEvents ( ) ;
path_checksthisframe = 0 ;
// Reset debug lines
G_InitDebugLines ( ) ;
G_InitDebugStrings ( ) ;
PathManager . ShowNodes ( ) ;
showentnums = ( sv_showentnums - > integer & & ( ! g_gametype - > integer | | sv_cheats - > integer ) ) ;
g_iInThinks + + ;
processedFrameID + + ;
if ( g_entinfo - > integer ) {
g_pPlayer = ( Player * ) G_GetEntity ( 0 ) ;
if ( ! g_pPlayer - > IsSubclassOfPlayer ( ) ) {
g_pPlayer = NULL ;
} else {
Vector vAngles = g_pPlayer - > GetViewAngles ( ) ;
vAngles . AngleVectorsLeft ( & g_vEyeDir ) ;
}
}
if ( g_timeents - > integer ) {
g_fMsecPerClock = 1.0f / gi . Cvar_Get ( " CPS " , " 1 " , 0 ) - > value ;
start = clock ( ) ;
}
for ( edict = active_edicts . next ; edict ! = & active_edicts ; edict = edict - > next ) {
if ( edict - > entity - > IsSubclassOfBot ( ) ) {
G_BotThink ( edict , frameTime ) ;
}
}
for ( edict = active_edicts . next ; edict ! = & active_edicts ; edict = edict - > next ) {
num = edict - > s . parent ;
if ( num ! = ENTITYNUM_NONE ) {
while ( 1 ) {
if ( processed [ num ] = = processedFrameID ) {
break ;
}
processed [ num ] = processedFrameID ;
G_AddGEntity ( edict , showentnums ) ;
if ( edict - > s . parent = = ENTITYNUM_NONE ) {
break ;
}
}
}
if ( processed [ edict - g_entities ] ! = processedFrameID ) {
processed [ edict - g_entities ] = processedFrameID ;
G_AddGEntity ( edict , showentnums ) ;
}
}
if ( g_timeents - > integer ) {
gi . cvar_set ( " g_timeents " , va ( " %d " , g_timeents - > integer - 1 ) ) ;
end = clock ( ) ;
gi . DebugPrintf (
" \n %i total: %d (%.1f) \n ----------------------- \n " ,
level . framenum ,
end - start ,
static_cast < float > ( end - start ) * g_fMsecPerClock
) ;
}
g_iInThinks - - ;
g_bBeforeThinks = qfalse ;
// Process any pending events that got posted during the physics code.
L_ProcessPendingEvents ( ) ;
level . DoEarthquakes ( ) ;
// build the playerstate_t structures for all players
G_ClientEndServerFrames ( ) ;
level . Unregister ( STRING_POSTTHINK ) ;
// Process any pending events that got posted during the script code
L_ProcessPendingEvents ( ) ;
// show how many traces the game code is doing
if ( sv_traceinfo - > integer ) {
if ( sv_traceinfo - > integer = = 3 ) {
if ( sv_drawtrace - > integer < = 1 ) {
gi . DebugPrintf ( " %0.2f : Total traces %3d \n " , level . time , sv_numtraces ) ;
} else {
gi . DebugPrintf (
" %0.2f : Total traces %3d pmove traces %3d \n " , level . time , sv_numtraces , sv_numpmtraces
) ;
}
} else {
if ( sv_drawtrace - > integer < = 1 ) {
gi . DebugPrintf ( " %0.2f : Total traces %3d \n " , level . time , sv_numtraces ) ;
} else {
gi . DebugPrintf (
" %0.2f : Total traces %3d pmove traces %3d \n " , level . time , sv_numtraces , sv_numpmtraces
) ;
}
}
}
level . framenum + + ;
// reset out count of the number of game traces
sv_numtraces = 0 ;
sv_numpmtraces = 0 ;
G_ClientDrawBoundingBoxes ( ) ;
G_UpdateMatchEndTime ( ) ;
G_CheckExitRules ( ) ;
G_CheckStartRules ( ) ;
gi . setConfigstring ( CS_WARMUP , va ( " %.0f " , dmManager . GetMatchStartTime ( ) ) ) ;
if ( g_gametype - > integer ) {
level . CheckVote ( ) ;
}
if ( g_animdump - > integer ) {
for ( edict = active_edicts . next ; edict ! = & active_edicts ; edict = edict - > next ) {
Animate * anim = ( Animate * ) edict - > entity ;
if ( anim - > IsSubclassOfAnimate ( ) ) {
anim - > DumpAnimInfo ( ) ;
}
}
}
}
catch ( const char * error ) {
G_ExitWithError ( error ) ;
}
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = = =
G_ClientDrawBoundingBoxes
= = = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_ClientDrawBoundingBoxes ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
gentity_t * edict ;
Entity * ent ;
Vector eye ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// don't show bboxes during deathmatch
if ( ( ! sv_showbboxes - > integer ) | | ( g_gametype - > integer & & ! sv_cheats - > integer ) ) {
return ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( sv_showbboxes - > integer ) {
edict = g_entities ;
ent = edict - > entity ;
if ( ent ) {
eye = ent - > origin ;
ent = findradius ( NULL , eye , 1000 ) ;
while ( ent ) {
ent - > DrawBoundingBox ( sv_showbboxes - > integer ) ;
ent = findradius ( ent , eye , 1000 ) ;
}
}
}
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:27:35 +02:00
// Used to tell the server about the edict pose, such as the player pose
// so that G_Trace with tracedeep will set the location
void G_UpdatePoseInternal ( gentity_t * edict )
{
if ( edict - > s . number = = ENTITYNUM_NONE | | level . frame_skel_index ! = level . skel_index [ edict - > s . number ] ) {
gi . TIKI_SetPoseInternal (
edict - > tiki ,
edict - > s . number ,
edict - > s . frameInfo ,
edict - > s . bone_tag ,
edict - > s . bone_quat ,
edict - > s . actionWeight
) ;
}
}
orientation_t G_TIKI_Orientation ( gentity_t * edict , int num )
{
orientation_t orient ;
G_UpdatePoseInternal ( edict ) ;
orient = gi . TIKI_OrientationInternal ( edict - > tiki , edict - > s . number , num , edict - > s . scale ) ;
return orient ;
}
SkelMat4 * G_TIKI_Transform ( gentity_t * edict , int num )
{
G_UpdatePoseInternal ( edict ) ;
return ( SkelMat4 * ) gi . TIKI_TransformInternal ( edict - > tiki , edict - > s . number , num ) ;
}
qboolean G_TIKI_IsOnGround ( gentity_t * edict , int num , float threshold )
{
G_UpdatePoseInternal ( edict ) ;
return gi . TIKI_IsOnGroundInternal ( edict - > tiki , edict - > s . number , num , threshold ) ;
}
2023-08-15 01:40:06 +02:00
void G_PrepFrame ( void ) { }
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
void G_RegisterSounds ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
int startTime ;
int endTime ;
Com_Printf ( " \n \n -----------PARSING UBERSOUND (SERVER)------------ \n " ) ;
Com_Printf (
" Any SetCurrentTiki errors means that tiki wasn't prefetched and tiki-specific sounds for it won't work. To "
" fix prefetch the tiki. Ignore if you don't use that tiki on this level. \n "
) ;
startTime = gi . Milliseconds ( ) ;
G_Command_ProcessFile ( " ubersound/ubersound.scr " , qfalse ) ;
endTime = gi . Milliseconds ( ) ;
Com_Printf ( " Parse/Load time: %f seconds. \n " , ( float ) ( endTime - startTime ) / 1000.0 ) ;
Com_Printf ( " -------------UBERSOUND DONE (SERVER)--------------- \n \n " ) ;
Com_Printf ( " \n \n -----------PARSING UBERDIALOG (SERVER)------------ \n " ) ;
Com_Printf (
" Any SetCurrentTiki errors means that tiki wasn't prefetched and tiki-specific sounds for it won't work. To "
" fix prefetch the tiki. Ignore if you don't use that tiki on this level. \n "
) ;
startTime = gi . Milliseconds ( ) ;
G_Command_ProcessFile ( " ubersound/uberdialog.scr " , qfalse ) ;
endTime = gi . Milliseconds ( ) ;
Com_Printf ( " Parse/Load time: %f seconds. \n " , ( float ) ( endTime - startTime ) / 1000.0 ) ;
Com_Printf ( " -------------UBERDIALOG DONE (SERVER)--------------- \n \n " ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_Restart ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
G_InitWorldSession ( ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_SetFrameNumber ( int framenum )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
level . frame_skel_index = framenum ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_SetMap ( const char * mapname )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
level . SetMap ( mapname ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_SetTime ( int svsStartTime , int svsTime )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
if ( level . svsStartTime ! = svsTime ) {
gi . setConfigstring ( CS_LEVEL_START_TIME , va ( " %i " , svsTime ) ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
level . svsStartTime = svsStartTime ;
level . setTime ( svsTime ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:27:35 +02:00
/*
= = = = = = = = = = = = = = = = =
G_LevelArchiveValid
= = = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
qboolean G_LevelArchiveValid ( const char * filename )
2023-08-15 01:27:35 +02:00
{
2023-08-15 01:40:06 +02:00
try {
qboolean ret ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
Archiver arc ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
if ( ! arc . Read ( filename ) ) {
return qfalse ;
}
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
ret = LevelArchiveValid ( arc ) ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
arc . Close ( ) ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
return ret ;
}
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
catch ( const char * error ) {
G_ExitWithError ( error ) ;
return qfalse ;
}
2023-08-15 01:27:35 +02:00
}
2023-08-15 01:40:06 +02:00
void G_SoundCallback ( int entNum , soundChannel_t channelNumber , const char * name )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
gentity_t * ent = & g_entities [ entNum ] ;
Entity * entity = ent - > entity ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( ! entity ) {
ScriptError ( " ERROR: wait on playsound only works on entities that still exist when the sound is done playing. "
) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
entity - > CancelEventsOfType ( EV_SoundDone ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
Event * ev = new Event ( EV_SoundDone ) ;
ev - > AddInteger ( channelNumber ) ;
ev - > AddString ( name ) ;
entity - > PostEvent ( ev , level . frametime ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:27:35 +02:00
qboolean G_Command_ProcessFile ( const char * filename , qboolean quiet )
{
char * buffer ;
const char * bufstart ;
const char * token ;
int numTokens = 0 ;
if ( gi . FS_ReadFile ( filename , ( void * * ) & buffer , quiet ) = = - 1 ) {
return qfalse ;
}
if ( ! quiet ) {
gi . DPrintf ( " G_Command_ProcessFile: %s \n " , filename ) ;
}
bufstart = buffer ;
while ( 1 ) {
// grab each line as we go
token = COM_ParseExt ( & buffer , qtrue ) ;
if ( ! token [ 0 ] ) {
break ;
}
if ( ! Q_stricmp ( token , " end " ) | | ! Q_stricmp ( token , " server " ) ) {
// skip the line
while ( 1 ) {
token = COM_ParseExt ( & buffer , qfalse ) ;
if ( ! token [ 0 ] ) {
break ;
}
}
continue ;
}
// Create the event
Event ev ( token ) ;
// get the rest of the line
while ( 1 ) {
token = COM_ParseExt ( & buffer , qfalse ) ;
if ( ! token [ 0 ] ) {
break ;
}
ev . AddToken ( token ) ;
}
Director . ProcessEvent ( ev ) ;
}
gi . FS_FreeFile ( ( void * ) bufstart ) ;
return qtrue ;
}
2023-08-15 01:40:06 +02:00
qboolean G_AllowPaused ( void )
2016-03-27 11:49:47 +02:00
{
2019-06-30 14:22:57 +02:00
# ifdef _DEBUG
2023-08-15 01:40:06 +02:00
return false ;
2019-06-30 14:22:57 +02:00
# endif
2023-08-15 01:40:06 +02:00
return ( ! level . exitintermission ) & & ( level . intermissiontime = = 0.0f ) & & ( ! level . died_already ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_UpdateMatchEndTime ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
int endtime = 0 ;
if ( dmManager . GameHasRounds ( ) & & dmManager . GetRoundLimit ( ) ) {
endtime = dmManager . GetMatchStartTime ( ) * 1000.0f + ( level . svsStartTime + 60000 * dmManager . GetRoundLimit ( ) ) ;
} else if ( timelimit - > integer ) {
endtime = level . svsStartTime + 60000 * timelimit - > integer ;
}
if ( level . svsEndTime ! = endtime ) {
level . svsEndTime = endtime ;
gi . setConfigstring ( CS_MATCHEND , va ( " %i " , endtime ) ) ;
}
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_ArchiveFloat ( float * fl )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
currentArc - > ArchiveFloat ( fl ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_ArchiveInteger ( int * i )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
currentArc - > ArchiveInteger ( i ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_ArchiveString ( char * s )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
if ( currentArc - > Loading ( ) ) {
str string ;
currentArc - > ArchiveString ( & string ) ;
strcpy ( s , string . c_str ( ) ) ;
} else {
str string = s ;
currentArc - > ArchiveString ( & string ) ;
}
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_ArchiveSvsTime ( int * pi )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
currentArc - > ArchiveSvsTime ( pi ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
void G_ArchivePersistantData ( Archiver & arc )
{
gentity_t * ed ;
int i ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
for ( i = 0 ; i < game . maxclients ; i + + ) {
Entity * ent ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
ed = & g_entities [ i ] ;
if ( ! ed - > inuse | | ! ed - > entity ) {
continue ;
}
ent = ed - > entity ;
if ( ! ent - > IsSubclassOfPlayer ( ) ) {
continue ;
}
( ( Player * ) ent ) - > ArchivePersistantData ( arc ) ;
}
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_ArchivePersistant ( const char * name , qboolean loading )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
int version ;
Archiver arc ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( loading ) {
if ( ! arc . Read ( name , qfalse ) ) {
return ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
arc . ArchiveInteger ( & version ) ;
if ( version < PERSISTANT_VERSION ) {
gi . Printf ( " Persistant data from an older version (%d) of MOHAA. \n " , version ) ;
arc . Close ( ) ;
return ;
} else if ( version > PERSISTANT_VERSION ) {
gi . DPrintf ( " Persistant data from newer version %d of MOHAA. \n " , version ) ;
arc . Close ( ) ;
return ;
}
} else {
arc . Create ( name ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
version = PERSISTANT_VERSION ;
arc . ArchiveInteger ( & version ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
arc . ArchiveObject ( game . Vars ( ) ) ;
G_ArchivePersistantData ( arc ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
arc . Close ( ) ;
return ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
qboolean G_ReadPersistant ( const char * name )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
try {
G_ArchivePersistant ( name , qtrue ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
catch ( const char * error ) {
G_ExitWithError ( error ) ;
}
return qfalse ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = =
G_WritePersistant
This will be called whenever the game goes to a new level ,
A single player death will automatically restore from the
last save position .
= = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_WritePersistant ( const char * name )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
try {
G_ArchivePersistant ( name , qfalse ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
catch ( const char * error ) {
G_ExitWithError ( error ) ;
}
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void G_Cleanup ( qboolean samemap )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
gi . Printf ( " ==== CleanupGame ==== \n " ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
G_WriteSessionData ( ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
level . CleanUp ( samemap , qtrue ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void ArchiveAliases ( Archiver & arc )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
int i ;
byte another ;
AliasList_t * alias_list ;
AliasListNode_t * alias_node ;
str alias_name ;
str model_name ;
const char * name ;
dtikianim_t * modelanim ;
Container < dtikianim_t * > animlist ;
if ( arc . Saving ( ) ) {
for ( i = 0 ; i < MAX_MODELS ; i + + ) {
name = gi . getConfigstring ( CS_MODELS + i ) ;
if ( name & & * name & & * name ! = ' * ' ) {
const char * p = name ;
while ( true ) {
p = strchr ( name , ' | ' ) ;
if ( ! p ) {
return ;
}
name = p + 1 ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
modelanim = gi . modeltikianim ( name ) ;
if ( modelanim & & ! animlist . IndexOfObject ( modelanim ) ) {
animlist . AddObject ( modelanim ) ;
alias_list = ( AliasList_t * ) modelanim - > alias_list ;
if ( alias_list ) {
alias_node = alias_list - > data_list ;
if ( alias_node ) {
another = true ;
arc . ArchiveByte ( & another ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
alias_name = name ;
arc . ArchiveString ( & alias_name ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
for ( ; alias_node ! = NULL ; alias_node = alias_node - > next ) {
another = true ;
arc . ArchiveByte ( & another ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
alias_name = alias_node - > alias_name ;
arc . ArchiveString ( & alias_name ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
another = false ;
arc . ArchiveByte ( & another ) ;
}
}
}
}
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
another = false ;
arc . ArchiveByte ( & another ) ;
} else {
arc . ArchiveByte ( & another ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
while ( another ) {
arc . ArchiveString ( & model_name ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
modelanim = gi . modeltikianim ( model_name . c_str ( ) ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
arc . ArchiveByte ( & another ) ;
while ( another ) {
// Read in aliases
arc . ArchiveString ( & alias_name ) ;
gi . Alias_UpdateDialog ( modelanim , alias_name . c_str ( ) ) ;
arc . ArchiveByte ( & another ) ;
}
arc . ArchiveByte ( & another ) ;
}
}
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:27:35 +02:00
/*
= = = = = = = = = = = = = = = = =
LevelArchiveValid
= = = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
qboolean LevelArchiveValid ( Archiver & arc )
2023-08-15 01:27:35 +02:00
{
2023-08-15 01:40:06 +02:00
int version ;
int savegame_version ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
// read the version number
arc . ArchiveInteger ( & version ) ;
arc . ArchiveInteger ( & savegame_version ) ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
if ( version < GAME_API_VERSION ) {
gi . Printf ( " Savegame from an older version (%d) of MOHAA. \n " , version ) ;
return qfalse ;
} else if ( version > GAME_API_VERSION ) {
gi . Printf ( " Savegame from version %d of MOHAA. \n " , version ) ;
return qfalse ;
}
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
if ( savegame_version < SAVEGAME_VERSION ) {
gi . Printf ( " Savegame from an older version (%d) of MoHAA. \n " , version ) ;
return qfalse ;
} else if ( savegame_version > SAVEGAME_VERSION ) {
gi . Printf ( " Savegame from version %d of MoHAA. \n " , version ) ;
return qfalse ;
}
return qtrue ;
2023-08-15 01:27:35 +02:00
}
2016-03-27 11:49:47 +02:00
/*
= = = = = = = = = = = = = = = = =
G_ArchiveLevel
= = = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
qboolean G_ArchiveLevel ( const char * filename , qboolean autosave , qboolean loading )
{
try {
int i ;
int num ;
Archiver arc ;
gentity_t * edict ;
char szSaveName [ MAX_STRING_TOKENS ] ;
const char * pszSaveName ;
cvar_t * cvar ;
COM_StripExtension ( filename , szSaveName , sizeof ( szSaveName ) ) ;
pszSaveName = COM_SkipPath ( szSaveName ) ;
gi . cvar_set ( " g_lastsave " , pszSaveName ) ;
if ( loading ) {
LoadingSavegame = true ;
arc . Read ( filename ) ;
if ( ! LevelArchiveValid ( arc ) ) {
arc . Close ( ) ;
return qfalse ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
// Read in the pending events. These are read in first in case
// later objects need to post events.
L_UnarchiveEvents ( arc ) ;
} else {
int temp ;
arc . Create ( filename ) ;
// write out the version number
temp = GAME_API_VERSION ;
arc . ArchiveInteger ( & temp ) ;
temp = SAVEGAME_VERSION ;
arc . ArchiveInteger ( & temp ) ;
// Write out the pending events. These are written first in case
// later objects need to post events when reading the archive.
L_ArchiveEvents ( arc ) ;
}
if ( arc . Saving ( ) ) {
str s ;
num = 0 ;
for ( cvar = gi . NextCvar ( NULL ) ; cvar ! = NULL ; cvar = gi . NextCvar ( cvar ) ) {
if ( cvar - > flags & CVAR_ROM ) {
num + + ;
}
}
arc . ArchiveInteger ( & num ) ;
for ( cvar = gi . NextCvar ( NULL ) ; cvar ! = NULL ; cvar = gi . NextCvar ( cvar ) ) {
if ( cvar - > flags & CVAR_ROM ) {
s = cvar - > name ;
arc . ArchiveString ( & s ) ;
s = cvar - > string ;
arc . ArchiveString ( & s ) ;
arc . ArchiveBoolean ( & cvar - > modified ) ;
arc . ArchiveInteger ( & cvar - > modificationCount ) ;
arc . ArchiveFloat ( & cvar - > value ) ;
arc . ArchiveInteger ( & cvar - > integer ) ;
}
}
} else {
str sName , sValue ;
arc . ArchiveInteger ( & num ) ;
for ( int i = 0 ; i < num ; i + + ) {
arc . ArchiveString ( & sName ) ;
arc . ArchiveString ( & sValue ) ;
cvar = gi . cvar_set2 ( sName , sValue , qfalse ) ;
arc . ArchiveBoolean ( & cvar - > modified ) ;
arc . ArchiveInteger ( & cvar - > modificationCount ) ;
arc . ArchiveFloat ( & cvar - > value ) ;
arc . ArchiveInteger ( & cvar - > integer ) ;
}
}
// archive the game object
arc . ArchiveObject ( & game ) ;
// archive Level
arc . ArchiveObject ( & level ) ;
// archive camera paths
arc . ArchiveObject ( & CameraMan ) ;
// archive paths
arc . ArchiveObject ( & PathManager ) ;
// archive script controller
arc . ArchiveObject ( & Director ) ;
// archive lightstyles
arc . ArchiveObject ( & lightStyles ) ;
if ( arc . Saving ( ) ) {
// count the entities
num = 0 ;
for ( i = 0 ; i < globals . num_entities ; i + + ) {
edict = & g_entities [ i ] ;
if ( edict - > inuse & & edict - > entity & & ! ( edict - > entity - > flags & FL_DONTSAVE ) ) {
num + + ;
}
}
}
// archive all the entities
arc . ArchiveInteger ( & globals . num_entities ) ;
arc . ArchiveInteger ( & num ) ;
if ( arc . Saving ( ) ) {
// write out the world
arc . ArchiveObject ( world ) ;
for ( i = 0 ; i < globals . num_entities ; i + + ) {
edict = & g_entities [ i ] ;
if ( ! edict - > inuse | | ! edict - > entity | | ( edict - > entity - > flags & FL_DONTSAVE ) ) {
continue ;
}
arc . ArchiveObject ( edict - > entity ) ;
}
} else {
// Tell the server about our data
gi . LocateGameData (
g_entities , globals . num_entities , sizeof ( gentity_t ) , & game . clients [ 0 ] . ps , sizeof ( game . clients [ 0 ] )
) ;
// read in the world
arc . ReadObject ( ) ;
// FIXME: PathSearch::LoadNodes();
//PathSearch::LoadNodes();
for ( i = 0 ; i < num ; i + + ) {
arc . ReadObject ( ) ;
}
}
ArchiveAliases ( arc ) ;
currentArc = & arc ;
gi . ArchiveLevel ( arc . Loading ( ) ) ;
currentArc = NULL ;
// FIXME: PathSearch::ArchiveDynamic();
//PathSearch::ArchiveDynamic();
arc . Close ( ) ;
if ( arc . Loading ( ) ) {
LoadingSavegame = false ;
gi . Printf ( HUD_MESSAGE_YELLOW " %s \n " , gi . LV_ConvertString ( " Game Loaded " ) ) ;
} else {
gi . Printf ( HUD_MESSAGE_YELLOW " %s \n " , gi . LV_ConvertString ( " Game Saved " ) ) ;
}
if ( arc . Loading ( ) ) {
// Make sure all code that needs to setup the player after they have been loaded is run
for ( i = 0 ; i < game . maxclients ; i + + ) {
edict = & g_entities [ i ] ;
if ( edict - > inuse & & edict - > entity ) {
Player * player = ( Player * ) edict - > entity ;
player - > Loaded ( ) ;
}
}
}
return qtrue ;
}
catch ( const char * error ) {
G_ExitWithError ( error ) ;
}
return qfalse ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = = =
G_WriteLevel
= = = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
void G_WriteLevel ( const char * filename , qboolean autosave )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
game . autosaved = autosave ;
G_ArchiveLevel ( filename , autosave , qfalse ) ;
game . autosaved = false ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = = =
G_ReadLevel
SpawnEntities will already have been called on the
level the same way it was when the level was saved .
That is necessary to get the baselines set up identically .
The server will have cleared all of the world links before
calling ReadLevel .
No clients are connected yet .
= = = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
qboolean G_ReadLevel ( const char * filename )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
qboolean status ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
status = G_ArchiveLevel ( filename , qfalse , qtrue ) ;
// if the level load failed make sure that these variables are not set
if ( ! status ) {
LoadingSavegame = false ;
LoadingServer = false ;
}
return status ;
2016-03-27 11:49:47 +02:00
}
/*
= = = = = = = = = = = = = = = =
GetGameAPI
Gets game imports and returns game exports
= = = = = = = = = = = = = = = =
*/
2023-08-15 01:40:06 +02:00
extern " C " game_export_t * GetGameAPI ( game_import_t * import )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
gi = * import ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . apiversion = GAME_API_VERSION ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . AllowPaused = G_AllowPaused ;
globals . ArchiveFloat = G_ArchiveFloat ;
globals . ArchiveInteger = G_ArchiveInteger ;
globals . ArchivePersistant = G_ArchivePersistant ;
globals . ArchiveString = G_ArchiveString ;
globals . ArchiveSvsTime = G_ArchiveSvsTime ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . BotBegin = G_BotBegin ;
globals . BotThink = G_BotThink ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . Cleanup = G_Cleanup ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . ClientCommand = G_ClientCommand ;
globals . ClientConnect = G_ClientConnect ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . ClientBegin = G_ClientBegin ;
globals . ClientThink = G_ClientThink ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . ClientDisconnect = G_ClientDisconnect ;
globals . ClientThink = G_ClientThink ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . ClientUserinfoChanged = G_ClientUserinfoChanged ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . ConsoleCommand = G_ConsoleCommand ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . DebugCircle = G_DebugCircle ;
globals . errorMessage = NULL ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . gentities = g_entities ;
globals . gentitySize = sizeof ( g_entities [ 0 ] ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . Init = G_InitGame ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . LevelArchiveValid = G_LevelArchiveValid ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . Precache = G_Precache ;
globals . SpawnEntities = G_SpawnEntities ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . PrepFrame = G_PrepFrame ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . profStruct = & G_profStruct ;
globals . ReadLevel = G_ReadLevel ;
globals . WriteLevel = G_WriteLevel ;
globals . RegisterSounds = G_RegisterSounds ;
globals . Restart = G_Restart ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . RunFrame = G_RunFrame ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . ServerSpawned = G_ServerSpawned ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . SetFrameNumber = G_SetFrameNumber ;
globals . SetMap = G_SetMap ;
globals . SetTime = G_SetTime ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . Shutdown = G_ShutdownGame ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
globals . SoundCallback = G_SoundCallback ;
globals . SpawnEntities = G_SpawnEntities ;
globals . TIKI_Orientation = G_TIKI_Orientation ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
return & globals ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:27:35 +02:00
/*
= = = = = = = = = = = = = = = = =
G_ClientEndServerFrames
= = = = = = = = = = = = = = = = =
*/
void G_ClientEndServerFrames ( void )
{
2023-08-15 01:40:06 +02:00
int i ;
gentity_t * ent ;
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
// calc the player views now that all pushing
// and damage has been added
for ( i = 0 ; i < game . maxclients ; i + + ) {
ent = g_entities + i ;
if ( ! ent - > inuse | | ! ent - > client | | ! ent - > entity ) {
continue ;
}
2023-08-15 01:27:35 +02:00
2023-08-15 01:40:06 +02:00
ent - > entity - > EndFrame ( ) ;
}
2023-08-15 01:27:35 +02:00
}
void G_ClientDoBlends ( )
{
2023-08-15 01:40:06 +02:00
// FIXME: unimplemented
2023-08-15 01:27:35 +02:00
}
void FindIntermissionPoint ( )
{
2023-08-15 01:40:06 +02:00
// FIXME: unimplemented
2023-08-15 01:27:35 +02:00
}
void G_MoveClientToIntermission ( Entity * ent )
{
G_DisplayScores ( ent ) ;
ent - > flags | = FL_IMMOBILE ;
}
void G_DisplayScores ( Entity * ent )
{
ent - > client - > ps . pm_flags | = PMF_INTERMISSION ;
}
void G_HideScores ( Entity * ent )
{
ent - > client - > ps . pm_flags & = ~ PMF_INTERMISSION ;
}
2023-08-15 01:40:06 +02:00
void G_BeginIntermission2 ( void )
{
gentity_t * client ;
Entity * ent ;
int i ;
if ( level . intermissiontime ) {
return ;
}
level . playerfrozen = qtrue ;
level . intermissiontime = level . time ;
ent = ( Entity * ) G_FindClass ( NULL , " info_player_intermission " ) ;
G_FadeSound ( 4.0f ) ;
if ( ent ) {
SetCamera ( ent , 0.5f ) ;
} else {
G_FadeOut ( 2.0f ) ;
}
for ( i = 0 , client = g_entities ; i < game . maxclients ; i + + , client + + ) {
if ( ! client - > inuse | | ! client - > entity | | ! client - > client ) {
continue ;
}
ent = client - > entity ;
G_DisplayScores ( ent ) ;
ent - > flags | = FL_IMMOBILE ;
}
}
void G_BeginIntermission ( const char * map_name , INTTYPE_e transtype , bool no_fade )
{
Entity * camera ;
Entity * node ;
Event * ev ;
gentity_t * client ;
int i ;
if ( level . intermissiontime | | g_gametype - > integer ) {
return ;
}
level . intermissiontime = level . time ;
level . intermissiontype = transtype ;
if ( ! no_fade ) {
G_FadeOut ( 2.0f ) ;
}
G_FadeSound ( 4.0f ) ;
level . nextmap = map_name ;
camera = ( Entity * ) G_FindClass ( NULL , " info_player_intermission " ) ;
if ( camera ) {
SetCamera ( camera , 0.5f ) ;
ev = new Event ( EV_Camera_Orbit ) ;
node = ( Entity * ) G_FindTarget ( NULL , " endnode1 " ) ;
if ( node & & node - > IsSubclassOfEntity ( ) ) {
ev - > AddEntity ( node ) ;
camera - > ProcessEvent ( ev ) ;
camera - > ProcessEvent ( EV_Camera_Cut ) ;
}
}
for ( i = 0 , client = g_entities ; i < game . maxclients ; client + + , i + + ) {
if ( ! client - > inuse | | ! client - > entity ) {
continue ;
}
client - > entity - > flags | = FL_IMMOBILE ;
client - > entity - > PostEvent ( EV_Player_EnterIntermission , 3.0f ) ;
}
}
void G_ExitLevel ( void )
{
static const char * seps = " , \n \r " ;
char command [ 256 ] ;
int j ;
gentity_t * ent ;
// Don't allow exit level if the mission was failed
if ( level . mission_failed ) {
return ;
}
// close the player log file if necessary
ClosePlayerLogFile ( ) ;
// kill the sounds
Com_sprintf ( command , sizeof ( command ) , " stopsound \n " ) ;
gi . SendConsoleCommand ( command ) ;
if ( g_gametype - > integer ) {
if ( strlen ( sv_nextmap - > string ) ) {
// The nextmap cvar was set (possibly by a vote - so go ahead and use it)
level . nextmap = sv_nextmap - > string ;
gi . cvar_set ( " nextmap " , " " ) ;
} else // Use the next map in the maplist
{
char * s , * f , * t ;
f = NULL ;
s = strdup ( sv_maplist - > string ) ;
t = strtok ( s , seps ) ;
while ( t ! = NULL ) {
if ( ! Q_stricmp ( t , level . mapname . c_str ( ) ) ) {
// it's in the list, go to the next one
t = strtok ( NULL , seps ) ;
if ( t = = NULL ) // end of list, go to first one
{
if ( f = = NULL ) // there isn't a first one, same level
{
level . nextmap = level . mapname ;
} else {
level . nextmap = f ;
}
} else {
level . nextmap = t ;
}
free ( s ) ;
goto out ;
}
// set the first map
if ( ! f ) {
f = t ;
}
t = strtok ( NULL , seps ) ;
}
free ( s ) ;
}
out :
// level.nextmap should be set now, but if it isn't use the same map
if ( level . nextmap . length ( ) = = 0 ) {
// Stay on the same map since no nextmap was set
Com_sprintf ( command , sizeof ( command ) , " restart \n " ) ;
gi . SendConsoleCommand ( command ) ;
} else // use the level.nextmap variable
{
Com_sprintf ( command , sizeof ( command ) , " gamemap \" %s \" \n " , level . nextmap . c_str ( ) ) ;
gi . SendConsoleCommand ( command ) ;
}
} else {
Com_sprintf ( command , sizeof ( command ) , " gamemap \" %s \" \n " , level . nextmap . c_str ( ) ) ;
gi . SendConsoleCommand ( command ) ;
}
// Tell all the clients that the level is done
for ( j = 0 ; j < game . maxclients ; j + + ) {
ent = & g_entities [ j ] ;
if ( ! ent - > inuse | | ! ent - > entity ) {
continue ;
}
ent - > entity - > ProcessEvent ( EV_Player_EndLevel ) ;
}
level . nextmap = " " ;
level . exitintermission = 0 ;
level . intermissiontime = 0 ;
G_ClientEndServerFrames ( ) ;
}
void G_CheckIntermissionExit ( void )
{
if ( ! level . exitintermission & & g_maxintermission - > value > level . time - level . intermissiontime ) {
return ;
}
if ( level . nextmap ! = level . current_map ) {
G_ExitLevel ( ) ;
} else {
G_RestartLevelWithDelay ( 0.1f ) ;
level . nextmap = " " ;
level . intermissiontime = 0 ;
level . exitintermission = qfalse ;
}
}
void G_ExitIntermission ( void )
{
level . exitintermission = qtrue ;
}
void G_CheckStartRules ( void )
{
if ( ( ! dmManager . IsGameActive ( ) ) & & ( ! dmManager . WaitingForPlayers ( ) ) ) {
dmManager . StartRound ( ) ;
}
}
void G_CheckExitRules ( void )
{
if ( g_gametype - > integer ) {
if ( level . intermissiontime = = 0.0f ) {
dmManager . CheckEndMatch ( ) ;
} else {
G_CheckIntermissionExit ( ) ;
}
}
}
2023-08-15 01:27:35 +02:00
void G_DisplayScoresToAllClients ( void )
{
gentity_t * ent ;
int i ;
for ( i = 0 , ent = g_entities ; i < game . maxclients ; ent + + , i + + ) {
if ( ! ent - > inuse | | ! ent - > entity ) {
continue ;
}
G_DisplayScores ( ent - > entity ) ;
}
}
void G_HideScoresToAllClients ( void )
{
gentity_t * ent ;
int i ;
for ( i = 0 , ent = g_entities ; i < game . maxclients ; ent + + , i + + ) {
if ( ! ent - > inuse | | ! ent - > entity ) {
continue ;
}
G_HideScores ( ent - > entity ) ;
}
}
2016-03-27 11:49:47 +02:00
# ifndef WIN32
2023-08-15 01:40:06 +02:00
# include <signal.h>
# include <sys / mman.h>
2016-03-27 11:49:47 +02:00
struct sigaction origSignalActions [ NSIG ] ;
2023-08-15 01:40:06 +02:00
int backtrace ( void * * buffer , int size )
{
return 0 ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
char * * backtrace_symbols ( void * const * buffer , int size )
{
return nullptr ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
void backtrace_symbols_fd ( void * const * buffer , int size , int fd ) { }
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
void resetsighandlers ( void )
{
sigaction ( SIGSEGV , & origSignalActions [ SIGSEGV ] , NULL ) ;
sigaction ( SIGFPE , & origSignalActions [ SIGFPE ] , NULL ) ;
sigaction ( SIGILL , & origSignalActions [ SIGILL ] , NULL ) ;
sigaction ( SIGBUS , & origSignalActions [ SIGBUS ] , NULL ) ;
sigaction ( SIGABRT , & origSignalActions [ SIGABRT ] , NULL ) ;
sigaction ( SIGSYS , & origSignalActions [ SIGSYS ] , NULL ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
void sighandler ( int sig , siginfo_t * info , void * secret )
{
void * trace [ 100 ] ;
char * * messages = ( char * * ) NULL ;
int i , trace_size = 0 ;
//ucontext_t *uc = (ucontext_t *)secret;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
char * signame = strsignal ( sig ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
/* Do something useful with siginfo_t */
if ( sig = = SIGSEGV ) {
printf ( " Got signal %d - %s, faulty address is %p \n " , sig , signame , info - > si_addr ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( gi . Printf ! = NULL ) {
gi . Printf ( " Got signal %d - %s, faulty address is %p \n " , sig , signame , info - > si_addr ) ;
}
} else {
if ( gi . Printf ! = NULL ) {
gi . Printf ( " Got signal %d - %s \n " , sig , signame ) ;
} else {
printf ( " Got signal %d - %s \n " , sig , signame ) ;
}
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
trace_size = backtrace ( trace , 100 ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
/* overwrite sigaction with caller's address */
//trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
messages = ( char * * ) ( long ) backtrace_symbols ( trace , trace_size ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
printf ( " Execution path: \n " ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( gi . Printf ! = NULL ) {
gi . Printf ( " Execution path: \n " ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
for ( i = 1 ; i < trace_size ; + + i ) { /* skip first stack frame (points here) */
printf ( " --> %s \n " , messages [ i ] ) ;
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
if ( gi . Printf ! = NULL ) {
gi . Printf ( " --> %s \n " , messages [ i ] ) ;
}
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
resetsighandlers (
) ; // reset original MOHAA or default signal handlers, so no more signals are handled by this handler (a signal is considered to be a fatal program error, the original implementations should exit)
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
raise ( sig ) ;
}
2016-03-27 11:49:47 +02:00
2023-08-15 01:40:06 +02:00
void initsighandlers ( void )
{
/* Install our signal handlers */
struct sigaction sa ;
sa . sa_sigaction = sighandler ;
sigemptyset ( & sa . sa_mask ) ;
sa . sa_flags = SA_RESTART | SA_SIGINFO ;
sigaction ( SIGSEGV , & sa , & origSignalActions [ SIGSEGV ] ) ;
sigaction ( SIGFPE , & sa , & origSignalActions [ SIGFPE ] ) ;
sigaction ( SIGILL , & sa , & origSignalActions [ SIGILL ] ) ;
sigaction ( SIGBUS , & sa , & origSignalActions [ SIGBUS ] ) ;
sigaction ( SIGABRT , & sa , & origSignalActions [ SIGABRT ] ) ;
sigaction ( SIGSYS , & sa , & origSignalActions [ SIGSYS ] ) ;
2016-03-27 11:49:47 +02:00
}
2023-08-15 01:40:06 +02:00
void __attribute__ ( ( constructor ) ) load ( void )
2016-03-27 11:49:47 +02:00
{
2023-08-15 01:40:06 +02:00
initsighandlers ( ) ;
2016-03-27 11:49:47 +02:00
}
# endif