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
# include <intrin.h>
# endif
# define SAVEGAME_VERSION 80
# define PERSISTANT_VERSION 2
static char G_ErrorMessage [ 4096 ] ;
profGame_t G_profStruct ;
qboolean LoadingSavegame = false ;
qboolean LoadingServer = false ;
Archiver * currentArc = NULL ;
gameExport_t globals ;
gameImport_t gi ;
gentity_t active_edicts ;
gentity_t free_edicts ;
int sv_numtraces = 0 ;
int sv_numpmtraces = 0 ;
gentity_t * g_entities ;
qboolean g_iInThinks = 0 ;
qboolean g_bBeforeThinks = qfalse ;
static float g_fMsecPerClock = 0 ;
usercmd_t * current_ucmd ;
usereyes_t * current_eyeinfo ;
Player * g_pPlayer ;
gclient_t g_clients [ MAX_CLIENTS ] ;
void ( * SV_Error ) ( int type , const char * fmt , . . . ) ;
void * ( * SV_Malloc ) ( int size ) ;
void ( * SV_Free ) ( void * ptr ) ;
void QDECL G_Printf ( const char * fmt , . . . ) {
va_list argptr ;
char text [ 1024 ] ;
va_start ( argptr , fmt ) ;
vsprintf ( text , fmt , argptr ) ;
va_end ( argptr ) ;
gi . Printf ( text ) ;
}
void QDECL G_Error ( const char * fmt , . . . )
{
va_list argptr ;
char text [ 1024 ] ;
va_start ( argptr , fmt ) ;
vsprintf ( text , fmt , argptr ) ;
va_end ( argptr ) ;
gi . Error ( ERR_DROP , text ) ;
}
void QDECL G_Error ( int type , const char * fmt , . . . )
{
va_list argptr ;
char text [ 1024 ] ;
va_start ( argptr , fmt ) ;
vsprintf ( text , fmt , argptr ) ;
va_end ( argptr ) ;
// need to manually crash otherwise visual studio fuck up with the stack pointer...
2019-06-29 23:43:30 +02:00
//*( int * )0 = 0;
2016-03-27 11:49:47 +02:00
2019-06-29 23:43:30 +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 .
= = = = = = = = = = = = = = =
*/
void G_ExitWithError ( const char * error )
{
//ServerError( ERR_DROP, error );
Q_strncpyz ( G_ErrorMessage , error , sizeof ( G_ErrorMessage ) ) ;
globals . errorMessage = G_ErrorMessage ;
}
void G_RemapTeamShaders ( void ) {
# ifdef MISSIONPACK
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 ( ) ) ;
# endif
}
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));
}
void G_AllocGameData ( void )
{
int i ;
// de-allocate from previous level
G_DeAllocGameData ( ) ;
// Initialize debug lines
G_AllocDebugLines ( ) ;
// Initialize debug strings
G_AllocDebugStrings ( ) ;
// initialize all entities for this game
game . maxentities = maxentities - > integer ;
2023-02-01 00:28:40 +01:00
g_entities = ( gentity_t * ) gi . Malloc ( game . maxentities * sizeof ( g_entities [ 0 ] ) ) ;
2016-03-27 11:49:47 +02:00
// clear out the entities
2023-02-01 00:28:40 +01:00
memset ( g_entities , 0 , game . maxentities * sizeof ( g_entities [ 0 ] ) ) ;
2016-03-27 11:49:47 +02:00
globals . gentities = g_entities ;
globals . max_entities = game . maxentities ;
// Add all the edicts to the free list
LL_Reset ( & free_edicts , next , prev ) ;
LL_Reset ( & active_edicts , next , prev ) ;
for ( i = 0 ; i < game . maxentities ; i + + )
{
LL_Add ( & free_edicts , & g_entities [ i ] , next , prev ) ;
}
// 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 ] ) ) ;
for ( i = 0 ; i < game . maxclients ; i + + )
{
// set client fields on player ents
g_entities [ i ] . client = game . clients + i ;
G_InitClientPersistant ( & game . clients [ i ] ) ;
}
globals . num_entities = game . maxclients ;
// Tell the server about our data
gi . LocateGameData ( g_entities , globals . num_entities , sizeof ( gentity_t ) , & game . clients [ 0 ] . ps , sizeof ( game . clients [ 0 ] ) ) ;
}
void G_DeAllocGameData ( void )
{
// Initialize debug lines
G_DeAllocDebugLines ( ) ;
// free up the entities
if ( g_entities )
{
gi . Free ( g_entities ) ;
g_entities = NULL ;
}
// free up the clients
if ( game . clients )
{
gi . Free ( game . clients ) ;
game . clients = NULL ;
}
}
/*
= = = = = = = = = = = =
G_InitGame
= = = = = = = = = = = =
*/
void G_InitGame ( int levelTime , int randomSeed )
{
2019-06-29 23:43:30 +02:00
G_Printf ( " ==== InitGame ==== \n " ) ;
2016-03-27 11:49:47 +02:00
G_Printf ( " gamename: %s \n " , GAMEVERSION ) ;
G_Printf ( " gamedate: %s \n " , __DATE__ ) ;
srand ( randomSeed ) ;
CVAR_Init ( ) ;
game . Vars ( ) - > ClearList ( ) ;
// set some level globals
level . svsStartTime = levelTime ;
level . reborn = sv_reborn - > integer ? true : false ;
if ( level . reborn )
{
gi . Cvar_Set ( " protocol " , " 9 " ) ;
}
G_InitConsoleCommands ( ) ;
Director . Reset ( ) ;
Actor : : Init ( ) ;
PlayerBot : : Init ( ) ;
sv_numtraces = 0 ;
2019-06-29 23:43:30 +02:00
sv_numpmtraces = 0 ;
2016-03-27 11:49:47 +02:00
if ( developer - > integer & & ! g_gametype - > integer )
{
Viewmodel . Init ( ) ;
LODModel . Init ( ) ;
}
game . maxentities = maxentities - > integer ;
if ( game . maxclients * 8 > maxentities - > integer )
{
game . maxentities = game . maxclients * 8 ;
}
game . maxclients = maxclients - > integer + maxbots - > integer ;
L_InitEvents ( ) ;
G_AllocGameData ( ) ;
}
/*
= = = = = = = = = = = =
G_SpawnEntities
= = = = = = = = = = = =
*/
void G_SpawnEntities ( char * entities , int svsTime )
{
level . SpawnEntities ( entities , svsTime ) ;
}
/*
= = = = = = = = = = = = = = = = =
G_ShutdownGame
= = = = = = = = = = = = = = = = =
*/
void G_ShutdownGame ( ) {
gi . Printf ( " ==== ShutdownGame ==== \n " ) ;
// write all the client session data so we can get it back
G_WriteSessionData ( ) ;
level . CleanUp ( ) ;
G_DeAllocDebugLines ( ) ;
/*
if ( g_entities )
{
gi . Free ( g_entities ) ;
g_entities = NULL ;
}
*/
if ( game . clients )
{
gi . Free ( game . clients ) ;
game . clients = NULL ;
}
}
//===================================================================
void QDECL Com_Error ( int level , const char * error , . . . ) {
va_list argptr ;
char text [ 1024 ] ;
va_start ( argptr , error ) ;
vsprintf ( text , error , argptr ) ;
va_end ( argptr ) ;
G_Error ( " %s " , text ) ;
}
void QDECL Com_Printf ( const char * msg , . . . ) {
va_list argptr ;
char text [ 1024 ] ;
va_start ( argptr , msg ) ;
vsprintf ( text , msg , argptr ) ;
va_end ( argptr ) ;
G_Printf ( " %s " , text ) ;
}
/*
= = = = = = = = = = = = = = = =
G_Precache
Calls precache scripts
= = = = = = = = = = = = = = = =
*/
void G_Precache ( void )
{
level . Precache ( ) ;
}
/*
= = = = = = = = = = = = = = = =
G_Precache
Called when server finished initializating
= = = = = = = = = = = = = = = =
*/
void G_ServerSpawned ( void )
{
level . ServerSpawned ( ) ;
}
void G_CheckExitRules ( void )
{
if ( g_gametype - > integer )
{
if ( level . intermissiontime = = 0.0f )
{
dmManager . CheckEndMatch ( ) ;
}
else
{
G_CheckIntermissionExit ( ) ;
}
}
}
void G_CheckStartRules ( void )
{
if ( ( ! dmManager . IsGameActive ( ) ) & & ( ! dmManager . WaitingForPlayers ( ) ) )
{
dmManager . StartRound ( ) ;
}
}
/*
= = = = = = = = = = = = = = = =
G_AddGEntity
= = = = = = = = = = = = = = = =
*/
void G_AddGEntity ( gentity_t * edict , qboolean showentnums )
{
unsigned long long start , end ;
Entity * ent = edict - > entity ;
if ( g_timeents - > integer )
{
2023-02-01 19:28:57 +01:00
start = clock ( ) ;
2016-03-27 11:49:47 +02:00
G_RunEntity ( ent ) ;
2023-02-01 19:28:57 +01:00
end = clock ( ) ;
2016-03-27 11:49:47 +02:00
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 ) ;
}
// 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 ) ;
}
if ( showentnums )
{
G_DrawDebugNumber ( ent - > origin , ent - > entnum , 2.0f , 1.0f , 1.0f , 0.0f ) ;
}
if ( g_entinfo - > integer & &
( g_pPlayer & &
( edict - > r . lastNetTime > = level . inttime - 200 | | ent - > IsSubclassOfPlayer ( ) ) ) )
{
float fDist = ( g_pPlayer - > centroid - g_pPlayer - > EyePosition ( ) ) . length ( ) ;
if ( fDist ! = 0.0f )
{
float fDot = _DotProduct ( g_vEyeDir , ( g_pPlayer - > centroid - g_pPlayer - > EyePosition ( ) ) ) ;
ent - > ShowInfo ( 0 , fDist ) ;
}
}
}
/*
= = = = = = = = = = = = = = = =
G_RunFrame
Advances the non - player objects in the world
= = = = = = = = = = = = = = = =
*/
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
{
// 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 ;
2019-06-29 23:43:30 +02:00
if ( actor - > m_bAnimating )
2016-03-27 11:49:47 +02:00
actor - > PreAnimate ( ) ;
}
}
Director . iPaused - - ;
g_iInThinks + + ;
if ( ! Director . iPaused )
{
Director . ExecuteRunning ( ) ;
}
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 ;
2023-02-01 19:28:57 +01:00
start = clock ( ) ;
2016-03-27 11:49:47 +02:00
}
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 ) ) ;
2023-02-01 19:28:57 +01:00
end = clock ( ) ;
2016-03-27 11:49:47 +02:00
gi . DebugPrintf ( " \n %i total: %d (%.1f) \n ----------------------- \n " ,
2023-06-22 18:32:15 +02:00
level . framenum , end - start , static_cast < float > ( end - start ) * g_fMsecPerClock ) ;
2016-03-27 11:49:47 +02:00
}
g_iInThinks - - ;
g_bBeforeThinks = qfalse ;
// Process any pending events that got posted during the physics code.
L_ProcessPendingEvents ( ) ;
level . DoEarthquakes ( ) ;
2023-02-01 19:28:57 +01:00
2016-03-27 11:49:47 +02:00
// 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 ) ;
}
}
/*
= = = = = = = = = = = = = = = = =
G_ClientDrawBoundingBoxes
= = = = = = = = = = = = = = = = =
*/
void G_ClientDrawBoundingBoxes
(
void
)
{
gentity_t * edict ;
Entity * ent ;
Vector eye ;
// don't show bboxes during deathmatch
if ( ( ! sv_showbboxes - > integer ) | | ( g_gametype - > integer & & ! sv_cheats - > integer ) )
{
return ;
}
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 ) ;
}
}
}
}
void G_PrepFrame ( void )
{
}
void G_RegisterSounds ( void )
{
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 " ) ;
}
void G_Restart ( void )
{
G_InitWorldSession ( ) ;
}
void G_SetFrameNumber ( int framenum )
{
level . frame_skel_index = framenum ;
}
void G_SetMap ( const char * mapname )
{
level . SetMap ( mapname ) ;
}
void G_SetTime ( int svsStartTime , int svsTime )
{
if ( level . svsStartTime ! = svsTime )
{
gi . SetConfigstring ( CS_LEVEL_START_TIME , va ( " %i " , svsTime ) ) ;
}
level . svsStartTime = svsStartTime ;
level . setTime ( svsTime ) ;
}
void G_SoundCallback ( int entNum , soundChannel_t channelNumber , const char * name )
{
gentity_t * ent = & g_entities [ entNum ] ;
Entity * entity = ent - > entity ;
if ( ! entity )
{
ScriptError ( " ERROR: wait on playsound only works on entities that still exist when the sound is done playing. " ) ;
}
entity - > CancelEventsOfType ( EV_SoundDone ) ;
Event * ev = new Event ( EV_SoundDone ) ;
ev - > AddInteger ( channelNumber ) ;
ev - > AddString ( name ) ;
entity - > PostEvent ( ev , level . frametime ) ;
}
qboolean G_AllowPaused ( void )
{
2019-06-30 14:22:57 +02:00
# ifdef _DEBUG
return false ;
# endif
2016-03-27 11:49:47 +02:00
return ( ! level . exitintermission ) & & ( level . intermissiontime = = 0.0f ) & & ( ! level . died_already ) ;
}
void G_ArchiveFloat ( float * fl )
{
currentArc - > ArchiveFloat ( fl ) ;
}
void G_ArchiveInteger ( int * i )
{
currentArc - > ArchiveInteger ( i ) ;
}
void G_ArchiveString ( char * s )
{
if ( currentArc - > Loading ( ) )
{
str string ;
currentArc - > ArchiveString ( & string ) ;
strcpy ( s , string . c_str ( ) ) ;
}
else
{
str string = s ;
currentArc - > ArchiveString ( & string ) ;
}
}
void G_ArchiveSvsTime ( int * pi )
{
currentArc - > ArchiveSvsTime ( pi ) ;
}
void G_ArchivePersistantData
(
Archiver & arc
)
{
gentity_t * ed ;
int i ;
for ( i = 0 ; i < game . maxclients ; i + + )
{
Entity * ent ;
ed = & g_entities [ i ] ;
if ( ! ed - > inuse | | ! ed - > entity )
continue ;
ent = ed - > entity ;
if ( ! ent - > IsSubclassOfPlayer ( ) )
continue ;
( ( Player * ) ent ) - > ArchivePersistantData ( arc ) ;
}
}
void G_ArchivePersistant ( const char * name , qboolean loading )
{
int version ;
Archiver arc ;
if ( loading )
{
if ( ! arc . Read ( name , qfalse ) )
{
return ;
}
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 ) ;
version = PERSISTANT_VERSION ;
arc . ArchiveInteger ( & version ) ;
}
arc . ArchiveObject ( game . Vars ( ) ) ;
G_ArchivePersistantData ( arc ) ;
arc . Close ( ) ;
return ;
}
qboolean G_ReadPersistant
(
const char * name
)
{
try
{
G_ArchivePersistant ( name , qtrue ) ;
}
catch ( const char * error )
{
G_ExitWithError ( error ) ;
}
return qfalse ;
}
/*
= = = = = = = = = = = =
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 .
= = = = = = = = = = = =
*/
void G_WritePersistant
(
const char * name
)
{
try
{
G_ArchivePersistant ( name , qfalse ) ;
}
catch ( const char * error )
{
G_ExitWithError ( error ) ;
}
}
/*
= = = = = = = = = = = = = = = = =
LevelArchiveValid
= = = = = = = = = = = = = = = = =
*/
qboolean LevelArchiveValid
(
Archiver & arc
)
{
int version ;
int savegame_version ;
// read the version number
arc . ArchiveInteger ( & version ) ;
arc . ArchiveInteger ( & savegame_version ) ;
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 ;
}
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 ;
}
void G_Cleanup ( qboolean samemap )
{
gi . Printf ( " ==== CleanupGame ==== \n " ) ;
G_WriteSessionData ( ) ;
level . CleanUp ( samemap , qtrue ) ;
}
void ArchiveAliases
(
Archiver & arc
)
{
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 ;
2018-09-17 23:50:38 +02:00
while ( true )
2016-03-27 11:49:47 +02:00
{
p = strchr ( name , ' | ' ) ;
2018-09-17 23:50:38 +02:00
if ( ! p )
{
return ;
}
name = p + 1 ;
2016-03-27 11:49:47 +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 ) ;
alias_name = name ;
arc . ArchiveString ( & alias_name ) ;
for ( ; alias_node ! = NULL ; alias_node = alias_node - > next )
{
another = true ;
arc . ArchiveByte ( & another ) ;
alias_name = alias_node - > alias_name ;
arc . ArchiveString ( & alias_name ) ;
}
another = false ;
arc . ArchiveByte ( & another ) ;
}
}
}
}
}
another = false ;
arc . ArchiveByte ( & another ) ;
}
else
{
arc . ArchiveByte ( & another ) ;
while ( another )
{
arc . ArchiveString ( & model_name ) ;
modelanim = gi . modeltikianim ( model_name . c_str ( ) ) ;
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 ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = =
G_ArchiveLevel
= = = = = = = = = = = = = = = = =
*/
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 ] ;
2017-02-19 21:14:27 +01:00
const char * pszSaveName ;
2016-03-27 11:49:47 +02:00
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 ;
}
// 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 ;
}
/*
= = = = = = = = = = = = = = = = =
G_WriteLevel
= = = = = = = = = = = = = = = = =
*/
void G_WriteLevel
(
const char * filename ,
qboolean autosave
)
{
game . autosaved = autosave ;
G_ArchiveLevel ( filename , autosave , qfalse ) ;
game . autosaved = false ;
}
/*
= = = = = = = = = = = = = = = = =
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 .
= = = = = = = = = = = = = = = = =
*/
qboolean G_ReadLevel
(
const char * filename
)
{
qboolean status ;
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 ;
}
/*
= = = = = = = = = = = = = = = = =
G_LevelArchiveValid
= = = = = = = = = = = = = = = = =
*/
qboolean G_LevelArchiveValid
(
const char * filename
)
{
try
{
qboolean ret ;
Archiver arc ;
if ( ! arc . Read ( filename ) )
{
return qfalse ;
}
ret = LevelArchiveValid ( arc ) ;
arc . Close ( ) ;
return ret ;
}
catch ( const char * error )
{
G_ExitWithError ( error ) ;
return qfalse ;
}
}
/*
= = = = = = = = = = = = = = = =
GetGameAPI
Gets game imports and returns game exports
= = = = = = = = = = = = = = = =
*/
extern " C "
2023-01-29 20:59:31 +01:00
gameExport_t * GetGameAPI ( gameImport_t * import )
2016-03-27 11:49:47 +02:00
{
gi = * import ;
globals . apiversion = GAME_API_VERSION ;
globals . AllowPaused = G_AllowPaused ;
globals . ArchiveFloat = G_ArchiveFloat ;
globals . ArchiveInteger = G_ArchiveInteger ;
globals . ArchivePersistant = G_ArchivePersistant ;
globals . ArchiveString = G_ArchiveString ;
globals . ArchiveSvsTime = G_ArchiveSvsTime ;
globals . BotBegin = G_BotBegin ;
globals . BotThink = G_BotThink ;
globals . Cleanup = G_Cleanup ;
globals . ClientCommand = G_ClientCommand ;
globals . ClientConnect = G_ClientConnect ;
globals . ClientBegin = G_ClientBegin ;
globals . ClientThink = G_ClientThink ;
globals . ClientDisconnect = G_ClientDisconnect ;
globals . ClientThink = G_ClientThink ;
globals . ClientUserinfoChanged = G_ClientUserinfoChanged ;
globals . ConsoleCommand = G_ConsoleCommand ;
globals . DebugCircle = G_DebugCircle ;
globals . errorMessage = NULL ;
globals . gentities = g_entities ;
2023-02-01 00:28:40 +01:00
globals . gentitySize = sizeof ( g_entities [ 0 ] ) ;
2016-03-27 11:49:47 +02:00
globals . Init = G_InitGame ;
globals . LevelArchiveValid = G_LevelArchiveValid ;
globals . Precache = G_Precache ;
globals . SpawnEntities = G_SpawnEntities ;
globals . PrepFrame = G_PrepFrame ;
globals . profStruct = & G_profStruct ;
globals . ReadLevel = G_ReadLevel ;
globals . WriteLevel = G_WriteLevel ;
globals . RegisterSounds = G_RegisterSounds ;
globals . Restart = G_Restart ;
globals . RunFrame = G_RunFrame ;
globals . ServerSpawned = G_ServerSpawned ;
globals . SetFrameNumber = G_SetFrameNumber ;
globals . SetMap = G_SetMap ;
globals . SetTime = G_SetTime ;
globals . Shutdown = G_ShutdownGame ;
globals . SoundCallback = G_SoundCallback ;
globals . SpawnEntities = G_SpawnEntities ;
globals . TIKI_Orientation = G_TIKI_Orientation ;
return & globals ;
}
# ifndef WIN32
# include <signal.h>
# include <sys/mman.h>
struct sigaction origSignalActions [ NSIG ] ;
2023-02-01 00:28:40 +01:00
int backtrace ( void * * buffer , int size ) { return 0 ; }
char * * backtrace_symbols ( void * const * buffer , int size ) { return nullptr ; }
2016-03-27 11:49:47 +02:00
void backtrace_symbols_fd ( void * const * buffer , int size , int fd ) { }
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 ) ;
}
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;
char * signame = strsignal ( sig ) ;
/* Do something useful with siginfo_t */
if ( sig = = SIGSEGV ) {
printf ( " Got signal %d - %s, faulty address is %p \n " , sig , signame , info - > si_addr ) ;
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 ) ;
}
}
trace_size = backtrace ( trace , 100 ) ;
/* overwrite sigaction with caller's address */
//trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
messages = ( char * * ) ( long ) backtrace_symbols ( trace , trace_size ) ;
printf ( " Execution path: \n " ) ;
if ( gi . Printf ! = NULL ) {
gi . Printf ( " Execution path: \n " ) ;
}
for ( i = 1 ; i < trace_size ; + + i ) { /* skip first stack frame (points here) */
printf ( " --> %s \n " , messages [ i ] ) ;
if ( gi . Printf ! = NULL ) {
gi . Printf ( " --> %s \n " , messages [ i ] ) ;
}
}
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)
raise ( sig ) ;
}
void initsighandlers ( void ) {
/* Install our signal handlers */
struct sigaction sa ;
2023-02-01 00:28:40 +01:00
sa . sa_sigaction = sighandler ;
2016-03-27 11:49:47 +02:00
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 ] ) ;
}
void __attribute__ ( ( constructor ) ) load ( void )
{
initsighandlers ( ) ;
}
# endif