2019-03-13 19:20:07 +00:00
/*
2020-06-04 21:01:28 +00:00
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2019-03-13 19:20:07 +00:00
Copyright ( C ) 1997 - 2001 Id Software , Inc .
2020-06-04 21:01:28 +00:00
This file is part of Quake 2 source code .
2019-03-13 19:20:07 +00:00
2020-06-04 21:01:28 +00:00
Quake 2 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 .
2019-03-13 19:20:07 +00:00
2020-06-04 21:01:28 +00:00
Quake 2 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 .
2019-03-13 19:20:07 +00:00
You should have received a copy of the GNU General Public License
2020-06-04 21:01:28 +00:00
along with Quake 2 source code ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2019-03-13 19:20:07 +00:00
*/
2020-06-04 21:01:28 +00:00
2019-03-13 19:20:07 +00:00
// sv_game.c -- interface to the game dll
# include "server.h"
game_export_t * ge ;
/*
= = = = = = = = = = = = = = =
PF_Unicast
Sends the contents of the mutlicast buffer to a single client
= = = = = = = = = = = = = = =
*/
void PF_Unicast ( edict_t * ent , qboolean reliable )
{
int p ;
client_t * client ;
if ( ! ent )
return ;
p = NUM_FOR_EDICT ( ent ) ;
2021-03-05 21:46:22 +00:00
// if ( (p < 1) || (p > maxclients->value) )
if ( ( p < 1 ) | | ( p > maxclients - > integer ) )
2019-03-13 19:20:07 +00:00
return ;
client = svs . clients + ( p - 1 ) ;
2020-05-05 01:24:51 +00:00
// r1ch: trap bad writes from game dll
// if (client->state <= cs_spawning)
2020-05-29 07:54:33 +00:00
if ( ( client - > state < cs_spawned ) & & ( sv . multicast . cursize > 0 ) )
2020-05-05 01:24:51 +00:00
{
int msgType = ( sv . multicast . cursize > 0 ) ? sv . multicast . data [ 0 ] : 0 ;
msgType = min ( max ( msgType , 0 ) , ( num_svc_ops - 1 ) ) ;
Com_Printf ( S_COLOR_RED " PF_Unicast: game logic error: Attempted to write %d byte %s to not-in-game client %d, ignored. \n " , sv . multicast . cursize , svc_strings [ msgType ] , p - 1 ) ;
SZ_Clear ( & sv . multicast ) ;
return ;
}
2019-03-13 19:20:07 +00:00
if ( reliable )
SZ_Write ( & client - > netchan . message , sv . multicast . data , sv . multicast . cursize ) ;
else
SZ_Write ( & client - > datagram , sv . multicast . data , sv . multicast . cursize ) ;
SZ_Clear ( & sv . multicast ) ;
}
/*
= = = = = = = = = = = = = = =
PF_dprintf
Debug print to server console
= = = = = = = = = = = = = = =
*/
void PF_dprintf ( char * fmt , . . . )
{
char msg [ 1024 ] ;
va_list argptr ;
va_start ( argptr , fmt ) ;
// vsprintf (msg, fmt, argptr);
Q_vsnprintf ( msg , sizeof ( msg ) , fmt , argptr ) ;
va_end ( argptr ) ;
Com_Printf ( " %s " , msg ) ;
}
/*
= = = = = = = = = = = = = = =
PF_cprintf
Print to a single client
= = = = = = = = = = = = = = =
*/
void PF_cprintf ( edict_t * ent , int level , char * fmt , . . . )
{
char msg [ 1024 ] ;
va_list argptr ;
int n ;
if ( ent )
{
n = NUM_FOR_EDICT ( ent ) ;
2021-03-05 21:46:22 +00:00
// if (n < 1 || n > maxclients->value)
if ( n < 1 | | n > maxclients - > integer )
2019-03-13 19:20:07 +00:00
Com_Error ( ERR_DROP , " cprintf to a non-client " ) ;
}
va_start ( argptr , fmt ) ;
// vsprintf (msg, fmt, argptr);
Q_vsnprintf ( msg , sizeof ( msg ) , fmt , argptr ) ;
va_end ( argptr ) ;
if ( ent )
SV_ClientPrintf ( svs . clients + ( n - 1 ) , level , " %s " , msg ) ;
else
Com_Printf ( " %s " , msg ) ;
}
/*
= = = = = = = = = = = = = = =
PF_centerprintf
centerprint to a single client
= = = = = = = = = = = = = = =
*/
void PF_centerprintf ( edict_t * ent , char * fmt , . . . )
{
char msg [ 1024 ] ;
va_list argptr ;
int n ;
n = NUM_FOR_EDICT ( ent ) ;
2021-03-05 21:46:22 +00:00
// if (n < 1 || n > maxclients->value)
if ( n < 1 | | n > maxclients - > integer )
2019-03-13 19:20:07 +00:00
return ; // Com_Error (ERR_DROP, "centerprintf to a non-client");
va_start ( argptr , fmt ) ;
// vsprintf (msg, fmt, argptr);
Q_vsnprintf ( msg , sizeof ( msg ) , fmt , argptr ) ;
va_end ( argptr ) ;
MSG_WriteByte ( & sv . multicast , svc_centerprint ) ;
MSG_WriteString ( & sv . multicast , msg ) ;
PF_Unicast ( ent , true ) ;
}
/*
= = = = = = = = = = = = = = =
PF_error
Abort the server with a game error
= = = = = = = = = = = = = = =
*/
void PF_error ( char * fmt , . . . )
{
char msg [ 1024 ] ;
va_list argptr ;
va_start ( argptr , fmt ) ;
// vsprintf (msg, fmt, argptr);
Q_vsnprintf ( msg , sizeof ( msg ) , fmt , argptr ) ;
va_end ( argptr ) ;
Com_Error ( ERR_DROP , " Game Error: %s " , msg ) ;
}
/*
= = = = = = = = = = = = = = = = =
PF_setmodel
Also sets mins and maxs for inline bmodels
= = = = = = = = = = = = = = = = =
*/
void PF_setmodel ( edict_t * ent , char * name )
{
int i ;
cmodel_t * mod ;
if ( ! name )
Com_Error ( ERR_DROP , " PF_setmodel: NULL " ) ;
i = SV_ModelIndex ( name ) ;
// ent->model = name;
ent - > s . modelindex = i ;
// if it is an inline model, get the size information for it
if ( name [ 0 ] = = ' * ' )
{
mod = CM_InlineModel ( name ) ;
VectorCopy ( mod - > mins , ent - > mins ) ;
VectorCopy ( mod - > maxs , ent - > maxs ) ;
SV_LinkEdict ( ent ) ;
}
}
/*
= = = = = = = = = = = = = = =
PF_Configstring
= = = = = = = = = = = = = = =
*/
void PF_Configstring ( int index , char * val )
{
size_t len , maxlen ;
char * dest ;
if ( index < 0 | | index > = MAX_CONFIGSTRINGS )
Com_Error ( ERR_DROP , " PF_Configstring: bad index %i \n " , index ) ;
if ( ! val )
val = " " ;
// catch overflow of indvidual configstrings
len = strlen ( val ) ;
maxlen = CS_SIZE ( index ) ;
if ( len > = maxlen ) {
Com_Printf ( S_COLOR_YELLOW " PF_Configstring: index %d overflowed: %d > %d \n " , index , len , maxlen ) ;
len = maxlen - 1 ;
}
// change the string in sv
// Don't use a null-terminated strncpy here!!
// strncpy (sv.configstrings[index], val);
dest = sv . configstrings [ index ] ;
memcpy ( dest , val , len ) ;
dest [ len ] = 0 ;
if ( sv . state ! = ss_loading )
{ // send the update to everyone
SZ_Clear ( & sv . multicast ) ;
MSG_WriteChar ( & sv . multicast , svc_configstring ) ;
MSG_WriteShort ( & sv . multicast , index ) ;
MSG_WriteString ( & sv . multicast , val ) ;
SV_Multicast ( vec3_origin , MULTICAST_ALL_R ) ;
}
}
void PF_WriteChar ( int c ) { MSG_WriteChar ( & sv . multicast , c ) ; }
void PF_WriteByte ( int c ) { MSG_WriteByte ( & sv . multicast , c ) ; }
void PF_WriteShort ( int c ) { MSG_WriteShort ( & sv . multicast , c ) ; }
void PF_WriteLong ( int c ) { MSG_WriteLong ( & sv . multicast , c ) ; }
void PF_WriteFloat ( float f ) { MSG_WriteFloat ( & sv . multicast , f ) ; }
void PF_WriteString ( char * s ) { MSG_WriteString ( & sv . multicast , s ) ; }
void PF_WritePos ( vec3_t pos ) { MSG_WritePos ( & sv . multicast , pos ) ; }
void PF_WriteDir ( vec3_t dir ) { MSG_WriteDir ( & sv . multicast , dir ) ; }
void PF_WriteAngle ( float f ) { MSG_WriteAngle ( & sv . multicast , f ) ; }
/*
= = = = = = = = = = = = = = = = =
PF_inPVS
Also checks portalareas so that doors block sight
= = = = = = = = = = = = = = = = =
*/
qboolean PF_inPVS ( vec3_t p1 , vec3_t p2 )
{
int leafnum ;
int cluster ;
int area1 , area2 ;
byte * mask ;
leafnum = CM_PointLeafnum ( p1 ) ;
cluster = CM_LeafCluster ( leafnum ) ;
area1 = CM_LeafArea ( leafnum ) ;
mask = CM_ClusterPVS ( cluster ) ;
leafnum = CM_PointLeafnum ( p2 ) ;
cluster = CM_LeafCluster ( leafnum ) ;
area2 = CM_LeafArea ( leafnum ) ;
if ( mask & & ( ! ( mask [ cluster > > 3 ] & ( 1 < < ( cluster & 7 ) ) ) ) )
return false ;
if ( ! CM_AreasConnected ( area1 , area2 ) )
return false ; // a door blocks sight
return true ;
}
/*
= = = = = = = = = = = = = = = = =
PF_inPHS
Also checks portalareas so that doors block sound
= = = = = = = = = = = = = = = = =
*/
qboolean PF_inPHS ( vec3_t p1 , vec3_t p2 )
{
int leafnum ;
int cluster ;
int area1 , area2 ;
byte * mask ;
leafnum = CM_PointLeafnum ( p1 ) ;
cluster = CM_LeafCluster ( leafnum ) ;
area1 = CM_LeafArea ( leafnum ) ;
mask = CM_ClusterPHS ( cluster ) ;
leafnum = CM_PointLeafnum ( p2 ) ;
cluster = CM_LeafCluster ( leafnum ) ;
area2 = CM_LeafArea ( leafnum ) ;
if ( mask & & ( ! ( mask [ cluster > > 3 ] & ( 1 < < ( cluster & 7 ) ) ) ) )
return false ; // more than one bounce away
if ( ! CM_AreasConnected ( area1 , area2 ) )
return false ; // a door blocks hearing
return true ;
}
void PF_StartSound ( edict_t * entity , int channel , int sound_num , float volume ,
float attenuation , float timeofs )
{
if ( ! entity )
return ;
SV_StartSound ( NULL , entity , channel , sound_num , volume , attenuation , timeofs ) ;
}
//==============================================
/*
= = = = = = = = = = = = = = =
SV_ShutdownGameProgs
Called when either the entire server is being killed , or
it is changing to a different game directory .
= = = = = = = = = = = = = = =
*/
void SV_ShutdownGameProgs ( void )
{
if ( ! ge )
return ;
ge - > Shutdown ( ) ;
Sys_UnloadGame ( ) ;
ge = NULL ;
}
/*
= = = = = = = = = = = = = = =
SV_InitGameProgs
Init the game subsystem for a new map
= = = = = = = = = = = = = = =
*/
void SCR_DebugGraph ( float value , int color ) ;
void SV_InitGameProgs ( void )
{
game_import_t import ;
// unload anything we have now
if ( ge )
SV_ShutdownGameProgs ( ) ;
// load a new game dll
import . multicast = SV_Multicast ;
import . unicast = PF_Unicast ;
import . bprintf = SV_BroadcastPrintf ;
import . dprintf = PF_dprintf ;
import . cprintf = PF_cprintf ;
import . centerprintf = PF_centerprintf ;
import . error = PF_error ;
import . linkentity = SV_LinkEdict ;
import . unlinkentity = SV_UnlinkEdict ;
import . BoxEdicts = SV_AreaEdicts ;
import . trace = SV_Trace ;
import . pointcontents = SV_PointContents ;
import . setmodel = PF_setmodel ;
import . inPVS = PF_inPVS ;
import . inPHS = PF_inPHS ;
import . Pmove = Pmove ;
import . modelindex = SV_ModelIndex ;
import . soundindex = SV_SoundIndex ;
import . imageindex = SV_ImageIndex ;
import . configstring = PF_Configstring ;
import . sound = PF_StartSound ;
import . positioned_sound = SV_StartSound ;
import . WriteChar = PF_WriteChar ;
import . WriteByte = PF_WriteByte ;
import . WriteShort = PF_WriteShort ;
import . WriteLong = PF_WriteLong ;
import . WriteFloat = PF_WriteFloat ;
import . WriteString = PF_WriteString ;
import . WritePosition = PF_WritePos ;
import . WriteDir = PF_WriteDir ;
import . WriteAngle = PF_WriteAngle ;
import . TagMalloc = Z_TagMalloc ;
import . TagFree = Z_Free ;
import . FreeTags = Z_FreeTags ;
import . cvar = Cvar_Get ;
import . cvar_set = Cvar_Set ;
import . cvar_forceset = Cvar_ForceSet ;
import . argc = Cmd_Argc ;
import . argv = Cmd_Argv ;
import . args = Cmd_Args ;
import . AddCommandString = Cbuf_AddText ;
import . DebugGraph = SCR_DebugGraph ;
// Knightmare- support game DLL loading from pak files thru engine
// This can be used to load script files, etc
import . ListPak = FS_ListPak ;
import . LoadFile = FS_LoadFile ;
import . FreeFile = FS_FreeFile ;
import . FreeFileList = FS_FreeFileList ;
2019-03-13 23:54:41 +00:00
import . OpenFile = FS_FOpenFile ;
import . OpenCompressedFile = FS_FOpenCompressedFile ;
import . CloseFile = FS_FCloseFile ;
import . FRead = FS_Read ;
import . FWrite = FS_Write ;
import . GameDir = FS_GameDir ;
2020-05-02 20:08:01 +00:00
import . SaveGameDir = FS_SaveGameDir ;
2019-03-13 23:54:41 +00:00
import . CreatePath = FS_CreatePath ;
2019-10-09 01:55:44 +00:00
import . GetFileList = FS_GetFileList ;
2021-03-25 06:25:48 +00:00
import . FSeek = FS_Seek ;
import . FTell = FS_Tell ;
2019-03-13 19:20:07 +00:00
import . SetAreaPortalState = CM_SetAreaPortalState ;
import . AreasConnected = CM_AreasConnected ;
ge = ( game_export_t * ) Sys_GetGameAPI ( & import ) ;
if ( ! ge )
Com_Error ( ERR_DROP , " failed to load game DLL " ) ;
if ( ge - > apiversion ! = GAME_API_VERSION )
Com_Error ( ERR_DROP , " game is version %i, not %i " , ge - > apiversion ,
GAME_API_VERSION ) ;
ge - > Init ( ) ;
}