This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/server/sv_game.c

4600 lines
100 KiB
C
Raw Normal View History

2008-12-15 22:00:00 +01:00
//=======================================================================
// Copyright XashXT Group 2008 <20>
// sv_game.c - server dlls interaction
//=======================================================================
#include "common.h"
#include "server.h"
2010-08-04 22:00:00 +02:00
#include "net_encode.h"
2008-12-15 22:00:00 +01:00
#include "matrix_lib.h"
2010-08-07 22:00:00 +02:00
#include "event_flags.h"
2009-11-10 22:00:00 +01:00
#include "pm_defs.h"
2008-12-15 22:00:00 +01:00
#include "const.h"
2010-12-08 22:00:00 +01:00
// fatpvs stuff
static byte fatpvs[MAX_MAP_LEAFS/8];
static byte fatphs[MAX_MAP_LEAFS/8];
static byte *bitvector;
static int fatbytes;
2010-08-15 22:00:00 +02:00
// exports
typedef void (*LINK_ENTITY_FUNC)( entvars_t *pev );
2010-10-20 22:00:00 +02:00
typedef void (__stdcall *GIVEFNPTRSTODLL)( enginefuncs_t* engfuncs, globalvars_t *pGlobals );
2010-08-15 22:00:00 +02:00
2010-06-28 22:00:00 +02:00
/*
=============
EntvarsDescription
entavrs table for FindEntityByString
=============
*/
#define ENTVARS_COUNT ( sizeof( gEntvarsDescription ) / sizeof( gEntvarsDescription[0] ))
static TYPEDESCRIPTION gEntvarsDescription[] =
{
DEFINE_ENTITY_FIELD( classname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( globalname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( model, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( viewmodel, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( weaponmodel, FIELD_MODELNAME ),
DEFINE_ENTITY_FIELD( target, FIELD_STRING ),
DEFINE_ENTITY_FIELD( targetname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( netname, FIELD_STRING ),
DEFINE_ENTITY_FIELD( message, FIELD_STRING ),
DEFINE_ENTITY_FIELD( noise, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise1, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise2, FIELD_SOUNDNAME ),
DEFINE_ENTITY_FIELD( noise3, FIELD_SOUNDNAME ),
};
/*
=============
SV_GetEntvarsDescription
entavrs table for FindEntityByString
=============
*/
TYPEDESCRIPTION *SV_GetEntvarsDescirption( int number )
{
if( number < 0 && number >= ENTVARS_COUNT )
return NULL;
return &gEntvarsDescription[number];
}
2010-08-15 22:00:00 +02:00
void SV_SysError( const char *error_string )
{
if( svgame.hInstance ) svgame.dllFuncs.pfnSys_Error( error_string );
}
2009-09-23 22:00:00 +02:00
void SV_SetMinMaxSize( edict_t *e, const float *min, const float *max )
2008-12-15 22:00:00 +01:00
{
2010-08-25 22:00:00 +02:00
float scale = 1.0f;
2009-09-23 22:00:00 +02:00
int i;
2008-12-15 22:00:00 +01:00
2010-04-07 22:00:00 +02:00
if( !SV_IsValidEdict( e ) || !min || !max )
return;
2008-12-15 22:00:00 +01:00
for( i = 0; i < 3; i++ )
2010-04-13 22:00:00 +02:00
{
2008-12-15 22:00:00 +01:00
if( min[i] > max[i] )
2010-04-13 22:00:00 +02:00
{
MsgDev( D_ERROR, "SV_SetMinMaxSize: %s backwards mins/maxs\n", SV_ClassName( e ));
SV_LinkEdict( e, false ); // just relink edict and exit
return;
}
}
2010-08-25 22:00:00 +02:00
#if 0
// FIXME: enable this when other server parts will be done and tested
if( e->v.scale > 0.0f && e->v.scale != 1.0f )
{
2010-12-08 22:00:00 +01:00
switch( Mod_GetType( e->v.modelindex ))
2010-08-25 22:00:00 +02:00
{
case mod_sprite:
case mod_studio:
scale = e->v.scale;
break;
}
}
#endif
VectorScale( min, scale, e->v.mins );
VectorScale( max, scale, e->v.maxs );
2008-12-15 22:00:00 +01:00
VectorSubtract( max, min, e->v.size );
2009-11-02 22:00:00 +01:00
SV_LinkEdict( e, false );
2008-12-15 22:00:00 +01:00
}
2009-11-02 22:00:00 +01:00
void SV_CopyTraceToGlobal( trace_t *trace )
2009-10-28 22:00:00 +01:00
{
2010-11-15 22:00:00 +01:00
svgame.globals->trace_allsolid = trace->allsolid;
svgame.globals->trace_startsolid = trace->startsolid;
svgame.globals->trace_fraction = trace->fraction;
svgame.globals->trace_plane_dist = trace->plane.dist;
svgame.globals->trace_ent = trace->ent;
2009-11-02 22:00:00 +01:00
svgame.globals->trace_flags = 0;
2010-11-15 22:00:00 +01:00
svgame.globals->trace_inopen = trace->inopen;
svgame.globals->trace_inwater = trace->inwater;
VectorCopy( trace->endpos, svgame.globals->trace_endpos );
VectorCopy( trace->plane.normal, svgame.globals->trace_plane_normal );
svgame.globals->trace_hitgroup = trace->hitgroup;
2008-12-15 22:00:00 +01:00
}
void SV_SetModel( edict_t *ent, const char *name )
{
2010-05-31 22:00:00 +02:00
vec3_t mins = { 0.0f, 0.0f, 0.0f };
vec3_t maxs = { 0.0f, 0.0f, 0.0f };
2010-08-25 22:00:00 +02:00
int i, mod_type;
2008-12-15 22:00:00 +01:00
i = SV_ModelIndex( name );
if( i == 0 ) return;
2009-01-02 22:00:00 +01:00
2010-10-26 22:00:00 +02:00
ent->v.model = MAKE_STRING( sv.model_precache[i] );
2008-12-15 22:00:00 +01:00
ent->v.modelindex = i;
2010-12-08 22:00:00 +01:00
mod_type = Mod_GetType( ent->v.modelindex );
2010-08-25 22:00:00 +02:00
// studio models set to zero sizes as default
switch( mod_type )
{
case mod_brush:
case mod_sprite:
2010-05-27 22:00:00 +02:00
Mod_GetBounds( ent->v.modelindex, mins, maxs );
2010-08-25 22:00:00 +02:00
break;
}
2010-05-31 22:00:00 +02:00
SV_SetMinMaxSize( ent, mins, maxs );
2008-12-15 22:00:00 +01:00
}
float SV_AngleMod( float ideal, float current, float speed )
{
float move;
2010-04-01 22:00:00 +02:00
if( anglemod( current ) == ideal ) // already there?
return current;
2008-12-15 22:00:00 +01:00
move = ideal - current;
2009-09-23 22:00:00 +02:00
if( ideal > current )
2008-12-15 22:00:00 +01:00
{
2010-04-01 22:00:00 +02:00
if( move >= 180 )
move = move - 360;
2008-12-15 22:00:00 +01:00
}
else
{
2010-04-01 22:00:00 +02:00
if( move <= -180 )
move = move + 360;
2008-12-15 22:00:00 +01:00
}
2009-09-23 22:00:00 +02:00
if( move > 0 )
2008-12-15 22:00:00 +01:00
{
2010-04-01 22:00:00 +02:00
if( move > speed )
move = speed;
2008-12-15 22:00:00 +01:00
}
else
{
2010-04-01 22:00:00 +02:00
if( move < -speed )
move = -speed;
2008-12-15 22:00:00 +01:00
}
2009-09-23 22:00:00 +02:00
return anglemod( current + move );
2008-12-15 22:00:00 +01:00
}
2010-11-15 22:00:00 +01:00
/*
=============
SV_ConvertTrace
2008-12-15 22:00:00 +01:00
2010-11-15 22:00:00 +01:00
convert trace_t to TraceResult
=============
*/
void SV_ConvertTrace( TraceResult *dst, trace_t *src )
{
ASSERT( src != NULL && dst != NULL );
2008-12-15 22:00:00 +01:00
2010-11-15 22:00:00 +01:00
dst->fAllSolid = src->allsolid;
dst->fStartSolid = src->startsolid;
dst->fInOpen = src->inopen;
dst->fInWater = src->inwater;
dst->flFraction = src->fraction;
VectorCopy( src->endpos, dst->vecEndPos );
dst->flPlaneDist = src->plane.dist;
VectorCopy( src->plane.normal, dst->vecPlaneNormal );
dst->pHit = src->ent;
dst->iHitgroup = src->hitgroup;
2010-10-09 22:00:00 +02:00
}
2010-12-08 22:00:00 +01:00
/*
=================
SV_LeafPVS
=================
*/
byte *SV_LeafPVS( int leafnum )
{
2010-12-09 22:00:00 +01:00
if( !sv.worldmodel || leafnum <= 0 || leafnum >= sv.worldmodel->numleafs || !svs.pvs || sv_novis->integer )
return world.nullrow;
2010-12-08 22:00:00 +01:00
2010-12-09 22:00:00 +01:00
return svs.pvs + leafnum * 4 * (( sv.worldmodel->numleafs + 31 ) >> 5 );
2010-12-08 22:00:00 +01:00
}
/*
=================
SV_LeafPHS
=================
*/
byte *SV_LeafPHS( int leafnum )
{
2010-12-09 22:00:00 +01:00
if( !sv.worldmodel || leafnum <= 0 || leafnum >= sv.worldmodel->numleafs || !svs.phs || sv_novis->integer )
return world.nullrow;
2010-12-08 22:00:00 +01:00
2010-12-09 22:00:00 +01:00
return svs.phs + leafnum * 4 * (( sv.worldmodel->numleafs + 31 ) >> 5 );
2010-12-08 22:00:00 +01:00
}
2010-10-09 22:00:00 +02:00
/*
=================
SV_Send
Sends the contents of sv.multicast to a subset of the clients,
then clears sv.multicast.
MSG_ONE send to one client (ent can't be NULL)
MSG_ALL same as broadcast (origin can be NULL)
MSG_PVS send to clients potentially visible from org
MSG_PHS send to clients potentially hearable from org
=================
*/
2010-10-26 22:00:00 +02:00
qboolean SV_Send( int dest, const vec3_t origin, const edict_t *ent )
2010-10-09 22:00:00 +02:00
{
byte *mask = NULL;
int leafnum = 0, numsends = 0;
int j, numclients = sv_maxclients->integer;
sv_client_t *cl, *current = svs.clients;
2010-10-26 22:00:00 +02:00
qboolean reliable = false;
qboolean specproxy = false;
2010-10-09 22:00:00 +02:00
float *viewOrg = NULL;
switch( dest )
{
case MSG_INIT:
if( sv.state == ss_loading )
{
// copy signon buffer
BF_WriteBits( &sv.signon, BF_GetData( &sv.multicast ), BF_GetNumBitsWritten( &sv.multicast ));
BF_Clear( &sv.multicast );
return true;
}
// intentional fallthrough (in-game MSG_INIT it's a MSG_ALL reliable)
case MSG_ALL:
reliable = true;
// intentional fallthrough
case MSG_BROADCAST:
// nothing to sort
break;
case MSG_PAS_R:
reliable = true;
// intentional fallthrough
case MSG_PAS:
if( origin == NULL ) return false;
2010-11-21 22:00:00 +01:00
leafnum = Mod_PointLeafnum( origin );
2010-12-08 22:00:00 +01:00
mask = SV_LeafPHS( leafnum );
2010-10-09 22:00:00 +02:00
break;
case MSG_PVS_R:
reliable = true;
// intentional fallthrough
case MSG_PVS:
if( origin == NULL ) return false;
2010-11-21 22:00:00 +01:00
leafnum = Mod_PointLeafnum( origin );
2010-12-08 22:00:00 +01:00
mask = SV_LeafPVS( leafnum );
2010-10-09 22:00:00 +02:00
break;
case MSG_ONE:
reliable = true;
// intentional fallthrough
case MSG_ONE_UNRELIABLE:
if( ent == NULL ) return false;
j = NUM_FOR_EDICT( ent );
if( j < 1 || j > numclients ) return false;
current = svs.clients + (j - 1);
numclients = 1; // send to one
break;
case MSG_SPEC:
specproxy = reliable = true;
break;
default:
Host_Error( "SV_Multicast: bad dest: %i\n", dest );
return false;
}
// send the data to all relevent clients (or once only)
for( j = 0, cl = current; j < numclients; j++, cl++ )
{
if( cl->state == cs_free || cl->state == cs_zombie )
continue;
if( cl->state != cs_spawned && !reliable )
continue;
if( specproxy && !( cl->edict->v.flags & FL_PROXY ))
continue;
if( !cl->edict || cl->fakeclient )
continue;
if( mask )
{
if( SV_IsValidEdict( cl->pViewEntity ))
viewOrg = cl->pViewEntity->v.origin;
else viewOrg = cl->edict->v.origin;
2010-11-21 22:00:00 +01:00
leafnum = Mod_PointLeafnum( viewOrg );
2010-10-09 22:00:00 +02:00
if( mask && (!(mask[leafnum>>3] & (1<<( leafnum & 7 )))))
continue;
}
if( reliable ) BF_WriteBits( &cl->netchan.message, BF_GetData( &sv.multicast ), BF_GetNumBitsWritten( &sv.multicast ));
else BF_WriteBits( &cl->datagram, BF_GetData( &sv.multicast ), BF_GetNumBitsWritten( &sv.multicast ));
numsends++;
2008-12-15 22:00:00 +01:00
}
2010-10-09 22:00:00 +02:00
BF_Clear( &sv.multicast );
return numsends; // debug
2008-12-15 22:00:00 +01:00
}
2010-07-01 22:00:00 +02:00
void SV_CreateDecal( const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags )
{
2010-10-09 22:00:00 +02:00
if( sv.state != ss_loading ) return;
2010-07-29 22:00:00 +02:00
ASSERT( origin );
2010-07-01 22:00:00 +02:00
2010-10-09 22:00:00 +02:00
// this can happens if serialized map contain 4096 static decals...
if(( BF_GetNumBytesWritten( &sv.signon ) + 20 ) >= BF_GetMaxBytes( &sv.signon ))
{
return;
}
2010-07-01 22:00:00 +02:00
// static decals are posters, it's always reliable
2010-10-09 22:00:00 +02:00
BF_WriteByte( &sv.signon, svc_bspdecal );
BF_WriteBitVec3Coord( &sv.signon, origin );
BF_WriteWord( &sv.signon, decalIndex );
BF_WriteShort( &sv.signon, entityIndex );
2010-07-02 22:00:00 +02:00
if( entityIndex > 0 )
2010-10-09 22:00:00 +02:00
BF_WriteWord( &sv.signon, modelIndex );
BF_WriteByte( &sv.signon, flags );
2010-07-01 22:00:00 +02:00
}
2010-10-26 22:00:00 +02:00
static qboolean SV_OriginIn( int mode, const vec3_t v1, const vec3_t v2 )
2008-12-15 22:00:00 +01:00
{
2010-05-22 22:00:00 +02:00
int leafnum;
2008-12-15 22:00:00 +01:00
byte *mask;
2010-11-21 22:00:00 +01:00
leafnum = Mod_PointLeafnum( v1 );
2010-04-13 22:00:00 +02:00
switch( mode )
{
case DVIS_PVS:
2010-12-08 22:00:00 +01:00
mask = SV_LeafPVS( leafnum );
2010-04-13 22:00:00 +02:00
break;
case DVIS_PHS:
2010-12-08 22:00:00 +01:00
mask = SV_LeafPHS( leafnum );
2010-04-13 22:00:00 +02:00
break;
default:
2010-05-22 22:00:00 +02:00
mask = NULL; // skip any checks
2010-04-13 22:00:00 +02:00
break;
}
2009-11-03 22:00:00 +01:00
2010-11-21 22:00:00 +01:00
leafnum = Mod_PointLeafnum( v2 );
2009-11-03 22:00:00 +01:00
2010-05-22 22:00:00 +02:00
if( mask && (!( mask[leafnum>>3] & (1<<( leafnum & 7 )))))
2008-12-15 22:00:00 +01:00
return false;
return true;
}
2010-12-08 22:00:00 +01:00
/*
=============================================================================
The PVS must include a small area around the client to allow head bobbing
or other small motion on the client side. Otherwise, a bob might cause an
entity that should be visible to not show up, especially when the bob
crosses a waterline.
=============================================================================
*/
static void SV_AddToFatPVS( const vec3_t org, int type, mnode_t *node )
{
byte *vis;
mplane_t *plane;
float d;
while( 1 )
{
// if this is a leaf, accumulate the pvs bits
if( node->contents < 0 )
{
if( node->contents != CONTENTS_SOLID )
{
mleaf_t *leaf;
int i;
leaf = (mleaf_t *)node;
if( type == DVIS_PVS )
2010-12-09 22:00:00 +01:00
vis = SV_LeafPVS( leaf - sv.worldmodel->leafs );
2010-12-08 22:00:00 +01:00
else if( type == DVIS_PHS )
2010-12-09 22:00:00 +01:00
vis = SV_LeafPHS( leaf - sv.worldmodel->leafs );
else vis = world.nullrow;
2010-12-08 22:00:00 +01:00
for( i = 0; i < fatbytes; i++ )
bitvector[i] |= vis[i];
}
return;
}
plane = node->plane;
d = DotProduct( org, plane->normal ) - plane->dist;
if( d > 8 ) node = node->children[0];
else if( d < -8 ) node = node->children[1];
else
{
// go down both
SV_AddToFatPVS( org, type, node->children[0] );
node = node->children[1];
}
}
}
2010-04-13 22:00:00 +02:00
/*
==============
SV_BoxInPVS
2010-04-12 22:00:00 +02:00
2010-04-13 22:00:00 +02:00
check brush boxes in fat pvs
==============
*/
2010-10-26 22:00:00 +02:00
static qboolean SV_BoxInPVS( const vec3_t org, const vec3_t absmin, const vec3_t absmax )
2010-04-13 22:00:00 +02:00
{
2010-12-08 22:00:00 +01:00
if( !Mod_BoxVisible( absmin, absmax, SV_LeafPVS( Mod_PointLeafnum( org ))))
2010-04-12 22:00:00 +02:00
return false;
return true;
}
2010-04-07 22:00:00 +02:00
void SV_WriteEntityPatch( const char *filename )
{
file_t *f;
dheader_t *header;
int ver = -1, lumpofs = 0, lumplen = 0;
byte buf[MAX_SYSPATH]; // 1 kb
2010-10-26 22:00:00 +02:00
qboolean result = false;
2010-04-07 22:00:00 +02:00
f = FS_Open( va( "maps/%s.bsp", filename ), "rb" );
if( !f ) return;
Mem_Set( buf, 0, MAX_SYSPATH );
FS_Read( f, buf, MAX_SYSPATH );
2010-11-21 22:00:00 +01:00
ver = *(uint *)buf;
2010-04-07 22:00:00 +02:00
2010-05-22 22:00:00 +02:00
switch( ver )
2010-04-07 22:00:00 +02:00
{
2010-05-22 22:00:00 +02:00
case Q1BSP_VERSION:
case HLBSP_VERSION:
2010-04-07 22:00:00 +02:00
header = (dheader_t *)buf;
2010-11-21 22:00:00 +01:00
if( header->lumps[LUMP_PLANES].filelen % sizeof( dplane_t ))
2010-05-31 22:00:00 +02:00
{
2010-11-21 22:00:00 +01:00
lumpofs = header->lumps[LUMP_PLANES].fileofs;
lumplen = header->lumps[LUMP_PLANES].filelen;
2010-05-31 22:00:00 +02:00
}
else
{
2010-11-21 22:00:00 +01:00
lumpofs = header->lumps[LUMP_ENTITIES].fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen;
2010-05-31 22:00:00 +02:00
}
2010-04-07 22:00:00 +02:00
break;
default:
FS_Close( f );
return;
}
if( lumplen >= 10 )
{
char *entities = NULL;
FS_Seek( f, lumpofs, SEEK_SET );
entities = (char *)Z_Malloc( lumplen + 1 );
FS_Read( f, entities, lumplen );
FS_WriteFile( va( "maps/%s.ent", filename ), entities, lumplen );
Msg( "Write 'maps/%s.ent'\n", filename );
Mem_Free( entities );
}
FS_Close( f );
}
2009-11-03 22:00:00 +01:00
script_t *SV_GetEntityScript( const char *filename )
2009-09-28 22:00:00 +02:00
{
2009-11-03 22:00:00 +01:00
file_t *f;
dheader_t *header;
string entfilename;
script_t *ents = NULL;
int ver = -1, lumpofs = 0, lumplen = 0;
byte buf[MAX_SYSPATH]; // 1 kb
2010-10-26 22:00:00 +02:00
qboolean result = false;
2009-09-28 22:00:00 +02:00
f = FS_Open( va( "maps/%s.bsp", filename ), "rb" );
2009-11-03 22:00:00 +01:00
if( !f ) return NULL;
Mem_Set( buf, 0, MAX_SYSPATH );
FS_Read( f, buf, MAX_SYSPATH );
2010-11-21 22:00:00 +01:00
ver = *(uint *)buf;
2009-11-03 22:00:00 +01:00
2010-05-22 22:00:00 +02:00
switch( ver )
2009-11-03 22:00:00 +01:00
{
2010-05-22 22:00:00 +02:00
case Q1BSP_VERSION:
case HLBSP_VERSION:
2009-11-03 22:00:00 +01:00
header = (dheader_t *)buf;
2010-11-21 22:00:00 +01:00
if( header->lumps[LUMP_PLANES].filelen % sizeof( dplane_t ))
2010-05-31 22:00:00 +02:00
{
2010-11-21 22:00:00 +01:00
lumpofs = header->lumps[LUMP_PLANES].fileofs;
lumplen = header->lumps[LUMP_PLANES].filelen;
2010-05-31 22:00:00 +02:00
}
else
{
2010-11-21 22:00:00 +01:00
lumpofs = header->lumps[LUMP_ENTITIES].fileofs;
lumplen = header->lumps[LUMP_ENTITIES].filelen;
2010-05-31 22:00:00 +02:00
}
2009-11-03 22:00:00 +01:00
break;
default:
FS_Close( f );
return NULL;
}
2009-09-28 22:00:00 +02:00
2009-11-03 22:00:00 +01:00
// check for entfile too
com.strncpy( entfilename, va( "maps/%s.ent", filename ), sizeof( entfilename ));
ents = Com_OpenScript( entfilename, NULL, 0 );
if( !ents && lumplen >= 10 )
2009-09-28 22:00:00 +02:00
{
2009-11-03 22:00:00 +01:00
char *entities = NULL;
FS_Seek( f, lumpofs, SEEK_SET );
entities = (char *)Z_Malloc( lumplen + 1 );
FS_Read( f, entities, lumplen );
ents = Com_OpenScript( "ents", entities, lumplen + 1 );
Mem_Free( entities ); // no reason to keep it
}
FS_Close( f ); // all done
2009-09-28 22:00:00 +02:00
2009-11-03 22:00:00 +01:00
return ents;
}
2009-09-28 22:00:00 +02:00
2010-04-02 22:00:00 +02:00
int SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name )
2009-11-03 22:00:00 +01:00
{
script_t *ents = NULL;
2010-04-02 22:00:00 +02:00
int flags = 0;
2009-09-28 22:00:00 +02:00
2009-11-03 22:00:00 +01:00
ents = SV_GetEntityScript( filename );
2009-09-28 22:00:00 +02:00
2009-11-03 22:00:00 +01:00
if( ents )
{
// if there are entities to parse, a missing message key just
// means there is no title, so clear the message string now
token_t token;
2010-04-02 22:00:00 +02:00
string check_name;
2010-10-26 22:00:00 +02:00
qboolean need_landmark = com.strlen( landmark_name ) > 0 ? true : false;
2009-09-28 22:00:00 +02:00
2010-04-02 22:00:00 +02:00
if( !need_landmark && host.developer >= 2 )
2009-09-28 22:00:00 +02:00
{
2010-04-02 22:00:00 +02:00
// not transition,
2009-11-03 22:00:00 +01:00
Com_CloseScript( ents );
2010-04-02 22:00:00 +02:00
// skip spawnpoint checks in devmode
return (MAP_IS_EXIST|MAP_HAS_SPAWNPOINT);
2009-09-28 22:00:00 +02:00
}
2010-04-02 22:00:00 +02:00
flags |= MAP_IS_EXIST; // map is exist
2010-06-08 22:00:00 +02:00
while( Com_ReadToken( ents, SC_ALLOW_NEWLINES|SC_ALLOW_PATHNAMES2, &token ))
2009-09-28 22:00:00 +02:00
{
2009-11-03 22:00:00 +01:00
if( !com.strcmp( token.string, "classname" ))
2009-09-28 22:00:00 +02:00
{
2009-11-03 22:00:00 +01:00
// check classname for spawn entity
2010-06-08 22:00:00 +02:00
Com_ReadString( ents, SC_ALLOW_PATHNAMES2, check_name );
2010-04-02 22:00:00 +02:00
if( !com.strcmp( spawn_entity, check_name ))
{
flags |= MAP_HAS_SPAWNPOINT;
// we already find landmark, stop the parsing
if( need_landmark && flags & MAP_HAS_LANDMARK )
break;
}
}
else if( need_landmark && !com.strcmp( token.string, "targetname" ))
{
// check targetname for landmark entity
2010-06-08 22:00:00 +02:00
Com_ReadString( ents, SC_ALLOW_PATHNAMES2, check_name );
2010-04-02 22:00:00 +02:00
if( !com.strcmp( landmark_name, check_name ))
2009-09-28 22:00:00 +02:00
{
2010-04-02 22:00:00 +02:00
flags |= MAP_HAS_LANDMARK;
// we already find spawnpoint, stop the parsing
if( flags & MAP_HAS_SPAWNPOINT )
break;
2009-09-28 22:00:00 +02:00
}
}
}
2009-11-03 22:00:00 +01:00
Com_CloseScript( ents );
2009-09-28 22:00:00 +02:00
}
2010-04-02 22:00:00 +02:00
return flags;
2009-09-28 22:00:00 +02:00
}
2009-09-24 22:00:00 +02:00
void SV_InitEdict( edict_t *pEdict )
2008-12-15 22:00:00 +01:00
{
2010-07-29 22:00:00 +02:00
ASSERT( pEdict );
ASSERT( pEdict->pvPrivateData == NULL );
2008-12-15 22:00:00 +01:00
2008-12-16 22:00:00 +01:00
pEdict->v.pContainingEntity = pEdict; // make cross-links for consistency
2008-12-26 22:00:00 +01:00
pEdict->pvPrivateData = NULL; // will be alloced later by pfnAllocPrivateData
2009-01-04 22:00:00 +01:00
pEdict->serialnumber = NUM_FOR_EDICT( pEdict );
2008-12-15 22:00:00 +01:00
pEdict->free = false;
}
2008-12-16 22:00:00 +01:00
void SV_FreeEdict( edict_t *pEdict )
2008-12-15 22:00:00 +01:00
{
2010-07-29 22:00:00 +02:00
ASSERT( pEdict );
ASSERT( pEdict->free == false );
2008-12-17 22:00:00 +01:00
2008-12-15 22:00:00 +01:00
// unlink from world
2008-12-16 22:00:00 +01:00
SV_UnlinkEdict( pEdict );
2008-12-15 22:00:00 +01:00
2010-10-17 22:00:00 +02:00
// never remove global entities from map
if( pEdict->v.globalname && sv.state == ss_active )
{
pEdict->v.solid = SOLID_NOT;
pEdict->v.flags &= ~FL_KILLME;
pEdict->v.effects = EF_NODRAW;
pEdict->v.movetype = MOVETYPE_NONE;
pEdict->v.modelindex = 0;
pEdict->v.nextthink = -1;
return;
}
2009-09-24 22:00:00 +02:00
if( pEdict->pvPrivateData )
{
2010-08-15 22:00:00 +02:00
if( svgame.dllFuncs2.pfnOnFreeEntPrivateData )
{
// NOTE: new interface can be missing
svgame.dllFuncs2.pfnOnFreeEntPrivateData( pEdict );
}
2009-09-24 22:00:00 +02:00
Mem_Free( pEdict->pvPrivateData );
}
Mem_Set( pEdict, 0, sizeof( *pEdict ));
2008-12-15 22:00:00 +01:00
// mark edict as freed
2010-07-23 22:00:00 +02:00
pEdict->freetime = sv_time();
2008-12-16 22:00:00 +01:00
pEdict->v.nextthink = -1;
pEdict->free = true;
}
edict_t *SV_AllocEdict( void )
{
edict_t *pEdict;
int i;
2010-08-15 22:00:00 +02:00
for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ )
2008-12-16 22:00:00 +01:00
{
pEdict = EDICT_NUM( i );
// the first couple seconds of server time can involve a lot of
// freeing and allocating, so relax the replacement policy
2010-07-23 22:00:00 +02:00
if( pEdict->free && ( pEdict->freetime < 2.0 || sv_time() - pEdict->freetime > 0.5 ))
2008-12-16 22:00:00 +01:00
{
SV_InitEdict( pEdict );
return pEdict;
}
}
2010-04-13 22:00:00 +02:00
if( i >= svgame.globals->maxEntities )
Host_Error( "ED_AllocEdict: no free edicts\n" );
2008-12-16 22:00:00 +01:00
2010-08-15 22:00:00 +02:00
svgame.numEntities++;
2008-12-16 22:00:00 +01:00
pEdict = EDICT_NUM( i );
SV_InitEdict( pEdict );
return pEdict;
2008-12-15 22:00:00 +01:00
}
2009-01-10 22:00:00 +01:00
edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
{
const char *pszClassName;
LINK_ENTITY_FUNC SpawnEdict;
pszClassName = STRING( className );
2010-03-30 22:00:00 +02:00
if( !ent )
{
// allocate new one
ent = SV_AllocEdict();
}
2009-09-24 22:00:00 +02:00
else if( ent->free )
{
2010-02-16 22:00:00 +01:00
SV_InitEdict( ent ); // re-init edict
MsgDev( D_WARN, "SV_AllocPrivateData: entity %s is freed!\n", STRING( className ));
2009-09-24 22:00:00 +02:00
}
2010-02-16 22:00:00 +01:00
2009-01-10 22:00:00 +01:00
ent->v.classname = className;
ent->v.pContainingEntity = ent; // re-link
2010-08-07 22:00:00 +02:00
2010-03-20 22:00:00 +01:00
VectorSet( ent->v.rendercolor, 255, 255, 255 ); // assume default color
2009-01-10 22:00:00 +01:00
// allocate edict private memory (passed by dlls)
2010-03-27 22:00:00 +01:00
SpawnEdict = (LINK_ENTITY_FUNC)FS_GetProcAddress( svgame.hInstance, pszClassName );
2009-01-10 22:00:00 +01:00
if( !SpawnEdict )
{
// attempt to create custom entity
2010-08-15 22:00:00 +02:00
if( svgame.dllFuncs2.pfnCreate && svgame.dllFuncs2.pfnCreate( ent, pszClassName ) == -1 )
2009-01-10 22:00:00 +01:00
{
ent->v.flags |= FL_KILLME;
2009-09-24 22:00:00 +02:00
MsgDev( D_ERROR, "No spawn function for %s\n", STRING( className ));
2009-01-10 22:00:00 +01:00
return ent; // this edict will be removed from map
}
}
else SpawnEdict( &ent->v );
return ent;
}
2008-12-26 22:00:00 +01:00
void SV_FreeEdicts( void )
{
2009-09-28 22:00:00 +02:00
int i = 0;
2008-12-26 22:00:00 +01:00
edict_t *ent;
2010-08-15 22:00:00 +02:00
for( i = 0; i < svgame.numEntities; i++ )
2008-12-26 22:00:00 +01:00
{
ent = EDICT_NUM( i );
if( ent->free ) continue;
SV_FreeEdict( ent );
}
2008-12-15 22:00:00 +01:00
}
2010-08-06 22:00:00 +02:00
void SV_PlaybackEvent( sizebuf_t *msg, event_info_t *info )
2009-12-02 22:00:00 +01:00
{
event_args_t nullargs;
2010-07-29 22:00:00 +02:00
ASSERT( msg );
ASSERT( info );
2009-12-02 22:00:00 +01:00
Mem_Set( &nullargs, 0, sizeof( nullargs ));
2010-08-04 22:00:00 +02:00
BF_WriteWord( msg, info->index ); // send event index
BF_WriteWord( msg, (int)( info->fire_time * 100.0f )); // send event delay
2009-12-02 22:00:00 +01:00
MSG_WriteDeltaEvent( msg, &nullargs, &info->args ); // FIXME: zero-compressing
}
2009-11-23 22:00:00 +01:00
const char *SV_ClassName( const edict_t *e )
{
if( !e ) return "(null)";
if( e->free ) return "freed";
return STRING( e->v.classname );
}
2010-10-26 22:00:00 +02:00
static qboolean SV_IsValidCmd( const char *pCmd )
2009-11-25 22:00:00 +01:00
{
size_t len;
len = com.strlen( pCmd );
// valid commands all have a ';' or newline '\n' as their last character
if( len && ( pCmd[len-1] == '\n' || pCmd[len-1] == ';' ))
return true;
return false;
}
2010-10-26 22:00:00 +02:00
sv_client_t *SV_ClientFromEdict( const edict_t *pEdict, qboolean spawned_only )
2009-11-23 22:00:00 +01:00
{
sv_client_t *client;
int i;
if( !SV_IsValidEdict( pEdict ))
return NULL;
i = NUM_FOR_EDICT( pEdict ) - 1;
if( i < 0 || i >= sv_maxclients->integer )
return NULL;
if( spawned_only )
{
if( svs.clients[i].state != cs_spawned )
return NULL;
}
2010-03-20 22:00:00 +01:00
#if 0
2009-11-23 22:00:00 +01:00
else
{
if( svs.clients[i].state < cs_connected )
return NULL;
}
2010-03-20 22:00:00 +01:00
#endif
2009-11-23 22:00:00 +01:00
client = svs.clients + i;
return client;
}
2009-11-28 22:00:00 +01:00
/*
=========
SV_BaselineForEntity
assume pEdict is valid
=========
*/
2010-06-28 22:00:00 +02:00
void SV_BaselineForEntity( edict_t *pEdict )
2009-11-28 22:00:00 +01:00
{
2010-08-18 22:00:00 +02:00
int usehull, player;
int modelindex;
entity_state_t baseline;
float *mins, *maxs;
sv_client_t *cl;
2009-11-28 22:00:00 +01:00
2010-08-18 22:00:00 +02:00
if( pEdict->v.flags & FL_CLIENT && ( cl = SV_ClientFromEdict( pEdict, false )))
2009-11-28 22:00:00 +01:00
{
2010-08-18 22:00:00 +02:00
usehull = ( pEdict->v.flags & FL_DUCKING ) ? true : false;
modelindex = cl->modelindex ? cl->modelindex : pEdict->v.modelindex;
mins = svgame.player_mins[usehull];
maxs = svgame.player_maxs[usehull];
player = true;
}
else
{
if( pEdict->v.effects == EF_NODRAW )
return;
2009-11-28 22:00:00 +01:00
2010-08-18 22:00:00 +02:00
if( !pEdict->v.modelindex || !STRING( pEdict->v.model ))
return; // invisible
2009-11-28 22:00:00 +01:00
2010-08-18 22:00:00 +02:00
modelindex = pEdict->v.modelindex;
mins = pEdict->v.mins;
maxs = pEdict->v.maxs;
player = false;
2009-11-28 22:00:00 +01:00
}
2010-08-18 22:00:00 +02:00
// take current state as baseline
Mem_Set( &baseline, 0, sizeof( baseline ));
baseline.number = pEdict->serialnumber;
svgame.dllFuncs.pfnCreateBaseline( player, baseline.number, &baseline, pEdict, modelindex, mins, maxs );
// set entity type
2010-10-20 22:00:00 +02:00
if( pEdict->v.flags & FL_CUSTOMENTITY )
baseline.entityType = ENTITY_BEAM;
else baseline.entityType = ENTITY_NORMAL;
2010-08-18 22:00:00 +02:00
svs.baselines[pEdict->serialnumber] = baseline;
2009-11-28 22:00:00 +01:00
}
2009-11-23 22:00:00 +01:00
void SV_SetClientMaxspeed( sv_client_t *cl, float fNewMaxspeed )
{
// fakeclients must be changed speed too
fNewMaxspeed = bound( -svgame.movevars.maxspeed, fNewMaxspeed, svgame.movevars.maxspeed );
cl->edict->v.maxspeed = fNewMaxspeed;
2010-08-05 22:00:00 +02:00
Info_SetValueForKey( cl->physinfo, "maxspd", va( "%.f", fNewMaxspeed ));
2009-11-23 22:00:00 +01:00
}
2008-12-15 22:00:00 +01:00
/*
===============================================================================
2009-09-23 22:00:00 +02:00
2008-12-15 22:00:00 +01:00
Game Builtin Functions
===============================================================================
*/
/*
=========
pfnPrecacheModel
=========
*/
int pfnPrecacheModel( const char *s )
{
2009-10-29 22:00:00 +01:00
int modelIndex = SV_ModelIndex( s );
2010-12-08 22:00:00 +01:00
Mod_RegisterModel( s, modelIndex );
2009-10-29 22:00:00 +01:00
return modelIndex;
2008-12-15 22:00:00 +01:00
}
/*
=========
pfnPrecacheSound
=========
*/
int pfnPrecacheSound( const char *s )
{
return SV_SoundIndex( s );
}
/*
=================
pfnSetModel
=================
*/
void pfnSetModel( edict_t *e, const char *m )
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( e ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_SetModel: invalid entity %s\n", SV_ClassName( e ));
2008-12-15 22:00:00 +01:00
return;
}
2009-11-23 22:00:00 +01:00
if( !m || m[0] <= ' ' )
2008-12-15 22:00:00 +01:00
{
MsgDev( D_WARN, "SV_SetModel: null name\n" );
return;
}
SV_SetModel( e, m );
}
/*
=================
pfnModelIndex
=================
*/
int pfnModelIndex( const char *m )
{
2010-10-26 22:00:00 +02:00
int i;
if( !m || !m[0] )
return 0;
2008-12-15 22:00:00 +01:00
2010-10-26 22:00:00 +02:00
for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ )
{
if( !com.strcmp( sv.model_precache[i], m ))
return i;
}
MsgDev( D_ERROR, "SV_ModelIndex: %s not precached\n", m );
2009-11-23 22:00:00 +01:00
2010-10-26 22:00:00 +02:00
return 0;
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnModelFrames
=================
*/
int pfnModelFrames( int modelIndex )
{
2009-10-28 22:00:00 +01:00
int numFrames = 0;
2008-12-15 22:00:00 +01:00
2009-10-28 22:00:00 +01:00
Mod_GetFrames( modelIndex, &numFrames );
2008-12-15 22:00:00 +01:00
2009-10-28 22:00:00 +01:00
return numFrames;
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnSetSize
=================
*/
void pfnSetSize( edict_t *e, const float *rgflMin, const float *rgflMax )
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( e ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_SetSize: invalid entity %s\n", SV_ClassName( e ));
2008-12-15 22:00:00 +01:00
return;
}
2009-11-23 22:00:00 +01:00
// ignore world silently
if( e == EDICT_NUM( 0 ))
2008-12-15 22:00:00 +01:00
return;
2009-11-23 22:00:00 +01:00
2009-09-23 22:00:00 +02:00
SV_SetMinMaxSize( e, rgflMin, rgflMax );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnChangeLevel
=================
*/
void pfnChangeLevel( const char* s1, const char* s2 )
{
2010-10-15 22:00:00 +02:00
static uint last_spawncount = 0;
2009-11-23 22:00:00 +01:00
if( !s1 || s1[0] <= ' ' ) return;
2008-12-15 22:00:00 +01:00
2010-03-24 22:00:00 +01:00
// make sure we don't issue two changelevels
2010-10-15 22:00:00 +02:00
if( svs.spawncount == last_spawncount )
return;
last_spawncount = svs.spawncount;
// make sure we don't issue two changelevels
if( svs.changelevel_next_time > host.realtime )
2010-04-02 22:00:00 +02:00
return;
2010-03-24 22:00:00 +01:00
2010-10-15 22:00:00 +02:00
svs.changelevel_next_time = host.realtime + 1.0f; // rest 1 secs if failed
2010-03-24 22:00:00 +01:00
2010-10-17 22:00:00 +02:00
SV_SkipUpdates ();
2009-09-28 22:00:00 +02:00
if( !s2 ) Cbuf_AddText( va( "changelevel %s\n", s1 )); // Quake changlevel
else Cbuf_AddText( va( "changelevel %s %s\n", s1, s2 )); // Half-Life changelevel
2008-12-15 22:00:00 +01:00
}
2010-10-26 22:00:00 +02:00
/*
=================
pfnGetSpawnParms
obsolete
=================
*/
void pfnGetSpawnParms( edict_t *ent )
{
Host_Error( "SV_GetSpawnParms: %s [%i]\n", SV_ClassName( ent ), NUM_FOR_EDICT( ent ));
}
/*
=================
pfnSaveSpawnParms
obsolete
=================
*/
void pfnSaveSpawnParms( edict_t *ent )
{
Host_Error( "SV_SaveSpawnParms: %s [%i]\n", SV_ClassName( ent ), NUM_FOR_EDICT( ent ));
}
2008-12-15 22:00:00 +01:00
/*
=================
pfnVecToYaw
=================
*/
float pfnVecToYaw( const float *rgflVector )
{
2009-11-23 22:00:00 +01:00
if( !rgflVector ) return 0;
return SV_VecToYaw( rgflVector );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnMoveToOrigin
=================
*/
void pfnMoveToOrigin( edict_t *ent, const float *pflGoal, float dist, int iMoveType )
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( ent ))
{
MsgDev( D_WARN, "SV_MoveToOrigin: invalid entity %s\n", SV_ClassName( ent ));
return;
}
2008-12-15 22:00:00 +01:00
2010-04-01 22:00:00 +02:00
if( !pflGoal )
{
MsgDev( D_WARN, "SV_MoveToOrigin: invalid goal pos\n" );
return;
}
2009-11-23 22:00:00 +01:00
SV_MoveToOrigin( ent, pflGoal, dist, iMoveType );
2008-12-15 22:00:00 +01:00
}
/*
==============
pfnChangeYaw
==============
*/
void pfnChangeYaw( edict_t* ent )
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( ent ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_ChangeYaw: invalid entity %s\n", SV_ClassName( ent ));
2008-12-15 22:00:00 +01:00
return;
}
2010-04-01 22:00:00 +02:00
ent->v.angles[YAW] = SV_AngleMod( ent->v.ideal_yaw, ent->v.angles[YAW], ent->v.yaw_speed );
2008-12-15 22:00:00 +01:00
}
/*
==============
pfnChangePitch
==============
*/
void pfnChangePitch( edict_t* ent )
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( ent ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_ChangePitch: invalid entity %s\n", SV_ClassName( ent ));
2008-12-15 22:00:00 +01:00
return;
}
2010-06-23 22:00:00 +02:00
ent->v.angles[PITCH] = SV_AngleMod( ent->v.idealpitch, ent->v.angles[PITCH], ent->v.pitch_speed );
2008-12-15 22:00:00 +01:00
}
/*
=========
pfnFindEntityByString
=========
*/
edict_t* pfnFindEntityByString( edict_t *pStartEdict, const char *pszField, const char *pszValue )
{
2009-09-23 22:00:00 +02:00
int index = 0, e = 0;
TYPEDESCRIPTION *desc = NULL;
2010-06-28 22:00:00 +02:00
edict_t *ed;
2008-12-21 22:00:00 +01:00
const char *t;
2008-12-20 22:00:00 +01:00
2009-01-04 22:00:00 +01:00
if( pStartEdict ) e = NUM_FOR_EDICT( pStartEdict );
2010-05-28 22:00:00 +02:00
if( !pszValue || !*pszValue ) return svgame.edicts;
2008-12-15 22:00:00 +01:00
2010-06-28 22:00:00 +02:00
while(( desc = SV_GetEntvarsDescirption( index++ )) != NULL )
2008-12-15 22:00:00 +01:00
{
2009-09-23 22:00:00 +02:00
if( !com.strcmp( pszField, desc->fieldName ))
break;
}
if( desc == NULL )
{
2010-06-28 22:00:00 +02:00
MsgDev( D_ERROR, "SV_FindEntityByString: field %s not a string\n", pszField );
2010-05-28 22:00:00 +02:00
return svgame.edicts;
2008-12-15 22:00:00 +01:00
}
2010-08-15 22:00:00 +02:00
for( e++; e < svgame.numEntities; e++ )
2008-12-15 22:00:00 +01:00
{
ed = EDICT_NUM( e );
2010-07-26 22:00:00 +02:00
2010-03-30 22:00:00 +02:00
if( !SV_IsValidEdict( ed )) continue;
2008-12-15 22:00:00 +01:00
2009-09-23 22:00:00 +02:00
switch( desc->fieldType )
{
case FIELD_STRING:
case FIELD_MODELNAME:
case FIELD_SOUNDNAME:
t = STRING( *(string_t *)&((byte *)&ed->v)[desc->fieldOffset] );
if( !t ) t = "";
if( !com.strcmp( t, pszValue ))
return ed;
break;
}
2008-12-15 22:00:00 +01:00
}
2010-05-28 22:00:00 +02:00
return svgame.edicts;
2008-12-15 22:00:00 +01:00
}
/*
==============
pfnGetEntityIllum
2010-07-01 22:00:00 +02:00
returns weighted lightvalue for entity position
2008-12-15 22:00:00 +01:00
==============
*/
int pfnGetEntityIllum( edict_t* pEnt )
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( pEnt ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_GetEntityIllum: invalid entity %s\n", SV_ClassName( pEnt ));
2010-07-01 22:00:00 +02:00
return 0;
2008-12-15 22:00:00 +01:00
}
2010-10-19 22:00:00 +02:00
return SV_LightForEntity( pEnt );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnFindEntityInSphere
return NULL instead of world
=================
*/
2009-11-16 22:00:00 +01:00
edict_t* pfnFindEntityInSphere( edict_t *pStartEdict, const float *org, float flRadius )
2008-12-15 22:00:00 +01:00
{
2008-12-18 22:00:00 +01:00
edict_t *ent;
2009-11-16 22:00:00 +01:00
float distSquared;
float eorg;
int j, e = 0;
flRadius *= flRadius;
2008-12-15 22:00:00 +01:00
2010-03-30 22:00:00 +02:00
if( SV_IsValidEdict( pStartEdict ))
2008-12-17 22:00:00 +01:00
e = NUM_FOR_EDICT( pStartEdict );
2009-11-16 22:00:00 +01:00
2010-08-15 22:00:00 +02:00
for( e++; e < svgame.numEntities; e++ )
2008-12-15 22:00:00 +01:00
{
ent = EDICT_NUM( e );
2010-03-30 22:00:00 +02:00
if( !SV_IsValidEdict( ent )) continue;
2010-07-26 22:00:00 +02:00
if( !ent->pvPrivateData ) continue;
2008-12-15 22:00:00 +01:00
2009-11-16 22:00:00 +01:00
distSquared = 0;
for( j = 0; j < 3 && distSquared <= flRadius; j++ )
{
if( org[j] < ent->v.absmin[j] )
eorg = org[j] - ent->v.absmin[j];
else if( org[j] > ent->v.absmax[j] )
eorg = org[j] - ent->v.absmax[j];
else eorg = 0;
2008-12-15 22:00:00 +01:00
2009-11-16 22:00:00 +01:00
distSquared += eorg * eorg;
}
if( distSquared > flRadius )
continue;
return ent;
2008-12-15 22:00:00 +01:00
}
2010-07-26 22:00:00 +02:00
return svgame.edicts;
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnFindClientInPVS
return NULL instead of world
=================
*/
edict_t* pfnFindClientInPVS( edict_t *pEdict )
{
2010-03-30 22:00:00 +02:00
edict_t *pClient;
sv_client_t *cl;
const float *org;
int i;
if( !SV_IsValidEdict( pEdict ))
return NULL;
2008-12-15 22:00:00 +01:00
2010-01-31 22:00:00 +01:00
for( i = 0; i < svgame.globals->maxClients; i++ )
2008-12-15 22:00:00 +01:00
{
2010-01-31 22:00:00 +01:00
pClient = EDICT_NUM( i + 1 );
2010-03-30 22:00:00 +02:00
if(( cl = SV_ClientFromEdict( pClient, true )) == NULL )
continue;
// check for SET_VIEW
if( SV_IsValidEdict( cl->pViewEntity ))
org = cl->pViewEntity->v.origin;
else org = pClient->v.origin;
2010-04-12 22:00:00 +02:00
if( SV_OriginIn( DVIS_PVS, pEdict->v.origin, org ))
2010-03-30 22:00:00 +02:00
return pClient;
2008-12-15 22:00:00 +01:00
}
2008-12-18 22:00:00 +01:00
return NULL;
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnFindClientInPHS
return NULL instead of world
=================
*/
edict_t* pfnFindClientInPHS( edict_t *pEdict )
{
2010-03-30 22:00:00 +02:00
edict_t *pClient;
sv_client_t *cl;
const float *org;
int i;
if( !SV_IsValidEdict( pEdict ))
return NULL;
2008-12-15 22:00:00 +01:00
2010-01-31 22:00:00 +01:00
for( i = 0; i < svgame.globals->maxClients; i++ )
2008-12-15 22:00:00 +01:00
{
2010-01-31 22:00:00 +01:00
pClient = EDICT_NUM( i + 1 );
2010-03-30 22:00:00 +02:00
if(( cl = SV_ClientFromEdict( pClient, true )) == NULL )
continue;
// check for SET_VIEW
if( SV_IsValidEdict( cl->pViewEntity ))
org = cl->pViewEntity->v.origin;
else org = pClient->v.origin;
2010-04-12 22:00:00 +02:00
if( SV_OriginIn( DVIS_PHS, pEdict->v.origin, org ))
2010-03-30 22:00:00 +02:00
return pClient;
2008-12-15 22:00:00 +01:00
}
2008-12-18 22:00:00 +01:00
return NULL;
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnEntitiesInPVS
=================
*/
2010-04-12 22:00:00 +02:00
edict_t *pfnEntitiesInPVS( edict_t *pplayer )
2008-12-15 22:00:00 +01:00
{
edict_t *pEdict, *chain;
2010-04-12 22:00:00 +02:00
int i, result;
2008-12-15 22:00:00 +01:00
2010-04-12 22:00:00 +02:00
if( !SV_IsValidEdict( pplayer ))
return NULL;
2009-09-28 22:00:00 +02:00
2010-08-15 22:00:00 +02:00
for( chain = NULL, i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ )
2008-12-15 22:00:00 +01:00
{
2008-12-26 22:00:00 +01:00
pEdict = EDICT_NUM( i );
2010-03-30 22:00:00 +02:00
if( !SV_IsValidEdict( pEdict )) continue;
2010-12-08 22:00:00 +01:00
if( Mod_GetType( pEdict->v.modelindex ) == mod_brush )
2010-04-13 22:00:00 +02:00
result = SV_BoxInPVS( pplayer->v.origin, pEdict->v.absmin, pEdict->v.absmax );
2010-04-12 22:00:00 +02:00
else result = SV_OriginIn( DVIS_PVS, pplayer->v.origin, pEdict->v.origin );
2010-03-30 22:00:00 +02:00
2010-04-12 22:00:00 +02:00
if( result )
2008-12-15 22:00:00 +01:00
{
pEdict->v.chain = chain;
chain = pEdict;
}
}
2009-01-06 22:00:00 +01:00
return chain;
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnEntitiesInPHS
=================
*/
2010-04-12 22:00:00 +02:00
edict_t *pfnEntitiesInPHS( edict_t *pplayer )
2008-12-15 22:00:00 +01:00
{
edict_t *pEdict, *chain;
2010-04-13 22:00:00 +02:00
vec3_t checkPos;
int i;
2008-12-15 22:00:00 +01:00
2010-04-12 22:00:00 +02:00
if( !SV_IsValidEdict( pplayer ))
return NULL;
2009-09-28 22:00:00 +02:00
2010-08-15 22:00:00 +02:00
for( chain = NULL, i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ )
2008-12-15 22:00:00 +01:00
{
2008-12-26 22:00:00 +01:00
pEdict = EDICT_NUM( i );
2010-03-30 22:00:00 +02:00
if( !SV_IsValidEdict( pEdict )) continue;
2010-12-08 22:00:00 +01:00
if( Mod_GetType( pEdict->v.modelindex ) == mod_brush )
2010-04-13 22:00:00 +02:00
VectorAverage( pEdict->v.absmin, pEdict->v.absmax, checkPos );
else VectorCopy( pEdict->v.origin, checkPos );
2010-03-30 22:00:00 +02:00
2010-04-13 22:00:00 +02:00
if( SV_OriginIn( DVIS_PHS, pplayer->v.origin, checkPos ))
2008-12-15 22:00:00 +01:00
{
pEdict->v.chain = chain;
chain = pEdict;
}
}
2009-01-06 22:00:00 +01:00
return chain;
2008-12-15 22:00:00 +01:00
}
/*
==============
pfnMakeVectors
==============
*/
void pfnMakeVectors( const float *rgflVector )
{
2008-12-26 22:00:00 +01:00
AngleVectors( rgflVector, svgame.globals->v_forward, svgame.globals->v_right, svgame.globals->v_up );
2008-12-15 22:00:00 +01:00
}
/*
==============
pfnCreateEntity
just allocate new one
==============
*/
edict_t* pfnCreateEntity( void )
{
return SV_AllocEdict();
}
/*
==============
pfnRemoveEntity
free edict private mem, unlink physics etc
==============
*/
void pfnRemoveEntity( edict_t* e )
{
2010-04-03 22:00:00 +02:00
if( !SV_IsValidEdict( e ))
2009-11-23 22:00:00 +01:00
{
MsgDev( D_ERROR, "SV_RemoveEntity: entity already freed\n" );
return;
}
2008-12-15 22:00:00 +01:00
// never free client or world entity
2010-04-03 22:00:00 +02:00
if( e->serialnumber < ( svgame.globals->maxClients + 1 ))
2008-12-15 22:00:00 +01:00
{
2008-12-26 22:00:00 +01:00
MsgDev( D_ERROR, "SV_RemoveEntity: can't delete %s\n", (e == EDICT_NUM( 0 )) ? "world" : "client" );
2008-12-15 22:00:00 +01:00
return;
}
2009-11-23 22:00:00 +01:00
2008-12-15 22:00:00 +01:00
SV_FreeEdict( e );
}
/*
==============
pfnCreateNamedEntity
==============
*/
edict_t* pfnCreateNamedEntity( string_t className )
{
2009-01-10 22:00:00 +01:00
return SV_AllocPrivateData( NULL, className );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnMakeStatic
disable entity updates to client
=============
*/
2009-11-23 22:00:00 +01:00
static void pfnMakeStatic( edict_t *ent )
2008-12-15 22:00:00 +01:00
{
2010-10-17 22:00:00 +02:00
int index, i;
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( ent ))
{
MsgDev( D_WARN, "SV_MakeStatic: invalid entity %s\n", SV_ClassName( ent ));
return;
}
2010-08-15 22:00:00 +02:00
2010-10-17 22:00:00 +02:00
index = SV_ModelIndex( STRING( ent->v.model ));
2010-08-15 22:00:00 +02:00
2010-10-17 22:00:00 +02:00
BF_WriteByte( &sv.signon, svc_spawnstatic );
BF_WriteShort(&sv.signon, index );
BF_WriteByte( &sv.signon, ent->v.sequence );
BF_WriteByte( &sv.signon, ent->v.frame );
BF_WriteWord( &sv.signon, ent->v.colormap );
BF_WriteByte( &sv.signon, ent->v.skin );
for(i = 0; i < 3; i++ )
{
BF_WriteBitCoord( &sv.signon, ent->v.origin[i] );
BF_WriteBitAngle( &sv.signon, ent->v.angles[i], 16 );
}
BF_WriteByte( &sv.signon, ent->v.rendermode );
if( ent->v.rendermode != kRenderNormal )
{
BF_WriteByte( &sv.signon, ent->v.renderamt );
BF_WriteByte( &sv.signon, ent->v.rendercolor[0] );
BF_WriteByte( &sv.signon, ent->v.rendercolor[1] );
BF_WriteByte( &sv.signon, ent->v.rendercolor[2] );
BF_WriteByte( &sv.signon, ent->v.renderfx );
}
// remove at end of the frame
ent->v.flags |= FL_KILLME;
2008-12-15 22:00:00 +01:00
}
/*
=============
2010-10-17 22:00:00 +02:00
pfnEntIsOnFloor
2008-12-15 22:00:00 +01:00
2010-10-17 22:00:00 +02:00
legacy builtin
2008-12-15 22:00:00 +01:00
=============
*/
2010-10-17 22:00:00 +02:00
static int pfnEntIsOnFloor( edict_t *e )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( e ))
{
2010-10-17 22:00:00 +02:00
MsgDev( D_WARN, "SV_CheckBottom: invalid entity %s\n", SV_ClassName( e ));
return 0;
2009-11-23 22:00:00 +01:00
}
2010-10-17 22:00:00 +02:00
return SV_CheckBottom( e, MOVE_NORMAL );
2008-12-15 22:00:00 +01:00
}
/*
===============
pfnDropToFloor
===============
*/
2009-11-23 22:00:00 +01:00
int pfnDropToFloor( edict_t* e )
2008-12-15 22:00:00 +01:00
{
2009-11-02 22:00:00 +01:00
vec3_t end;
trace_t trace;
2008-12-15 22:00:00 +01:00
2010-04-12 22:00:00 +02:00
if( sv.loadgame )
return 0;
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( e ))
2008-12-15 22:00:00 +01:00
{
2010-01-31 22:00:00 +01:00
MsgDev( D_ERROR, "SV_DropToFloor: invalid entity %s\n", SV_ClassName( e ));
2008-12-15 22:00:00 +01:00
return false;
}
2009-11-23 22:00:00 +01:00
VectorCopy( e->v.origin, end );
2008-12-15 22:00:00 +01:00
end[2] -= 256;
2009-11-23 22:00:00 +01:00
trace = SV_Move( e->v.origin, e->v.mins, e->v.maxs, end, MOVE_NORMAL, e );
2008-12-15 22:00:00 +01:00
2010-11-15 22:00:00 +01:00
if( trace.fraction == 1.0f || trace.allsolid )
2008-12-15 22:00:00 +01:00
{
2010-06-27 22:00:00 +02:00
return false;
2008-12-15 22:00:00 +01:00
}
2010-04-09 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
VectorCopy( trace.endpos, e->v.origin );
2010-06-27 22:00:00 +02:00
SV_LinkEdict( e, false );
e->v.flags |= FL_ONGROUND;
2010-11-15 22:00:00 +01:00
e->v.groundentity = trace.ent;
2010-04-09 22:00:00 +02:00
2010-06-27 22:00:00 +02:00
return true;
2008-12-15 22:00:00 +01:00
}
/*
===============
pfnWalkMove
===============
*/
int pfnWalkMove( edict_t *ent, float yaw, float dist, int iMode )
{
2009-11-23 22:00:00 +01:00
vec3_t move;
2008-12-15 22:00:00 +01:00
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( ent ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_WalkMove: invalid entity %s\n", SV_ClassName( ent ));
2008-12-15 22:00:00 +01:00
return false;
}
2010-04-01 22:00:00 +02:00
if(!( ent->v.flags & ( FL_FLY|FL_SWIM|FL_ONGROUND )))
2008-12-15 22:00:00 +01:00
return false;
2009-11-23 22:00:00 +01:00
yaw = yaw * M_PI * 2 / 360;
2008-12-15 22:00:00 +01:00
VectorSet( move, com.cos( yaw ) * dist, com.sin( yaw ) * dist, 0.0f );
2010-04-01 22:00:00 +02:00
2010-10-17 22:00:00 +02:00
switch( iMode )
{
case WALKMOVE_NORMAL:
return SV_MoveStep( ent, move, true );
case WALKMOVE_WORLDONLY:
return SV_MoveTest( ent, move, true );
case WALKMOVE_CHECKONLY:
return SV_MoveStep( ent, move, false);
default:
MsgDev( D_ERROR, "SV_WalkMove: invalid walk mode %i.\n", iMode );
break;
}
return false;
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnSetOrigin
=================
*/
void pfnSetOrigin( edict_t *e, const float *rgflOrigin )
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( e ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_SetOrigin: invalid entity %s\n", SV_ClassName( e ));
2008-12-15 22:00:00 +01:00
return;
}
VectorCopy( rgflOrigin, e->v.origin );
2009-11-02 22:00:00 +01:00
SV_LinkEdict( e, false );
2008-12-15 22:00:00 +01:00
}
2010-10-20 22:00:00 +02:00
/*
=================
SV_BuildSoundMsg
=================
*/
int SV_BuildSoundMsg( edict_t *ent, int chan, const char *samp, int vol, float attn, int flags, int pitch, const vec3_t pos )
{
int sound_idx;
int entityIndex;
if( vol < 0 || vol > 255 )
{
MsgDev( D_ERROR, "SV_StartSound: volume = %i\n", vol );
return 0;
}
if( attn < ATTN_NONE || attn > ATTN_IDLE )
{
MsgDev( D_ERROR, "SV_StartSound: attenuation = %g\n", attn );
return 0;
}
if( chan < 0 || chan > 7 )
{
MsgDev( D_ERROR, "SV_StartSound: channel = %i\n", chan );
return 0;
}
if( pitch < 0 || pitch > 255 )
{
MsgDev( D_ERROR, "SV_StartSound: pitch = %i\n", pitch );
return 0;
}
if( !samp || !*samp )
{
MsgDev( D_ERROR, "SV_StartSound: passed NULL sample\n" );
return 0;
}
if( samp[0] == '!' && com.is_digit( samp + 1 ))
{
flags |= SND_SENTENCE;
sound_idx = com.atoi( samp + 1 );
if( sound_idx >= 1536 )
{
MsgDev( D_ERROR, "SV_StartSound: invalid sentence number %s.\n", samp );
return 0;
}
}
else if( samp[0] == '#' && com.is_digit( samp + 1 ))
{
flags |= SND_SENTENCE;
sound_idx = com.atoi( samp + 1 ) + 1536;
}
else
{
sound_idx = SV_SoundIndex( samp );
}
if( !ent->v.modelindex || !ent->v.model )
entityIndex = 0;
else if( SV_IsValidEdict( ent->v.aiment ))
entityIndex = ent->v.aiment->serialnumber;
else entityIndex = ent->serialnumber;
if( vol != 255 ) flags |= SND_VOLUME;
if( attn != ATTN_NONE ) flags |= SND_ATTENUATION;
if( pitch != PITCH_NORM ) flags |= SND_PITCH;
BF_WriteByte( &sv.multicast, svc_sound );
BF_WriteWord( &sv.multicast, flags );
BF_WriteWord( &sv.multicast, sound_idx );
BF_WriteByte( &sv.multicast, chan );
if( flags & SND_VOLUME ) BF_WriteByte( &sv.multicast, vol );
if( flags & SND_ATTENUATION ) BF_WriteByte( &sv.multicast, attn * 64 );
if( flags & SND_PITCH ) BF_WriteByte( &sv.multicast, pitch );
BF_WriteWord( &sv.multicast, entityIndex );
if( flags & SND_FIXED_ORIGIN ) BF_WriteBitVec3Coord( &sv.multicast, pos );
return 1;
}
2008-12-15 22:00:00 +01:00
/*
=================
2009-01-25 22:00:00 +01:00
SV_StartSound
2008-12-15 22:00:00 +01:00
=================
*/
2009-01-25 22:00:00 +01:00
void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch )
2008-12-15 22:00:00 +01:00
{
2010-04-13 22:00:00 +02:00
int sound_idx;
2010-07-01 22:00:00 +02:00
int entityIndex;
2010-04-24 22:00:00 +02:00
int msg_dest;
2009-11-25 22:00:00 +01:00
vec3_t origin;
2008-12-21 22:00:00 +01:00
if( attn < ATTN_NONE || attn > ATTN_IDLE )
{
MsgDev( D_ERROR, "SV_StartSound: attenuation must be in range 0-2\n" );
return;
}
2009-11-23 22:00:00 +01:00
2008-12-21 22:00:00 +01:00
if( chan < 0 || chan > 7 )
{
MsgDev( D_ERROR, "SV_StartSound: channel must be in range 0-7\n" );
return;
}
2009-11-23 22:00:00 +01:00
2009-11-25 22:00:00 +01:00
if( !SV_IsValidEdict( ent ))
2008-12-21 22:00:00 +01:00
{
MsgDev( D_ERROR, "SV_StartSound: edict == NULL\n" );
return;
}
2009-09-29 22:00:00 +02:00
if( vol != VOL_NORM ) flags |= SND_VOLUME;
2010-04-24 22:00:00 +02:00
if( attn != ATTN_NONE ) flags |= SND_ATTENUATION;
2008-12-21 22:00:00 +01:00
if( pitch != PITCH_NORM ) flags |= SND_PITCH;
2010-07-01 22:00:00 +02:00
// can't track this entity on the client.
// write static sound
2010-08-30 22:00:00 +02:00
if( !ent->v.modelindex || !ent->v.model )
flags |= SND_FIXED_ORIGIN;
2010-07-01 22:00:00 +02:00
2009-11-25 22:00:00 +01:00
// ultimate method for detect bsp models with invalid solidity (e.g. func_pushable)
2010-12-08 22:00:00 +01:00
if( Mod_GetType( ent->v.modelindex ) == mod_brush )
2008-12-21 22:00:00 +01:00
{
2010-04-13 22:00:00 +02:00
VectorAverage( ent->v.absmin, ent->v.absmax, origin );
2009-11-25 22:00:00 +01:00
if( flags & SND_SPAWNING )
2010-06-27 22:00:00 +02:00
{
2009-11-25 22:00:00 +01:00
msg_dest = MSG_INIT;
2010-06-27 22:00:00 +02:00
}
else
{
if( chan == CHAN_STATIC )
msg_dest = MSG_ALL;
2010-06-28 22:00:00 +02:00
else msg_dest = MSG_PAS_R;
2010-06-27 22:00:00 +02:00
}
2008-12-21 22:00:00 +01:00
}
2009-09-29 22:00:00 +02:00
else
2008-12-21 22:00:00 +01:00
{
2010-04-13 22:00:00 +02:00
VectorAverage( ent->v.mins, ent->v.maxs, origin );
VectorAdd( origin, ent->v.origin, origin );
2009-11-25 22:00:00 +01:00
if( flags & SND_SPAWNING )
msg_dest = MSG_INIT;
2010-06-28 22:00:00 +02:00
else msg_dest = MSG_PAS_R;
2008-12-21 22:00:00 +01:00
}
// always sending stop sound command
2009-11-25 22:00:00 +01:00
if( flags & SND_STOP ) msg_dest = MSG_ALL;
2008-12-21 22:00:00 +01:00
2010-06-27 22:00:00 +02:00
if( sample[0] == '!' && com.is_digit( sample + 1 ))
{
flags |= SND_SENTENCE;
sound_idx = com.atoi( sample + 1 );
}
else
{
// precache_sound can be used twice: cache sounds when loading
// and return sound index when server is active
sound_idx = SV_SoundIndex( sample );
}
2008-12-21 22:00:00 +01:00
2010-08-30 22:00:00 +02:00
if( !ent->v.modelindex || !ent->v.model )
2010-07-01 22:00:00 +02:00
entityIndex = 0;
else if( SV_IsValidEdict( ent->v.aiment ))
entityIndex = ent->v.aiment->serialnumber;
else entityIndex = ent->serialnumber;
2010-08-04 22:00:00 +02:00
BF_WriteByte( &sv.multicast, svc_sound );
BF_WriteWord( &sv.multicast, flags );
BF_WriteWord( &sv.multicast, sound_idx );
BF_WriteByte( &sv.multicast, chan );
2008-12-21 22:00:00 +01:00
2010-08-04 22:00:00 +02:00
if( flags & SND_VOLUME ) BF_WriteByte( &sv.multicast, vol * 255 );
if( flags & SND_ATTENUATION ) BF_WriteByte( &sv.multicast, attn * 64 );
if( flags & SND_PITCH ) BF_WriteByte( &sv.multicast, pitch );
2009-09-29 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
BF_WriteWord( &sv.multicast, entityIndex );
if( flags & SND_FIXED_ORIGIN ) BF_WriteBitVec3Coord( &sv.multicast, origin );
2009-09-29 22:00:00 +02:00
2010-08-06 22:00:00 +02:00
SV_Send( msg_dest, origin, NULL );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnEmitAmbientSound
=================
*/
2010-06-28 22:00:00 +02:00
void pfnEmitAmbientSound( edict_t *ent, float *pos, const char *sample, float vol, float attn, int flags, int pitch )
2008-12-15 22:00:00 +01:00
{
2010-04-18 22:00:00 +02:00
int number = 0, sound_idx;
2009-11-25 22:00:00 +01:00
int msg_dest = MSG_PAS_R;
vec3_t origin;
2009-10-11 22:00:00 +02:00
2009-11-25 22:00:00 +01:00
if( attn < ATTN_NONE || attn > ATTN_IDLE )
2009-10-11 22:00:00 +02:00
{
2009-11-25 22:00:00 +01:00
MsgDev( D_ERROR, "SV_AmbientSound: attenuation must be in range 0-2\n" );
return;
2009-10-11 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
if( !pos )
{
MsgDev( D_ERROR, "SV_AmbientSound: pos == NULL!\n" );
return;
}
2009-10-11 22:00:00 +02:00
2009-11-25 22:00:00 +01:00
if( sv.state == ss_loading ) flags |= SND_SPAWNING;
if( vol != VOL_NORM ) flags |= SND_VOLUME;
2010-04-24 22:00:00 +02:00
if( attn != ATTN_NONE ) flags |= SND_ATTENUATION;
2009-11-25 22:00:00 +01:00
if( pitch != PITCH_NORM ) flags |= SND_PITCH;
2009-10-11 22:00:00 +02:00
2010-04-18 22:00:00 +02:00
if( flags & SND_SPAWNING )
msg_dest = MSG_INIT;
2010-06-28 22:00:00 +02:00
else msg_dest = MSG_ALL;
2010-04-18 22:00:00 +02:00
2009-11-25 22:00:00 +01:00
// ultimate method for detect bsp models with invalid solidity (e.g. func_pushable)
2010-04-18 22:00:00 +02:00
if( SV_IsValidEdict( ent ))
2009-10-11 22:00:00 +02:00
{
2010-12-08 22:00:00 +01:00
if( Mod_GetType( ent->v.modelindex ) == mod_brush )
2010-04-18 22:00:00 +02:00
{
VectorAverage( ent->v.absmin, ent->v.absmax, origin );
number = ent->serialnumber;
}
else
{
VectorAverage( ent->v.mins, ent->v.maxs, origin );
VectorAdd( origin, ent->v.origin, origin );
}
2009-10-11 22:00:00 +02:00
}
else
{
2010-04-18 22:00:00 +02:00
VectorCopy( pos, origin );
2009-10-11 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
// always sending stop sound command
if( flags & SND_STOP ) msg_dest = MSG_ALL;
flags |= SND_FIXED_ORIGIN;
2010-06-28 22:00:00 +02:00
if( sample[0] == '!' && com.is_digit( sample + 1 ))
{
flags |= SND_SENTENCE;
sound_idx = com.atoi( sample + 1 );
}
else
{
// precache_sound can be used twice: cache sounds when loading
// and return sound index when server is active
sound_idx = SV_SoundIndex( sample );
}
2009-11-25 22:00:00 +01:00
2010-08-04 22:00:00 +02:00
BF_WriteByte( &sv.multicast, svc_ambientsound );
BF_WriteWord( &sv.multicast, flags );
BF_WriteWord( &sv.multicast, sound_idx );
2010-10-17 22:00:00 +02:00
BF_WriteByte( &sv.multicast, CHAN_STATIC );
2009-11-25 22:00:00 +01:00
2010-08-04 22:00:00 +02:00
if( flags & SND_VOLUME ) BF_WriteByte( &sv.multicast, vol * 255 );
if( flags & SND_ATTENUATION ) BF_WriteByte( &sv.multicast, attn * 64 );
if( flags & SND_PITCH ) BF_WriteByte( &sv.multicast, pitch );
2009-11-25 22:00:00 +01:00
// plays from fixed position
2010-08-04 22:00:00 +02:00
BF_WriteWord( &sv.multicast, number );
BF_WriteBitVec3Coord( &sv.multicast, pos );
2009-11-25 22:00:00 +01:00
2010-08-06 22:00:00 +02:00
SV_Send( msg_dest, origin, NULL );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnTraceLine
=================
*/
2008-12-25 22:00:00 +01:00
static void pfnTraceLine( const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr )
2008-12-15 22:00:00 +01:00
{
2010-11-15 22:00:00 +01:00
trace_t trace;
2010-08-26 22:00:00 +02:00
if( !ptr ) return;
2008-12-15 22:00:00 +01:00
2009-12-17 22:00:00 +01:00
if( svgame.globals->trace_flags & 1 )
2010-05-04 22:00:00 +02:00
fNoMonsters |= FMOVE_SIMPLEBOX;
2009-12-17 22:00:00 +01:00
svgame.globals->trace_flags = 0;
2010-11-15 22:00:00 +01:00
trace = SV_Move( v1, vec3_origin, vec3_origin, v2, fNoMonsters, pentToSkip );
SV_ConvertTrace( ptr, &trace );
SV_CopyTraceToGlobal( &trace );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnTraceToss
=================
*/
2009-01-11 22:00:00 +01:00
static void pfnTraceToss( edict_t* pent, edict_t* pentToIgnore, TraceResult *ptr )
2008-12-15 22:00:00 +01:00
{
2010-11-15 22:00:00 +01:00
trace_t trace;
2010-08-26 22:00:00 +02:00
if( !ptr ) return;
2009-11-02 22:00:00 +01:00
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( pent ))
{
MsgDev( D_WARN, "SV_MoveToss: invalid entity %s\n", SV_ClassName( pent ));
return;
}
2010-11-15 22:00:00 +01:00
trace = SV_MoveToss( pent, pentToIgnore );
SV_ConvertTrace( ptr, &trace );
SV_CopyTraceToGlobal( &trace );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnTraceHull
=================
*/
2009-11-23 22:00:00 +01:00
static void pfnTraceHull( const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr )
2008-12-15 22:00:00 +01:00
{
2010-11-15 22:00:00 +01:00
trace_t trace;
2010-08-26 22:00:00 +02:00
if( !ptr ) return;
2008-12-15 22:00:00 +01:00
2009-12-17 22:00:00 +01:00
if( svgame.globals->trace_flags & 1 )
2010-05-04 22:00:00 +02:00
fNoMonsters |= FMOVE_SIMPLEBOX;
2009-12-17 22:00:00 +01:00
svgame.globals->trace_flags = 0;
2010-11-15 22:00:00 +01:00
trace = SV_MoveHull( v1, hullNumber, v2, fNoMonsters, pentToSkip );
SV_ConvertTrace( ptr, &trace );
SV_CopyTraceToGlobal( &trace );
2008-12-15 22:00:00 +01:00
}
2009-09-22 22:00:00 +02:00
/*
=============
pfnTraceMonsterHull
=============
*/
static int pfnTraceMonsterHull( edict_t *pEdict, const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr )
{
2010-11-15 22:00:00 +01:00
trace_t trace;
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( pEdict ))
{
MsgDev( D_WARN, "SV_TraceMonsterHull: invalid entity %s\n", SV_ClassName( pEdict ));
return 1;
}
2009-12-17 22:00:00 +01:00
if( svgame.globals->trace_flags & 1 )
2010-05-04 22:00:00 +02:00
fNoMonsters |= FMOVE_SIMPLEBOX;
2009-12-17 22:00:00 +01:00
svgame.globals->trace_flags = 0;
2010-11-15 22:00:00 +01:00
trace = SV_Move( v1, pEdict->v.mins, pEdict->v.maxs, v2, fNoMonsters, pentToSkip );
2010-10-17 22:00:00 +02:00
if( ptr )
{
2010-11-15 22:00:00 +01:00
SV_ConvertTrace( ptr, &trace );
SV_CopyTraceToGlobal( &trace );
2010-10-17 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
2010-11-15 22:00:00 +01:00
if( trace.allsolid || trace.fraction != 1.0f )
2010-10-17 22:00:00 +02:00
return true;
return false;
2009-09-22 22:00:00 +02:00
}
/*
=============
pfnTraceModel
=============
*/
2010-08-15 22:00:00 +02:00
static void pfnTraceModel( const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr )
2008-12-15 22:00:00 +01:00
{
2010-08-15 22:00:00 +02:00
float *mins, *maxs;
2010-11-15 22:00:00 +01:00
trace_t trace;
2010-08-15 22:00:00 +02:00
2010-08-26 22:00:00 +02:00
if( !ptr ) return;
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( pent ))
{
2010-05-22 22:00:00 +02:00
MsgDev( D_WARN, "TraceModel: invalid entity %s\n", SV_ClassName( pent ));
2009-11-23 22:00:00 +01:00
return;
}
2010-08-15 22:00:00 +02:00
hullNumber = bound( 0, hullNumber, 3 );
2010-08-26 22:00:00 +02:00
mins = sv.worldmodel->hulls[hullNumber].clip_mins;
maxs = sv.worldmodel->hulls[hullNumber].clip_maxs;
2010-08-15 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
trace = SV_TraceHull( pent, hullNumber, v1, mins, maxs, v2 );
SV_ConvertTrace( ptr, &trace );
SV_CopyTraceToGlobal( &trace );
2008-12-15 22:00:00 +01:00
}
2009-09-22 22:00:00 +02:00
/*
=============
2009-10-28 22:00:00 +01:00
pfnTraceTexture
2009-09-22 22:00:00 +02:00
2009-10-28 22:00:00 +01:00
returns texture basename
2009-09-22 22:00:00 +02:00
=============
*/
2009-01-11 22:00:00 +01:00
static const char *pfnTraceTexture( edict_t *pTextureEntity, const float *v1, const float *v2 )
2008-12-15 22:00:00 +01:00
{
2010-05-22 22:00:00 +02:00
if( !SV_IsValidEdict( pTextureEntity ))
{
MsgDev( D_WARN, "TraceTexture: invalid entity %s\n", SV_ClassName( pTextureEntity ));
return NULL;
}
2010-10-18 22:00:00 +02:00
return SV_TraceTexture( pTextureEntity, v1, v2 );
2008-12-15 22:00:00 +01:00
}
2010-10-26 22:00:00 +02:00
/*
=============
pfnTraceSphere
trace sphere instead of bbox
=============
*/
void pfnTraceSphere( const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr )
{
// FIXME: implement
}
2009-09-22 22:00:00 +02:00
/*
=============
2010-09-02 22:00:00 +02:00
pfnBoxVisible
2009-09-22 22:00:00 +02:00
=============
*/
2010-09-02 22:00:00 +02:00
static int pfnBoxVisible( const float *mins, const float *maxs, const byte *pset )
2009-09-22 22:00:00 +02:00
{
2010-11-21 22:00:00 +01:00
return Mod_BoxVisible( mins, maxs, pset );
2009-09-22 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
/*
=============
pfnGetAimVector
FIXME: use speed for reduce aiming accuracy
=============
*/
void pfnGetAimVector( edict_t* ent, float speed, float *rgflReturn )
{
2009-10-28 22:00:00 +01:00
edict_t *check, *bestent;
vec3_t start, dir, end, bestdir;
float dist, bestdist;
2010-10-26 22:00:00 +02:00
qboolean fNoFriendlyFire;
2009-10-28 22:00:00 +01:00
int i, j;
2009-11-02 22:00:00 +01:00
trace_t tr;
2008-12-15 22:00:00 +01:00
2010-10-21 22:00:00 +02:00
// these vairable defined in game.dll
2008-12-15 22:00:00 +01:00
fNoFriendlyFire = Cvar_VariableValue( "mp_friendlyfire" );
2009-11-23 22:00:00 +01:00
VectorCopy( svgame.globals->v_forward, rgflReturn ); // assume failure if it returns early
2008-12-15 22:00:00 +01:00
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( ent ))
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_WARN, "SV_GetAimVector: invalid entity %s\n", SV_ClassName( ent ));
2008-12-15 22:00:00 +01:00
return;
}
VectorCopy( ent->v.origin, start );
start[2] += 20;
// try sending a trace straight
2008-12-26 22:00:00 +01:00
VectorCopy( svgame.globals->v_forward, dir );
2008-12-15 22:00:00 +01:00
VectorMA( start, 2048, dir, end );
2009-11-02 22:00:00 +01:00
tr = SV_Move( start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent );
2008-12-15 22:00:00 +01:00
2010-11-15 22:00:00 +01:00
if( tr.ent && (tr.ent->v.takedamage == DAMAGE_AIM && fNoFriendlyFire || ent->v.team <= 0 || ent->v.team != tr.ent->v.team ))
2008-12-15 22:00:00 +01:00
{
2008-12-26 22:00:00 +01:00
VectorCopy( svgame.globals->v_forward, rgflReturn );
2008-12-15 22:00:00 +01:00
return;
}
// try all possible entities
VectorCopy( dir, bestdir );
bestdist = 0.5f;
bestent = NULL;
2008-12-26 22:00:00 +01:00
check = EDICT_NUM( 1 ); // start at first client
2010-08-15 22:00:00 +02:00
for( i = 1; i < svgame.numEntities; i++, check++ )
2008-12-15 22:00:00 +01:00
{
if( check->v.takedamage != DAMAGE_AIM ) continue;
if( check == ent ) continue;
if( fNoFriendlyFire && ent->v.team > 0 && ent->v.team == check->v.team )
continue; // don't aim at teammate
for( j = 0; j < 3; j++ )
2010-03-30 22:00:00 +02:00
end[j] = check->v.origin[j] + 0.5f * (check->v.mins[j] + check->v.maxs[j]);
2008-12-15 22:00:00 +01:00
VectorSubtract( end, start, dir );
VectorNormalize( dir );
2008-12-26 22:00:00 +01:00
dist = DotProduct( dir, svgame.globals->v_forward );
2008-12-15 22:00:00 +01:00
if( dist < bestdist ) continue; // to far to turn
2009-11-02 22:00:00 +01:00
tr = SV_Move( start, vec3_origin, vec3_origin, end, MOVE_NORMAL, ent );
2010-11-15 22:00:00 +01:00
if( tr.ent == check )
2008-12-15 22:00:00 +01:00
{
// can shoot at this one
bestdist = dist;
bestent = check;
}
}
if( bestent )
{
VectorSubtract( bestent->v.origin, ent->v.origin, dir );
2008-12-26 22:00:00 +01:00
dist = DotProduct( dir, svgame.globals->v_forward );
VectorScale( svgame.globals->v_forward, dist, end );
2008-12-15 22:00:00 +01:00
end[2] = dir[2];
VectorNormalize( end );
VectorCopy( end, rgflReturn );
}
else VectorCopy( bestdir, rgflReturn );
}
/*
=========
pfnServerCommand
=========
*/
void pfnServerCommand( const char* str )
{
2009-11-25 22:00:00 +01:00
if( SV_IsValidCmd( str )) Cbuf_AddText( str );
else MsgDev( D_ERROR, "bad server command %s\n", str );
2008-12-15 22:00:00 +01:00
}
/*
=========
pfnServerExecute
=========
*/
void pfnServerExecute( void )
{
Cbuf_Execute();
}
/*
=========
pfnClientCommand
=========
*/
void pfnClientCommand( edict_t* pEdict, char* szFmt, ... )
{
sv_client_t *client;
string buffer;
va_list args;
2009-11-23 22:00:00 +01:00
if( sv.state != ss_active )
{
MsgDev( D_ERROR, "SV_ClientCommand: server is not active!\n" );
return;
}
client = SV_ClientFromEdict( pEdict, true );
if( client == NULL )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_ERROR, "SV_ClientCommand: client is not spawned!\n" );
2008-12-15 22:00:00 +01:00
return;
}
2010-10-09 22:00:00 +02:00
if( client->fakeclient )
2009-06-24 22:00:00 +02:00
return;
2008-12-15 22:00:00 +01:00
va_start( args, szFmt );
com.vsnprintf( buffer, MAX_STRING, szFmt, args );
va_end( args );
2009-11-25 22:00:00 +01:00
if( SV_IsValidCmd( buffer ))
{
2010-10-09 22:00:00 +02:00
BF_WriteByte( &client->netchan.message, svc_stufftext );
BF_WriteString( &client->netchan.message, buffer );
2009-11-25 22:00:00 +01:00
}
else MsgDev( D_ERROR, "Tried to stuff bad command %s\n", buffer );
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnParticleEffect
2009-11-23 22:00:00 +01:00
Make sure the event gets sent to all clients
2008-12-15 22:00:00 +01:00
=================
*/
void pfnParticleEffect( const float *org, const float *dir, float color, float count )
{
2009-11-23 22:00:00 +01:00
int i, v;
if( !org || !dir )
{
if( !org ) MsgDev( D_ERROR, "SV_StartParticle: NULL origin. Ignored\n" );
if( !dir ) MsgDev( D_ERROR, "SV_StartParticle: NULL dir. Ignored\n" );
return;
}
2010-10-09 22:00:00 +02:00
BF_WriteByte( &sv.datagram, svc_particle );
BF_WriteBitVec3Coord( &sv.datagram, org );
2009-11-23 22:00:00 +01:00
for( i = 0; i < 3; i++ )
{
v = bound( -128, dir[i] * 16, 127 );
2010-10-09 22:00:00 +02:00
BF_WriteChar( &sv.datagram, v );
2009-11-23 22:00:00 +01:00
}
2010-10-09 22:00:00 +02:00
BF_WriteByte( &sv.datagram, count );
BF_WriteByte( &sv.datagram, color );
2008-12-15 22:00:00 +01:00
}
/*
===============
pfnLightStyle
===============
*/
2009-11-25 22:00:00 +01:00
void pfnLightStyle( int style, const char* val )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
if( style < 0 ) style = 0;
if( style >= MAX_LIGHTSTYLES )
2008-12-15 22:00:00 +01:00
Host_Error( "SV_LightStyle: style: %i >= %d", style, MAX_LIGHTSTYLES );
2010-07-01 22:00:00 +02:00
2010-10-28 22:00:00 +02:00
SV_SetLightStyle( style, val ); // set correct style
2008-12-15 22:00:00 +01:00
}
/*
=================
pfnDecalIndex
register decal shader on client
=================
*/
int pfnDecalIndex( const char *m )
{
2010-10-26 22:00:00 +02:00
int i;
if( !m || !m[0] )
return 0;
2010-10-28 22:00:00 +02:00
for( i = 1; i < MAX_DECALS && host.draw_decals[i][0]; i++ )
2010-10-26 22:00:00 +02:00
{
2010-10-28 22:00:00 +02:00
if( !com.stricmp( host.draw_decals[i], m ))
2010-10-26 22:00:00 +02:00
return i;
}
// throw warning
MsgDev( D_WARN, "Can't find decal %s\n", m );
return 0;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnPointContents
=============
*/
2008-12-25 22:00:00 +01:00
static int pfnPointContents( const float *rgflVector )
2008-12-15 22:00:00 +01:00
{
return SV_PointContents( rgflVector );
}
/*
=============
pfnMessageBegin
=============
*/
2009-09-28 22:00:00 +02:00
void pfnMessageBegin( int msg_dest, int msg_num, const float *pOrigin, edict_t *ed )
2008-12-15 22:00:00 +01:00
{
2010-08-16 22:00:00 +02:00
int i, iSize;
2009-09-29 22:00:00 +02:00
if( svgame.msg_started )
Host_Error( "MessageBegin: New message started when msg '%s' has not been sent yet\n", svgame.msg_name );
svgame.msg_started = true;
2010-08-16 22:00:00 +02:00
// check range
msg_num = bound( svc_bad, msg_num, 255 );
if( msg_num < svc_lastmsg )
{
svgame.msg_name = NULL;
svgame.msg_index = -1; // this is a system message
if( msg_num == svc_temp_entity )
iSize = -1; // temp entity have variable size
else iSize = 0;
}
else
{
// check for existing
for( i = 0; i < MAX_USER_MESSAGES && svgame.msg[i].name[0]; i++ )
{
if( svgame.msg[i].number == msg_num )
break; // found
}
if( i == MAX_USER_MESSAGES )
{
Host_Error( "MessageBegin: tired to send unregistered message %i\n", msg_num );
return;
}
2008-12-15 22:00:00 +01:00
2010-08-16 22:00:00 +02:00
svgame.msg_name = svgame.msg[i].name;
iSize = svgame.msg[i].size;
svgame.msg_index = i;
}
2009-01-02 22:00:00 +01:00
2010-08-16 22:00:00 +02:00
BF_WriteByte( &sv.multicast, msg_num );
2008-12-15 22:00:00 +01:00
// save message destination
2008-12-26 22:00:00 +01:00
if( pOrigin ) VectorCopy( pOrigin, svgame.msg_org );
else VectorClear( svgame.msg_org );
2010-08-16 22:00:00 +02:00
if( iSize == -1 )
2008-12-26 22:00:00 +01:00
{
// variable sized messages sent size as first byte
2010-08-04 22:00:00 +02:00
svgame.msg_size_index = BF_GetNumBytesWritten( &sv.multicast );
BF_WriteByte( &sv.multicast, 0 ); // reserve space for now
2008-12-26 22:00:00 +01:00
}
2010-08-16 22:00:00 +02:00
else svgame.msg_size_index = -1; // message has constant size
2008-12-26 22:00:00 +01:00
svgame.msg_realsize = 0;
svgame.msg_dest = msg_dest;
svgame.msg_ent = ed;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnMessageEnd
=============
*/
void pfnMessageEnd( void )
{
2009-11-23 22:00:00 +01:00
const char *name = "Unknown";
float *org = NULL;
2009-01-02 22:00:00 +01:00
if( svgame.msg_name ) name = svgame.msg_name;
2010-04-13 22:00:00 +02:00
if( !svgame.msg_started ) Host_Error( "MessageEnd: called with no active message\n" );
2009-09-29 22:00:00 +02:00
svgame.msg_started = false;
2008-12-26 22:00:00 +01:00
2010-08-16 22:00:00 +02:00
// check for system message
if( svgame.msg_index == -1 )
{
if( svgame.msg_size_index != -1 )
{
// variable sized message
2010-11-26 22:00:00 +01:00
if( svgame.msg_realsize >= 255 )
2010-08-16 22:00:00 +02:00
{
MsgDev( D_ERROR, "SV_Message: %s too long (more than 255 bytes)\n", name );
BF_Clear( &sv.multicast );
return;
}
2010-11-26 22:00:00 +01:00
else if( svgame.msg_realsize < 0 )
2010-08-16 22:00:00 +02:00
{
MsgDev( D_ERROR, "SV_Message: %s writes NULL message\n", name );
BF_Clear( &sv.multicast );
return;
}
}
sv.multicast.pData[svgame.msg_size_index] = svgame.msg_realsize;
}
else if( svgame.msg[svgame.msg_index].size != -1 )
2008-12-26 22:00:00 +01:00
{
2010-06-22 22:00:00 +02:00
int expsize = svgame.msg[svgame.msg_index].size;
2008-12-26 22:00:00 +01:00
int realsize = svgame.msg_realsize;
2008-12-17 22:00:00 +01:00
2008-12-26 22:00:00 +01:00
// compare bounds
if( expsize != realsize )
{
2010-06-22 22:00:00 +02:00
MsgDev( D_ERROR, "SV_Message: %s expected %i bytes, it written %i. Ignored.\n", name, expsize, realsize );
2010-08-04 22:00:00 +02:00
BF_Clear( &sv.multicast );
2008-12-26 22:00:00 +01:00
return;
}
}
else if( svgame.msg_size_index != -1 )
2008-12-15 22:00:00 +01:00
{
2008-12-26 22:00:00 +01:00
// variable sized message
2010-10-09 22:00:00 +02:00
if( svgame.msg_realsize >= 255 )
2008-12-26 22:00:00 +01:00
{
MsgDev( D_ERROR, "SV_Message: %s too long (more than 255 bytes)\n", name );
2010-08-04 22:00:00 +02:00
BF_Clear( &sv.multicast );
2008-12-26 22:00:00 +01:00
return;
}
2010-11-26 22:00:00 +01:00
else if( svgame.msg_realsize < 0 )
2009-09-28 22:00:00 +02:00
{
MsgDev( D_ERROR, "SV_Message: %s writes NULL message\n", name );
2010-08-04 22:00:00 +02:00
BF_Clear( &sv.multicast );
2009-09-28 22:00:00 +02:00
return;
}
2010-10-09 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
sv.multicast.pData[svgame.msg_size_index] = svgame.msg_realsize;
2008-12-26 22:00:00 +01:00
}
else
{
2009-09-28 22:00:00 +02:00
// this should never happen
2008-12-26 22:00:00 +01:00
MsgDev( D_ERROR, "SV_Message: %s have encountered error\n", name );
2010-08-04 22:00:00 +02:00
BF_Clear( &sv.multicast );
2008-12-15 22:00:00 +01:00
return;
}
2009-11-23 22:00:00 +01:00
if( !VectorIsNull( svgame.msg_org )) org = svgame.msg_org;
2009-11-25 22:00:00 +01:00
svgame.msg_dest = bound( MSG_BROADCAST, svgame.msg_dest, MSG_SPEC );
2010-08-06 22:00:00 +02:00
SV_Send( svgame.msg_dest, org, svgame.msg_ent );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteByte
=============
*/
void pfnWriteByte( int iValue )
{
2009-01-02 22:00:00 +01:00
if( iValue == -1 ) iValue = 0xFF; // convert char to byte
2010-08-04 22:00:00 +02:00
BF_WriteByte( &sv.multicast, iValue );
2008-12-26 22:00:00 +01:00
svgame.msg_realsize++;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteChar
=============
*/
void pfnWriteChar( int iValue )
{
2010-08-04 22:00:00 +02:00
BF_WriteChar( &sv.multicast, iValue );
2008-12-26 22:00:00 +01:00
svgame.msg_realsize++;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteShort
=============
*/
void pfnWriteShort( int iValue )
{
2010-09-02 22:00:00 +02:00
BF_WriteShort( &sv.multicast, (short)iValue );
2008-12-26 22:00:00 +01:00
svgame.msg_realsize += 2;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteLong
=============
*/
void pfnWriteLong( int iValue )
{
2010-08-04 22:00:00 +02:00
BF_WriteLong( &sv.multicast, iValue );
2008-12-26 22:00:00 +01:00
svgame.msg_realsize += 4;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteAngle
2010-08-16 22:00:00 +02:00
this is low-res angle
2008-12-15 22:00:00 +01:00
=============
*/
void pfnWriteAngle( float flValue )
{
2010-08-16 22:00:00 +02:00
int iAngle = ((int)(( flValue ) * 256 / 360) & 255);
BF_WriteChar( &sv.multicast, iAngle );
svgame.msg_realsize += 1;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteCoord
=============
*/
void pfnWriteCoord( float flValue )
{
2010-08-16 22:00:00 +02:00
BF_WriteShort( &sv.multicast, (int)( flValue * 8.0f ));
svgame.msg_realsize += 2;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteString
=============
*/
2010-08-06 22:00:00 +02:00
void pfnWriteString( const char *src )
2008-12-15 22:00:00 +01:00
{
2010-08-06 22:00:00 +02:00
char *dst, string[MAX_SYSPATH];
int len = com.strlen( src ) + 1;
2008-12-15 22:00:00 +01:00
2010-08-06 22:00:00 +02:00
if( len >= MAX_SYSPATH )
{
MsgDev( D_ERROR, "pfnWriteString: exceeds %i symbols\n", MAX_SYSPATH );
BF_WriteChar( &sv.multicast, 0 );
svgame.msg_realsize += 1;
return;
}
// prepare string to sending
dst = string;
while( 1 )
{
// some escaped chars parsed as two symbols - merge it here
if( src[0] == '\\' && src[1] == 'n' )
{
*dst++ = '\n';
src += 2;
len -= 1;
}
else if( src[0] == '\\' && src[1] == 'r' )
{
*dst++ = '\r';
src += 2;
len -= 1;
}
else if( src[0] == '\\' && src[1] == 't' )
{
*dst++ = '\t';
src += 2;
len -= 1;
}
else if(( *dst++ = *src++ ) == 0 )
break;
}
*dst = '\0'; // string end (not included in count)
BF_WriteString( &sv.multicast, string );
2008-12-15 22:00:00 +01:00
2009-09-28 22:00:00 +02:00
// NOTE: some messages with constant string length can be marked as known sized
2010-08-06 22:00:00 +02:00
svgame.msg_realsize += len;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnWriteEntity
=============
*/
void pfnWriteEntity( int iValue )
{
2010-08-15 22:00:00 +02:00
if( iValue < 0 || iValue >= svgame.numEntities )
2010-08-04 22:00:00 +02:00
Host_Error( "BF_WriteEntity: invalid entnumber %i\n", iValue );
BF_WriteShort( &sv.multicast, iValue );
2008-12-26 22:00:00 +01:00
svgame.msg_realsize += 2;
2008-12-15 22:00:00 +01:00
}
2010-10-22 22:00:00 +02:00
/*
=============
pfnCVarRegister
=============
*/
void pfnCVarRegister( cvar_t *pCvar )
{
Cvar_Register( pCvar );
}
/*
=============
pfnCvar_DirectSet
=============
*/
void pfnCvar_DirectSet( cvar_t *var, char *value )
{
Cvar_DirectSet( var, value );
}
2010-08-15 22:00:00 +02:00
/*
=============
pfnAlertMessage
=============
*/
static void pfnAlertMessage( ALERT_TYPE level, char *szFmt, ... )
{
char buffer[2048]; // must support > 1k messages
va_list args;
va_start( args, szFmt );
com.vsnprintf( buffer, 2048, szFmt, args );
va_end( args );
2010-10-17 22:00:00 +02:00
if( level == at_notice )
{
com.print( buffer ); // notice printing always
}
else if( level == at_console && host.developer >= D_INFO )
2010-08-15 22:00:00 +02:00
{
com.print( buffer );
2010-10-17 22:00:00 +02:00
}
else if( level == at_aiconsole && host.developer >= D_AICONSOLE )
{
com.print( buffer );
}
else if( level == at_warning && host.developer >= D_WARN )
{
2010-09-09 22:00:00 +02:00
com.print( va( "^3Warning:^7 %s", buffer ));
2010-08-15 22:00:00 +02:00
}
2010-10-17 22:00:00 +02:00
else if( level == at_error && host.developer >= D_ERROR )
{
com.print( va( "^1Error:^7 %s", buffer ));
}
2010-08-15 22:00:00 +02:00
}
2010-10-26 22:00:00 +02:00
/*
=============
pfnEngineFprintf
legacy. probably was a part of early save\restore system
=============
*/
static void pfnEngineFprintf( FILE *pfile, char *szFmt, ... )
{
char buffer[2048]; // must support > 1k messages
va_list args;
va_start( args, szFmt );
com.vsnprintf( buffer, 2048, szFmt, args );
va_end( args );
fprintf( pfile, buffer );
}
2008-12-15 22:00:00 +01:00
/*
=============
pfnPvAllocEntPrivateData
2010-10-20 22:00:00 +02:00
=============
*/
void pfnBuildSoundMsg( edict_t *pSource, int chan, const char *samp, float fvol, float attn, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t *pSend )
{
pfnMessageBegin( msg_dest, msg_type, pOrigin, pSend );
SV_BuildSoundMsg( pSource, chan, samp, fvol * 255, attn, fFlags, pitch, pOrigin );
pfnMessageEnd();
}
/*
=============
pfnPvAllocEntPrivateData
2008-12-15 22:00:00 +01:00
=============
*/
void *pfnPvAllocEntPrivateData( edict_t *pEdict, long cb )
{
2010-07-29 22:00:00 +02:00
ASSERT( pEdict );
ASSERT( pEdict->free == false );
2008-12-15 22:00:00 +01:00
// to avoid multiple alloc
2010-08-18 22:00:00 +02:00
pEdict->pvPrivateData = (void *)Mem_Realloc( svgame.mempool, pEdict->pvPrivateData, cb );
2008-12-15 22:00:00 +01:00
2008-12-26 22:00:00 +01:00
return pEdict->pvPrivateData;
2008-12-15 22:00:00 +01:00
}
2009-09-22 22:00:00 +02:00
/*
=============
pfnPvEntPrivateData
=============
*/
void *pfnPvEntPrivateData( edict_t *pEdict )
{
if( pEdict )
return pEdict->pvPrivateData;
return NULL;
}
/*
2008-12-15 22:00:00 +01:00
=============
pfnFreeEntPrivateData
=============
*/
void pfnFreeEntPrivateData( edict_t *pEdict )
{
2009-11-23 22:00:00 +01:00
if( !pEdict ) return;
2008-12-26 22:00:00 +01:00
if( pEdict->pvPrivateData )
Mem_Free( pEdict->pvPrivateData );
pEdict->pvPrivateData = NULL; // freed
}
/*
=============
SV_AllocString
=============
*/
string_t SV_AllocString( const char *szValue )
{
2010-11-19 22:00:00 +01:00
const char *newString;
newString = com.stralloc( svgame.stringspool, szValue, __FILE__, __LINE__ );
return newString - svgame.globals->pStringBase;
2008-12-26 22:00:00 +01:00
}
/*
=============
SV_GetString
=============
*/
const char *SV_GetString( string_t iString )
{
2010-11-19 22:00:00 +01:00
return (svgame.globals->pStringBase + iString);
2008-12-15 22:00:00 +01:00
}
2009-09-22 22:00:00 +02:00
/*
=============
pfnGetVarsOfEnt
=============
*/
entvars_t *pfnGetVarsOfEnt( edict_t *pEdict )
{
if( pEdict )
return &pEdict->v;
return NULL;
}
2008-12-15 22:00:00 +01:00
/*
=============
pfnPEntityOfEntOffset
=============
*/
edict_t* pfnPEntityOfEntOffset( int iEntOffset )
{
2008-12-26 22:00:00 +01:00
return (&((edict_t*)svgame.vp)[iEntOffset]);
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnEntOffsetOfPEntity
=============
*/
int pfnEntOffsetOfPEntity( const edict_t *pEdict )
{
2008-12-26 22:00:00 +01:00
return ((byte *)pEdict - (byte *)svgame.vp);
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnIndexOfEdict
=============
*/
int pfnIndexOfEdict( const edict_t *pEdict )
{
2010-03-30 22:00:00 +02:00
if( !SV_IsValidEdict( pEdict ))
2010-07-02 22:00:00 +02:00
return 0;
2008-12-17 22:00:00 +01:00
return NUM_FOR_EDICT( pEdict );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnPEntityOfEntIndex
=============
*/
edict_t* pfnPEntityOfEntIndex( int iEntIndex )
{
2010-08-15 22:00:00 +02:00
if( iEntIndex < 0 || iEntIndex >= svgame.numEntities )
2009-09-28 22:00:00 +02:00
return NULL; // out of range
2010-08-25 22:00:00 +02:00
if( EDICT_NUM( iEntIndex )->free )
return NULL;
2008-12-17 22:00:00 +01:00
return EDICT_NUM( iEntIndex );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnFindEntityByVars
2009-11-23 22:00:00 +01:00
debug routine
2008-12-15 22:00:00 +01:00
=============
*/
2009-11-23 22:00:00 +01:00
edict_t* pfnFindEntityByVars( entvars_t *pvars )
2008-12-15 22:00:00 +01:00
{
2010-03-30 22:00:00 +02:00
edict_t *e;
2008-12-15 22:00:00 +01:00
int i;
2010-03-30 22:00:00 +02:00
// don't pass invalid arguments
if( !pvars ) return NULL;
2010-08-15 22:00:00 +02:00
for( i = 0; i < svgame.numEntities; i++ )
2008-12-15 22:00:00 +01:00
{
2010-03-30 22:00:00 +02:00
e = EDICT_NUM( i );
2010-10-19 22:00:00 +02:00
if( &e->v == pvars )
2009-11-23 22:00:00 +01:00
return e; // found it
2008-12-15 22:00:00 +01:00
}
return NULL;
}
/*
=============
pfnGetModelPtr
returns pointer to a studiomodel
=============
*/
2009-01-22 22:00:00 +01:00
static void *pfnGetModelPtr( edict_t* pEdict )
2008-12-15 22:00:00 +01:00
{
2010-11-15 22:00:00 +01:00
model_t *mod;
2009-10-28 22:00:00 +01:00
if( !pEdict || pEdict->free )
return NULL;
2010-11-15 22:00:00 +01:00
mod = CM_ClipHandleToModel( pEdict->v.modelindex );
return Mod_Extradata( mod );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnRegUserMsg
=============
*/
int pfnRegUserMsg( const char *pszName, int iSize )
{
2010-06-22 22:00:00 +02:00
int i;
if( !pszName || !pszName[0] )
return svc_bad;
if( com.strlen( pszName ) >= sizeof( svgame.msg[0].name ))
{
MsgDev( D_ERROR, "REG_USER_MSG: too long name %s\n", pszName );
return svc_bad; // force error
}
2008-12-25 22:00:00 +01:00
2010-06-22 22:00:00 +02:00
if( iSize > 255 )
2008-12-25 22:00:00 +01:00
{
2010-06-22 22:00:00 +02:00
MsgDev( D_ERROR, "REG_USER_MSG: %s has too big size %i\n", pszName, iSize );
2008-12-25 22:00:00 +01:00
return svc_bad; // force error
}
2008-12-15 22:00:00 +01:00
2010-06-22 22:00:00 +02:00
// make sure what size inrange
iSize = bound( -1, iSize, 255 );
// message 0 is reserved for svc_bad
for( i = 0; i < MAX_USER_MESSAGES && svgame.msg[i].name[0]; i++ )
{
// see if already registered
if( !com.strcmp( svgame.msg[i].name, pszName ))
2010-08-16 22:00:00 +02:00
return svc_lastmsg + i; // offset
2010-06-22 22:00:00 +02:00
}
if( i == MAX_USER_MESSAGES )
{
MsgDev( D_ERROR, "REG_USER_MSG: user messages limit exceeded\n" );
return svc_bad;
}
// register new message
com.strncpy( svgame.msg[i].name, pszName, sizeof( svgame.msg[i].name ));
2010-08-16 22:00:00 +02:00
svgame.msg[i].number = svc_lastmsg + i;
2010-06-22 22:00:00 +02:00
svgame.msg[i].size = iSize;
2008-12-15 22:00:00 +01:00
2010-07-19 22:00:00 +02:00
// catch some user messages
if( !com.strcmp( pszName, "HudText" ))
2010-08-16 22:00:00 +02:00
svgame.gmsgHudText = svc_lastmsg + i;
2010-07-19 22:00:00 +02:00
2010-07-08 22:00:00 +02:00
if( sv.state == ss_active )
{
2010-08-16 22:00:00 +02:00
// tell the client about new user message
2010-10-09 22:00:00 +02:00
BF_WriteByte( &sv.reliable_datagram, svc_usermessage );
BF_WriteByte( &sv.reliable_datagram, svgame.msg[i].number );
BF_WriteByte( &sv.reliable_datagram, (byte)iSize );
2010-10-19 22:00:00 +02:00
BF_WriteString( &sv.reliable_datagram, svgame.msg[i].name );
2010-07-08 22:00:00 +02:00
}
2010-10-09 22:00:00 +02:00
2010-08-16 22:00:00 +02:00
return svgame.msg[i].number;
2008-12-15 22:00:00 +01:00
}
/*
=============
2010-05-22 22:00:00 +02:00
pfnAnimationAutomove
2008-12-15 22:00:00 +01:00
2010-05-22 22:00:00 +02:00
animating studiomodel
2008-12-15 22:00:00 +01:00
=============
*/
2010-05-22 22:00:00 +02:00
void pfnAnimationAutomove( const edict_t* pEdict, float flTime )
2008-12-15 22:00:00 +01:00
{
2010-10-19 22:00:00 +02:00
// this is empty in the original HL
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnGetBonePosition
=============
*/
2009-12-05 22:00:00 +01:00
static void pfnGetBonePosition( const edict_t* pEdict, int iBone, float *rgflOrigin, float *rgflAngles )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( pEdict ))
{
MsgDev( D_WARN, "SV_GetBonePos: invalid entity %s\n", SV_ClassName( pEdict ));
return;
}
2010-08-25 22:00:00 +02:00
SV_GetBonePosition( (edict_t *)pEdict, iBone, rgflOrigin, rgflAngles );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnFunctionFromName
=============
*/
dword pfnFunctionFromName( const char *pName )
{
2010-03-27 22:00:00 +01:00
return FS_FunctionFromName( svgame.hInstance, pName );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnNameForFunction
=============
*/
const char *pfnNameForFunction( dword function )
{
2010-03-27 22:00:00 +01:00
return FS_NameForFunction( svgame.hInstance, function );
2008-12-15 22:00:00 +01:00
}
2009-09-22 22:00:00 +02:00
/*
=============
pfnClientPrintf
=============
*/
void pfnClientPrintf( edict_t* pEdict, PRINT_TYPE ptype, const char *szMsg )
{
2009-11-23 22:00:00 +01:00
sv_client_t *client;
2008-12-15 22:00:00 +01:00
2009-11-23 22:00:00 +01:00
if( sv.state != ss_active )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
// send message into console during loading
MsgDev( D_INFO, szMsg );
return;
2008-12-15 22:00:00 +01:00
}
2009-11-23 22:00:00 +01:00
client = SV_ClientFromEdict( pEdict, true );
if( client == NULL )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_ERROR, "SV_ClientPrintf: client is not spawned!\n" );
2008-12-15 22:00:00 +01:00
return;
}
2009-11-23 22:00:00 +01:00
switch( ptype )
2009-01-02 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
case print_console:
2010-10-09 22:00:00 +02:00
if( client->fakeclient ) MsgDev( D_INFO, szMsg );
2009-11-23 22:00:00 +01:00
else SV_ClientPrintf( client, PRINT_HIGH, "%s", szMsg );
break;
case print_chat:
2010-10-09 22:00:00 +02:00
if( client->fakeclient ) return;
2009-11-23 22:00:00 +01:00
SV_ClientPrintf( client, PRINT_CHAT, "%s", szMsg );
break;
case print_center:
2010-10-09 22:00:00 +02:00
if( client->fakeclient ) return;
BF_WriteByte( &client->netchan.message, svc_centerprint );
BF_WriteString( &client->netchan.message, szMsg );
2009-11-23 22:00:00 +01:00
break;
2009-01-02 22:00:00 +01:00
}
}
2009-09-22 22:00:00 +02:00
/*
=============
2009-11-23 22:00:00 +01:00
pfnServerPrint
2009-09-22 22:00:00 +02:00
=============
*/
2009-11-23 22:00:00 +01:00
void pfnServerPrint( const char *szMsg )
2009-09-22 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
// while loading in-progress we can sending message only for local client
2010-10-23 22:00:00 +02:00
if( sv.state != ss_active ) MsgDev( D_INFO, szMsg );
2009-11-23 22:00:00 +01:00
else SV_BroadcastPrintf( PRINT_HIGH, "%s", szMsg );
2009-09-22 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
/*
=============
pfnGetAttachment
=============
*/
2009-10-15 22:00:00 +02:00
static void pfnGetAttachment( const edict_t *pEdict, int iAttachment, float *rgflOrigin, float *rgflAngles )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
if( !SV_IsValidEdict( pEdict ))
{
MsgDev( D_WARN, "SV_GetAttachment: invalid entity %s\n", SV_ClassName( pEdict ));
return;
}
2010-08-25 22:00:00 +02:00
SV_StudioGetAttachment(( edict_t *)pEdict, iAttachment, rgflOrigin, rgflAngles );
2008-12-15 22:00:00 +01:00
}
/*
=============
2009-11-23 22:00:00 +01:00
pfnCRC32_Init
2008-12-15 22:00:00 +01:00
=============
*/
2010-08-15 22:00:00 +02:00
void pfnCRC32_Init( dword *pulCRC )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
CRC32_Init( pulCRC );
2008-12-15 22:00:00 +01:00
}
/*
=============
2009-11-23 22:00:00 +01:00
pfnCRC32_ProcessBuffer
2008-12-15 22:00:00 +01:00
=============
*/
2010-08-15 22:00:00 +02:00
void pfnCRC32_ProcessBuffer( dword *pulCRC, void *p, int len )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
CRC32_ProcessBuffer( pulCRC, p, len );
2008-12-15 22:00:00 +01:00
}
2009-09-22 22:00:00 +02:00
/*
=============
2009-11-23 22:00:00 +01:00
pfnCRC32_ProcessByte
2009-09-22 22:00:00 +02:00
=============
*/
2010-08-15 22:00:00 +02:00
void pfnCRC32_ProcessByte( dword *pulCRC, byte ch )
2009-09-22 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
CRC32_ProcessByte( pulCRC, ch );
2009-09-22 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
/*
=============
2009-11-23 22:00:00 +01:00
pfnCRC32_Final
2008-12-15 22:00:00 +01:00
=============
*/
2010-08-15 22:00:00 +02:00
dword pfnCRC32_Final( dword pulCRC )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
CRC32_Final( &pulCRC );
return pulCRC;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnCrosshairAngle
=============
*/
void pfnCrosshairAngle( const edict_t *pClient, float pitch, float yaw )
{
2009-09-29 22:00:00 +02:00
sv_client_t *client;
2009-11-23 22:00:00 +01:00
client = SV_ClientFromEdict( pClient, true );
if( client == NULL )
2009-09-29 22:00:00 +02:00
{
2009-11-23 22:00:00 +01:00
MsgDev( D_ERROR, "SV_SetCrosshairAngle: invalid client!\n" );
return;
}
2010-10-09 22:00:00 +02:00
// fakeclients ignores it silently
if( client->fakeclient ) return;
2009-11-23 22:00:00 +01:00
2010-10-19 22:00:00 +02:00
if( pitch > 180.0f ) pitch -= 360;
if( pitch < -180.0f ) pitch += 360;
if( yaw > 180.0f ) yaw -= 360;
if( yaw < -180.0f ) yaw += 360;
2010-10-09 22:00:00 +02:00
BF_WriteByte( &client->netchan.message, svc_crosshairangle );
2010-10-19 22:00:00 +02:00
BF_WriteChar( &client->netchan.message, pitch * 5 );
BF_WriteChar( &client->netchan.message, yaw * 5 );
2009-11-23 22:00:00 +01:00
}
/*
=============
pfnSetView
=============
*/
void pfnSetView( const edict_t *pClient, const edict_t *pViewent )
{
sv_client_t *client;
if( pClient == NULL || pClient->free )
{
2010-03-21 22:00:00 +01:00
MsgDev( D_ERROR, "PF_SetView: invalid client!\n" );
2009-09-29 22:00:00 +02:00
return;
}
2010-08-18 22:00:00 +02:00
client = SV_ClientFromEdict( pClient, true );
2009-09-29 22:00:00 +02:00
if( !client )
{
2010-03-21 22:00:00 +01:00
MsgDev( D_ERROR, "PF_SetView: not a client!\n" );
2009-09-29 22:00:00 +02:00
return;
}
2009-11-23 22:00:00 +01:00
if( pViewent == NULL || pViewent->free )
{
2010-03-21 22:00:00 +01:00
MsgDev( D_ERROR, "PF_SetView: invalid viewent!\n" );
2009-11-23 22:00:00 +01:00
return;
}
2009-11-25 22:00:00 +01:00
if( pClient == pViewent ) client->pViewEntity = NULL;
else client->pViewEntity = (edict_t *)pViewent;
// fakeclients ignore to send client message
2010-10-09 22:00:00 +02:00
if( client->fakeclient ) return;
2009-11-25 22:00:00 +01:00
2010-08-04 22:00:00 +02:00
BF_WriteByte( &client->netchan.message, svc_setview );
BF_WriteWord( &client->netchan.message, NUM_FOR_EDICT( pViewent ));
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnCompareFileTime
=============
*/
int pfnCompareFileTime( const char *filename1, const char *filename2, int *iCompare )
{
2009-09-28 22:00:00 +02:00
int bRet = 0;
*iCompare = 0;
2008-12-15 22:00:00 +01:00
2009-09-28 22:00:00 +02:00
if( filename1 && filename2 )
2008-12-15 22:00:00 +01:00
{
2010-03-27 22:00:00 +01:00
long ft1 = FS_FileTime( filename1 );
long ft2 = FS_FileTime( filename2 );
2009-09-28 22:00:00 +02:00
*iCompare = Host_CompareFileTime( ft1, ft2 );
bRet = 1;
2008-12-15 22:00:00 +01:00
}
2009-09-28 22:00:00 +02:00
return bRet;
2008-12-15 22:00:00 +01:00
}
/*
=============
2008-12-25 22:00:00 +01:00
pfnStaticDecal
2008-12-15 22:00:00 +01:00
=============
*/
void pfnStaticDecal( const float *origin, int decalIndex, int entityIndex, int modelIndex )
{
2009-11-23 22:00:00 +01:00
if( !origin )
{
MsgDev( D_ERROR, "SV_StaticDecal: NULL origin. Ignored\n" );
return;
}
2010-07-01 22:00:00 +02:00
SV_CreateDecal( origin, decalIndex, entityIndex, modelIndex, FDECAL_PERMANENT );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnPrecacheGeneric
can be used for precache scripts
=============
*/
2009-11-23 22:00:00 +01:00
int pfnPrecacheGeneric( const char *s )
2008-12-15 22:00:00 +01:00
{
2009-11-23 22:00:00 +01:00
return SV_GenericIndex( s );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnIsDedicatedServer
=============
*/
int pfnIsDedicatedServer( void )
{
return (host.type == HOST_DEDICATED);
}
2010-08-15 22:00:00 +02:00
/*
=============
pfnGetPlayerWONId
=============
*/
uint pfnGetPlayerWONId( edict_t *e )
{
2010-10-19 22:00:00 +02:00
int i;
sv_client_t *cl;
if( sv.state != ss_active )
return -1;
if( !SV_ClientFromEdict( e, false ))
return -1;
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
{
if( cl->edict == e && cl->authentication_method == 0 )
{
return cl->WonID;
}
}
2010-08-15 22:00:00 +02:00
return -1;
}
2008-12-15 22:00:00 +01:00
/*
=============
pfnIsMapValid
vaild map must contain one info_player_deatchmatch
=============
*/
int pfnIsMapValid( char *filename )
{
2009-09-28 22:00:00 +02:00
char *spawn_entity;
2010-04-02 22:00:00 +02:00
int flags;
2009-07-12 22:00:00 +02:00
2009-09-28 22:00:00 +02:00
// determine spawn entity classname
2010-10-21 22:00:00 +02:00
// determine spawn entity classname
if( sv_maxclients->integer == 1 )
spawn_entity = GI->sp_entity;
else spawn_entity = GI->mp_entity;
2009-07-12 22:00:00 +02:00
2010-04-02 22:00:00 +02:00
flags = SV_MapIsValid( filename, spawn_entity, NULL );
if(( flags & MAP_IS_EXIST ) && ( flags & MAP_HAS_SPAWNPOINT ))
return true;
return false;
2008-12-15 22:00:00 +01:00
}
2009-11-23 22:00:00 +01:00
/*
=============
pfnFadeClientVolume
=============
*/
2010-10-19 22:00:00 +02:00
void pfnFadeClientVolume( const edict_t *pEdict, int fadePercent, int fadeOutSeconds, int holdTime, int fadeInSeconds )
2009-11-23 22:00:00 +01:00
{
2009-11-25 22:00:00 +01:00
sv_client_t *cl;
cl = SV_ClientFromEdict( pEdict, true );
if( !cl )
{
MsgDev( D_ERROR, "SV_FadeClientVolume: client is not spawned!\n" );
return;
}
2010-10-19 22:00:00 +02:00
if( cl->fakeclient ) return;
2010-10-09 22:00:00 +02:00
BF_WriteByte( &cl->netchan.message, svc_soundfade );
2010-10-19 22:00:00 +02:00
BF_WriteByte( &cl->netchan.message, fadePercent );
BF_WriteByte( &cl->netchan.message, holdTime );
BF_WriteByte( &cl->netchan.message, fadeOutSeconds );
BF_WriteByte( &cl->netchan.message, fadeInSeconds );
2009-11-23 22:00:00 +01:00
}
/*
=============
pfnSetClientMaxspeed
2010-10-19 22:00:00 +02:00
fakeclients can be changed speed to
2009-11-23 22:00:00 +01:00
=============
*/
void pfnSetClientMaxspeed( const edict_t *pEdict, float fNewMaxspeed )
{
sv_client_t *cl;
cl = SV_ClientFromEdict( pEdict, false ); // connected clients allowed
if( !cl )
{
2010-04-09 22:00:00 +02:00
MsgDev( D_ERROR, "SV_SetClientMaxspeed: client is not active!\n" );
2009-11-23 22:00:00 +01:00
return;
}
SV_SetClientMaxspeed( cl, fNewMaxspeed );
}
/*
=============
pfnCreateFakeClient
=============
*/
edict_t *pfnCreateFakeClient( const char *netname )
{
return SV_FakeConnect( netname );
}
/*
=============
2010-06-23 22:00:00 +02:00
pfnRunPlayerMove
2009-11-23 22:00:00 +01:00
=============
*/
2010-06-23 22:00:00 +02:00
void pfnRunPlayerMove( edict_t *pClient, const float *v_angle, float fmove, float smove, float upmove, word buttons, byte impulse, byte msec )
2009-11-23 22:00:00 +01:00
{
sv_client_t *cl;
2010-06-23 22:00:00 +02:00
usercmd_t cmd;
2009-11-23 22:00:00 +01:00
if( sv.paused ) return;
2010-06-23 22:00:00 +02:00
if(( cl = SV_ClientFromEdict( pClient, true )) == NULL )
2009-11-23 22:00:00 +01:00
{
2010-06-23 22:00:00 +02:00
MsgDev( D_ERROR, "SV_ClientThink: fakeclient is not spawned!\n" );
2009-11-23 22:00:00 +01:00
return;
}
2010-10-09 22:00:00 +02:00
if( !cl->fakeclient )
2009-11-23 22:00:00 +01:00
return; // only fakeclients allows
2010-06-23 22:00:00 +02:00
Mem_Set( &cmd, 0, sizeof( cmd ));
if( v_angle ) VectorCopy( v_angle, cmd.viewangles );
cmd.forwardmove = fmove;
cmd.sidemove = smove;
cmd.upmove = upmove;
cmd.buttons = buttons;
cmd.impulse = impulse;
cmd.msec = msec;
2010-08-04 22:00:00 +02:00
cl->random_seed = Com_RandomLong( 0, 0x7fffffff ); // full range
2010-10-14 22:00:00 +02:00
SV_PreRunCmd( cl, &cmd, cl->random_seed );
SV_RunCmd( cl, &cmd, cl->random_seed );
2009-11-23 22:00:00 +01:00
SV_PostRunCmd( cl );
2010-06-23 22:00:00 +02:00
cl->lastcmd = cmd;
2009-11-23 22:00:00 +01:00
cl->lastcmd.buttons = 0; // avoid multiple fires on lag
}
2010-08-15 22:00:00 +02:00
/*
=============
pfnNumberOfEntities
returns actual entity count
=============
*/
int pfnNumberOfEntities( void )
{
2010-10-19 22:00:00 +02:00
int i, total = 0;
for( i = 0; i < svgame.numEntities; i++ )
{
if( !svgame.edicts[i].free )
total++;
}
return total;
2010-08-15 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
/*
=============
pfnInfo_RemoveKey
=============
*/
2010-10-26 22:00:00 +02:00
void pfnInfo_RemoveKey( char *s, const char *key )
2008-12-15 22:00:00 +01:00
{
Info_RemoveKey( s, key );
}
/*
=============
pfnInfoKeyValue
=============
*/
char *pfnInfoKeyValue( char *infobuffer, char *key )
{
return Info_ValueForKey( infobuffer, key );
}
/*
=============
pfnSetKeyValue
=============
*/
void pfnSetKeyValue( char *infobuffer, char *key, char *value )
{
Info_SetValueForKey( infobuffer, key, value );
}
/*
=============
pfnGetInfoKeyBuffer
=============
*/
char *pfnGetInfoKeyBuffer( edict_t *e )
{
2009-11-23 22:00:00 +01:00
sv_client_t *cl;
2008-12-15 22:00:00 +01:00
2009-11-23 22:00:00 +01:00
cl = SV_ClientFromEdict( e, false ); // pfnUserInfoChanged passed
if( cl == NULL )
{
MsgDev( D_ERROR, "SV_GetClientUserinfo: client is not connected!\n" );
return Cvar_Serverinfo(); // otherwise return ServerInfo
}
return cl->userinfo;
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnSetClientKeyValue
=============
*/
void pfnSetClientKeyValue( int clientIndex, char *infobuffer, char *key, char *value )
{
2009-11-23 22:00:00 +01:00
sv_client_t *cl;
if( clientIndex < 0 || clientIndex >= sv_maxclients->integer )
return;
if( svs.clients[clientIndex].state < cs_spawned )
return;
cl = svs.clients + clientIndex;
Info_SetValueForKey( cl->userinfo, key, value );
2009-12-04 22:00:00 +01:00
cl->sendinfo = true;
2008-12-15 22:00:00 +01:00
}
2009-11-10 22:00:00 +01:00
/*
=============
pfnGetPhysicsKeyValue
=============
*/
const char *pfnGetPhysicsKeyValue( const edict_t *pClient, const char *key )
{
2009-11-23 22:00:00 +01:00
sv_client_t *cl;
cl = SV_ClientFromEdict( pClient, false ); // pfnUserInfoChanged passed
if( cl == NULL )
{
MsgDev( D_ERROR, "SV_GetClientPhysKey: client is not connected!\n" );
return "";
}
return Info_ValueForKey( cl->physinfo, key );
2009-11-10 22:00:00 +01:00
}
/*
=============
pfnSetPhysicsKeyValue
=============
*/
void pfnSetPhysicsKeyValue( const edict_t *pClient, const char *key, const char *value )
{
2009-11-23 22:00:00 +01:00
sv_client_t *cl;
cl = SV_ClientFromEdict( pClient, false ); // pfnUserInfoChanged passed
if( cl == NULL )
{
MsgDev( D_ERROR, "SV_SetClientPhysinfo: client is not connected!\n" );
return;
}
2010-08-05 22:00:00 +02:00
Info_SetValueForKey( cl->physinfo, key, value );
2009-11-10 22:00:00 +01:00
}
/*
=============
pfnGetPhysicsInfoString
=============
*/
const char *pfnGetPhysicsInfoString( const edict_t *pClient )
{
2009-11-23 22:00:00 +01:00
sv_client_t *cl;
cl = SV_ClientFromEdict( pClient, false ); // pfnUserInfoChanged passed
if( cl == NULL )
{
MsgDev( D_ERROR, "SV_GetClientPhysinfo: client is not connected!\n" );
return "";
}
return cl->physinfo;
2009-11-10 22:00:00 +01:00
}
2009-01-11 22:00:00 +01:00
/*
=============
pfnPrecacheEvent
2009-11-23 22:00:00 +01:00
register or returns already registered event id
2010-10-28 22:00:00 +02:00
a type of event is ignored at this moment
2009-01-11 22:00:00 +01:00
=============
*/
word pfnPrecacheEvent( int type, const char *psz )
{
2010-10-28 22:00:00 +02:00
return (word)SV_EventIndex( psz );
2009-01-11 22:00:00 +01:00
}
/*
=============
pfnPlaybackEvent
=============
*/
2010-08-12 22:00:00 +02:00
void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin,
float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 )
2009-01-11 22:00:00 +01:00
{
2009-12-02 22:00:00 +01:00
sv_client_t *cl;
event_state_t *es;
2010-06-23 22:00:00 +02:00
event_args_t args;
2009-12-02 22:00:00 +01:00
event_info_t *ei = NULL;
2010-05-22 22:00:00 +02:00
int j, leafnum, slot, bestslot;
int invokerIndex = 0;
2009-12-02 22:00:00 +01:00
byte *mask = NULL;
vec3_t pvspoint;
// first check event for out of bounds
if( eventindex < 1 || eventindex > MAX_EVENTS )
{
MsgDev( D_ERROR, "SV_PlaybackEvent: invalid eventindex %i\n", eventindex );
return;
}
// check event for precached
2010-10-28 22:00:00 +02:00
if( !sv.event_precache[eventindex][0] )
2009-12-02 22:00:00 +01:00
{
MsgDev( D_ERROR, "SV_PlaybackEvent: event %i was not precached\n", eventindex );
return;
}
2009-11-23 22:00:00 +01:00
2010-06-23 22:00:00 +02:00
args.flags = 0;
if( SV_IsValidEdict( pInvoker ))
args.entindex = NUM_FOR_EDICT( pInvoker );
else args.entindex = 0;
VectorCopy( origin, args.origin );
VectorCopy( angles, args.angles );
args.fparam1 = fparam1;
args.fparam2 = fparam2;
args.iparam1 = iparam1;
args.iparam2 = iparam2;
args.bparam1 = bparam1;
args.bparam2 = bparam2;
2009-12-02 22:00:00 +01:00
if(!( flags & FEV_GLOBAL ))
{
// PVS message - trying to get a pvspoint
2010-06-23 22:00:00 +02:00
// args.origin always have higher priority than invoker->origin
if( !VectorIsNull( args.origin ))
2009-12-02 22:00:00 +01:00
{
2010-06-23 22:00:00 +02:00
VectorCopy( args.origin, pvspoint );
2009-12-02 22:00:00 +01:00
}
else if( SV_IsValidEdict( pInvoker ))
{
VectorCopy( pInvoker->v.origin, pvspoint );
}
else
{
2010-10-28 22:00:00 +02:00
const char *ev_name = sv.event_precache[eventindex];
2009-12-02 22:00:00 +01:00
MsgDev( D_ERROR, "%s: not a FEV_GLOBAL event missing origin. Ignored.\n", ev_name );
return;
}
}
// check event for some user errors
if( flags & (FEV_NOTHOST|FEV_HOSTONLY))
{
if( !SV_ClientFromEdict( pInvoker, true ))
{
2010-10-28 22:00:00 +02:00
const char *ev_name = sv.event_precache[eventindex];
2009-12-02 22:00:00 +01:00
if( flags & FEV_NOTHOST )
MsgDev( D_WARN, "%s: specified FEV_NOTHOST when invoker not a client\n", ev_name );
if( flags & FEV_HOSTONLY )
MsgDev( D_WARN, "%s: specified FEV_HOSTONLY when invoker not a client\n", ev_name );
// pInvoker isn't a client
flags &= ~(FEV_NOTHOST|FEV_HOSTONLY);
}
}
flags |= FEV_SERVER; // it's a server event
if( delay < 0.0f )
delay = 0.0f; // fixup negative delays
if( SV_IsValidEdict( pInvoker ))
invokerIndex = NUM_FOR_EDICT( pInvoker );
if( flags & FEV_RELIABLE )
{
2010-06-23 22:00:00 +02:00
args.ducking = 0;
VectorClear( args.velocity );
2009-12-02 22:00:00 +01:00
}
else if( invokerIndex )
{
// get up some info from invoker
2010-06-23 22:00:00 +02:00
if( VectorIsNull( args.origin ))
VectorCopy( pInvoker->v.origin, args.origin );
if( VectorIsNull( args.angles ))
2010-02-26 22:00:00 +01:00
{
if( SV_ClientFromEdict( pInvoker, true ))
2010-06-23 22:00:00 +02:00
VectorCopy( pInvoker->v.v_angle, args.angles );
else VectorCopy( pInvoker->v.angles, args.angles );
2010-02-26 22:00:00 +01:00
}
2010-06-23 22:00:00 +02:00
else if( SV_ClientFromEdict( pInvoker, true ) && VectorCompare( pInvoker->v.angles, args.angles ))
2010-03-14 22:00:00 +01:00
{
// NOTE: if user specified pPlayer->pev->angles
// silently replace it with viewangles, client expected this
2010-06-23 22:00:00 +02:00
VectorCopy( pInvoker->v.v_angle, args.angles );
2010-03-14 22:00:00 +01:00
}
2010-06-23 22:00:00 +02:00
VectorCopy( pInvoker->v.velocity, args.velocity );
args.ducking = (pInvoker->v.flags & FL_DUCKING) ? true : false;
2009-12-02 22:00:00 +01:00
}
if(!( flags & FEV_GLOBAL ))
{
// setup pvs cluster for invoker
2010-11-21 22:00:00 +01:00
leafnum = Mod_PointLeafnum( pvspoint );
2010-12-08 22:00:00 +01:00
mask = SV_LeafPVS( leafnum );
2009-12-02 22:00:00 +01:00
}
// process all the clients
for( slot = 0, cl = svs.clients; slot < sv_maxclients->integer; slot++, cl++ )
{
2010-10-09 22:00:00 +02:00
if( cl->state != cs_spawned || !cl->edict || cl->fakeclient )
2009-12-02 22:00:00 +01:00
continue;
2010-10-21 22:00:00 +02:00
if( flags & FEV_NOTHOST && cl->edict == pInvoker && cl->local_weapons )
2009-12-02 22:00:00 +01:00
continue; // will be played on client side
if( flags & FEV_HOSTONLY && cl->edict != pInvoker )
continue; // sending only to invoker
if(!( flags & FEV_GLOBAL ))
{
2010-11-21 22:00:00 +01:00
leafnum = Mod_PointLeafnum( cl->edict->v.origin );
2010-05-22 22:00:00 +02:00
if( mask && (!(mask[leafnum>>3] & (1<<(leafnum & 7)))))
2009-12-02 22:00:00 +01:00
continue;
}
// all checks passed, send the event
// reliable event
if( flags & FEV_RELIABLE )
{
event_info_t info;
info.index = eventindex;
info.fire_time = delay;
2010-06-23 22:00:00 +02:00
info.args = args;
2009-12-02 22:00:00 +01:00
info.entity_index = invokerIndex;
info.packet_index = -1;
info.flags = 0; // server ignore flags
// skipping queue, write in reliable datagram
2010-10-09 22:00:00 +02:00
BF_WriteByte( &cl->netchan.message, svc_event_reliable );
SV_PlaybackEvent( &cl->netchan.message, &info );
2009-12-02 22:00:00 +01:00
continue;
}
// unreliable event (stores in queue)
es = &cl->events;
bestslot = -1;
for( j = 0; j < MAX_EVENT_QUEUE; j++ )
{
ei = &es->ei[j];
if( ei->index == 0 )
{
// found an empty slot
bestslot = j;
break;
}
}
// no slot found for this player, oh well
if( bestslot == -1 ) continue;
ei->index = eventindex;
ei->fire_time = delay;
2010-06-23 22:00:00 +02:00
ei->args = args;
2009-12-02 22:00:00 +01:00
ei->entity_index = invokerIndex;
ei->packet_index = -1;
ei->flags = 0; // server ignore flags
}
2009-11-23 22:00:00 +01:00
}
2009-11-25 22:00:00 +01:00
/*
=============
2010-05-22 22:00:00 +02:00
pfnSetFatPVS
2009-11-25 22:00:00 +01:00
2010-12-08 22:00:00 +01:00
The client will interpolate the view position,
so we can't use a single PVS point
2009-11-25 22:00:00 +01:00
=============
*/
2010-08-15 22:00:00 +02:00
byte *pfnSetFatPVS( const float *org )
2009-11-23 22:00:00 +01:00
{
2010-12-08 22:00:00 +01:00
if( !svs.pvs || sv_novis->integer || !org )
2010-12-09 22:00:00 +01:00
return world.nullrow;
2010-12-08 22:00:00 +01:00
bitvector = fatpvs;
2010-12-09 22:00:00 +01:00
fatbytes = (sv.worldmodel->numleafs+31)>>3;
2010-12-08 22:00:00 +01:00
if(!( sv.hostflags & SVF_PORTALPASS ))
Mem_Set( bitvector, 0, fatbytes );
2010-12-09 22:00:00 +01:00
SV_AddToFatPVS( org, DVIS_PVS, sv.worldmodel->nodes );
2010-12-08 22:00:00 +01:00
return bitvector;
2009-11-23 22:00:00 +01:00
}
2009-11-25 22:00:00 +01:00
/*
=============
2010-05-22 22:00:00 +02:00
pfnSetFatPHS
2009-11-25 22:00:00 +01:00
2010-12-08 22:00:00 +01:00
The client will interpolate the hear position,
so we can't use a single PHS point
2009-11-25 22:00:00 +01:00
=============
*/
2010-08-15 22:00:00 +02:00
byte *pfnSetFatPAS( const float *org )
2009-11-23 22:00:00 +01:00
{
2010-12-08 22:00:00 +01:00
if( !svs.phs || sv_novis->integer || !org )
2010-12-09 22:00:00 +01:00
return world.nullrow;
2010-12-08 22:00:00 +01:00
bitvector = fatphs;
2010-12-09 22:00:00 +01:00
fatbytes = (sv.worldmodel->numleafs+31)>>3;
2010-12-08 22:00:00 +01:00
if(!( sv.hostflags & SVF_PORTALPASS ))
Mem_Set( bitvector, 0, fatbytes );
2010-12-09 22:00:00 +01:00
SV_AddToFatPVS( org, DVIS_PHS, sv.worldmodel->nodes );
2010-12-08 22:00:00 +01:00
return bitvector;
2009-01-11 22:00:00 +01:00
}
2009-11-25 22:00:00 +01:00
/*
=============
pfnCheckVisibility
=============
*/
2010-08-25 22:00:00 +02:00
int pfnCheckVisibility( const edict_t *ent, byte *pset )
2009-11-23 22:00:00 +01:00
{
2010-10-19 22:00:00 +02:00
int result = 0;
2010-08-25 22:00:00 +02:00
if( !SV_IsValidEdict( ent ))
2009-11-25 22:00:00 +01:00
{
2010-08-25 22:00:00 +02:00
MsgDev( D_WARN, "SV_CheckVisibility: invalid entity %s\n", SV_ClassName( ent ));
2009-11-25 22:00:00 +01:00
return 0;
}
2010-08-25 22:00:00 +02:00
// vis not set - fullvis enabled
if( !pset ) return 1;
2009-11-25 22:00:00 +01:00
2010-08-25 22:00:00 +02:00
if( ent->headnode == -1 )
2009-11-25 22:00:00 +01:00
{
2010-08-25 22:00:00 +02:00
int i;
2010-05-22 22:00:00 +02:00
// check individual leafs
2010-08-25 22:00:00 +02:00
for( i = 0; i < ent->num_leafs; i++ )
2009-11-25 22:00:00 +01:00
{
2010-08-25 22:00:00 +02:00
if( pset[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7 )))
2010-05-22 22:00:00 +02:00
break;
2009-11-25 22:00:00 +01:00
}
2010-05-22 22:00:00 +02:00
2010-08-25 22:00:00 +02:00
if( i == ent->num_leafs )
2010-05-22 22:00:00 +02:00
return 0; // not visible
2010-10-19 22:00:00 +02:00
result = 1; // visible passed by leafs
2009-11-25 22:00:00 +01:00
}
2010-08-25 22:00:00 +02:00
else
{
mnode_t *node;
2010-05-28 22:00:00 +02:00
2010-08-26 22:00:00 +02:00
if( ent->headnode < 0 )
node = (mnode_t *)(sv.worldmodel->leafs + (-1 - ent->headnode));
else node = sv.worldmodel->nodes + ent->headnode;
2010-08-25 22:00:00 +02:00
// too many leafs for individual check, go by headnode
if( !SV_HeadnodeVisible( node, pset ))
return 0;
2010-10-19 22:00:00 +02:00
result = 2; // visible passed by headnode
2010-08-25 22:00:00 +02:00
}
2010-08-26 22:00:00 +02:00
2010-08-25 22:00:00 +02:00
#if 0
// NOTE: uncommenat this if you want to get more accuracy culling on large brushes
2010-12-08 22:00:00 +01:00
if( Mod_GetType( ent->v.modelindex ) == mod_brush )
2010-05-28 22:00:00 +02:00
{
2010-11-21 22:00:00 +01:00
if( !Mod_BoxVisible( ent->v.absmin, ent->v.absmax, pset ))
2010-05-28 22:00:00 +02:00
return 0;
2010-10-19 22:00:00 +02:00
result = 3; // visible passed by BoxVisible
2010-05-28 22:00:00 +02:00
}
2010-08-25 22:00:00 +02:00
#endif
2010-10-19 22:00:00 +02:00
return result;
2009-11-25 22:00:00 +01:00
}
2009-01-11 22:00:00 +01:00
/*
=============
pfnCanSkipPlayer
=============
*/
int pfnCanSkipPlayer( const edict_t *player )
{
2009-11-25 22:00:00 +01:00
sv_client_t *cl;
2010-08-16 22:00:00 +02:00
cl = SV_ClientFromEdict( player, false );
2009-11-25 22:00:00 +01:00
if( cl == NULL )
{
MsgDev( D_ERROR, "SV_CanSkip: client is not connected!\n" );
return false;
}
2010-10-14 22:00:00 +02:00
return cl->local_weapons;
2009-01-11 22:00:00 +01:00
}
2010-06-23 22:00:00 +02:00
/*
=============
pfnGetCurrentPlayer
=============
*/
int pfnGetCurrentPlayer( void )
{
if( svs.currentPlayer )
return svs.currentPlayer - svs.clients;
return -1;
}
2009-09-18 22:00:00 +02:00
/*
=============
2009-11-23 22:00:00 +01:00
pfnSetGroupMask
2009-09-18 22:00:00 +02:00
=============
*/
2009-11-23 22:00:00 +01:00
void pfnSetGroupMask( int mask, int op )
2009-09-18 22:00:00 +02:00
{
2009-11-25 22:00:00 +01:00
svs.groupmask = mask;
svs.groupop = op;
2009-09-18 22:00:00 +02:00
}
2010-08-15 22:00:00 +02:00
/*
=============
pfnCreateInstancedBaseline
=============
*/
int pfnCreateInstancedBaseline( int classname, struct entity_state_s *baseline )
{
2010-10-19 22:00:00 +02:00
int i;
if( !baseline ) return -1;
i = sv.instanced.count;
if( i > 62 ) return 0;
sv.instanced.classnames[i] = classname;
sv.instanced.baselines[i] = *baseline;
sv.instanced.count++;
return i+1;
2010-08-15 22:00:00 +02:00
}
2009-09-22 22:00:00 +02:00
/*
=============
2010-06-23 22:00:00 +02:00
pfnEndSection
2009-09-22 22:00:00 +02:00
=============
*/
2010-06-23 22:00:00 +02:00
void pfnEndSection( const char *pszSection )
2009-09-22 22:00:00 +02:00
{
2010-06-23 22:00:00 +02:00
if( !com.stricmp( "credits", pszSection ))
2009-11-25 22:00:00 +01:00
Host_Credits ();
2010-06-23 22:00:00 +02:00
else Host_EndGame( pszSection );
}
/*
=============
pfnGetPlayerUserId
=============
*/
int pfnGetPlayerUserId( edict_t *e )
{
sv_client_t *cl;
int i;
2010-10-19 22:00:00 +02:00
if( sv.state != ss_active )
2010-06-23 22:00:00 +02:00
return -1;
if( !SV_ClientFromEdict( e, false ))
return -1;
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
{
if( cl->edict == e )
{
return cl->userid;
}
}
// couldn't find it
return -1;
2009-09-22 22:00:00 +02:00
}
2008-12-15 22:00:00 +01:00
/*
=============
2010-08-15 22:00:00 +02:00
pfnSoundTrack
2008-12-15 22:00:00 +01:00
2010-10-31 22:00:00 +01:00
empty trackname - stop previous
2008-12-15 22:00:00 +01:00
=============
*/
2010-08-15 22:00:00 +02:00
void pfnSoundTrack( const char *trackname, int flags )
2008-12-15 22:00:00 +01:00
{
2010-10-31 22:00:00 +01:00
sizebuf_t *msg;
if( !trackname ) return;
if( sv.state == ss_loading )
msg = &sv.signon;
else msg = &sv.reliable_datagram;
// tell the client about new user message
BF_WriteByte( &sv.reliable_datagram, svc_cdtrack );
BF_WriteString( &sv.reliable_datagram, trackname );
2008-12-15 22:00:00 +01:00
}
/*
=============
pfnDropClient
=============
*/
void pfnDropClient( int clientIndex )
{
2008-12-26 22:00:00 +01:00
if( clientIndex < 0 || clientIndex >= svgame.globals->maxClients )
2008-12-15 22:00:00 +01:00
{
MsgDev( D_ERROR, "SV_DropClient: not a client\n" );
return;
}
if( svs.clients[clientIndex].state != cs_spawned )
{
MsgDev( D_ERROR, "SV_DropClient: that client slot is not connected\n" );
return;
}
SV_DropClient( svs.clients + clientIndex );
}
2009-09-22 22:00:00 +02:00
/*
=============
pfnGetPlayerPing
=============
*/
2009-11-23 22:00:00 +01:00
void pfnGetPlayerStats( const edict_t *pClient, int *ping, int *packet_loss )
2009-09-22 22:00:00 +02:00
{
2009-11-25 22:00:00 +01:00
sv_client_t *cl;
cl = SV_ClientFromEdict( pClient, false );
if( cl == NULL )
{
MsgDev( D_ERROR, "SV_GetPlayerStats: client is not connected!\n" );
return;
}
2010-10-19 22:00:00 +02:00
if( ping ) *ping = cl->ping * 1000; // this is should be cl->latency not ping!
2009-11-25 22:00:00 +01:00
if( packet_loss ) *packet_loss = cl->packet_loss;
2009-09-22 22:00:00 +02:00
}
2010-08-15 22:00:00 +02:00
/*
=============
pfnForceUnmodified
=============
*/
void pfnForceUnmodified( FORCE_TYPE type, float *mins, float *maxs, const char *filename )
{
2010-10-19 22:00:00 +02:00
sv_consistency_t *pData;
int i;
if( !filename || !*filename )
{
Host_Error( "SV_ForceUnmodified: bad filename string.\n" );
}
if( sv.state == ss_loading )
{
for( i = 0, pData = sv.consistency_files; i < MAX_MODELS; i++, pData++ )
{
if( !pData->name )
{
pData->name = filename;
pData->force_state = type;
if( mins ) VectorCopy( mins, pData->mins );
if( maxs ) VectorCopy( maxs, pData->maxs );
return;
}
else if( !com.strcmp( filename, pData->name ))
{
return;
}
}
Host_Error( "SV_ForceUnmodified: MAX_MODELS limit exceeded\n" );
}
else
{
for( i = 0, pData = sv.consistency_files; i < MAX_MODELS; i++, pData++ )
{
if( !pData->name || com.strcmp( filename, pData->name ))
continue;
// if we are here' we found a match.
return;
}
Host_Error( "SV_ForceUnmodified: can only be done during precache\n" );
}
2010-08-15 22:00:00 +02:00
}
2009-11-25 22:00:00 +01:00
/*
=============
pfnAddServerCommand
=============
*/
2010-10-22 22:00:00 +02:00
void pfnAddServerCommand( const char *cmd_name, void (*function)(void) )
2009-11-23 22:00:00 +01:00
{
2010-10-23 22:00:00 +02:00
Cmd_AddGameCommand( cmd_name, function );
2009-11-23 22:00:00 +01:00
}
2010-08-15 22:00:00 +02:00
2010-10-19 22:00:00 +02:00
/*
=============
pfnVoice_GetClientListening
=============
*/
2010-10-26 22:00:00 +02:00
qboolean pfnVoice_GetClientListening( int iReceiver, int iSender )
2010-10-19 22:00:00 +02:00
{
int iMaxClients = sv_maxclients->integer;
if( !svs.initialized ) return false;
if( iReceiver <= 0 || iReceiver > iMaxClients || iSender <= 0 || iSender > iMaxClients )
{
MsgDev( D_ERROR, "Voice_GetClientListening: invalid client indexes (%i, %i).\n", iReceiver, iSender );
return false;
}
2010-11-15 22:00:00 +01:00
return ((svs.clients[iSender-1].listeners & ( 1 << iReceiver )) != 0 );
2010-10-19 22:00:00 +02:00
}
/*
=============
pfnVoice_SetClientListening
=============
*/
2010-10-26 22:00:00 +02:00
qboolean pfnVoice_SetClientListening( int iReceiver, int iSender, qboolean bListen )
2010-10-19 22:00:00 +02:00
{
int iMaxClients = sv_maxclients->integer;
if( !svs.initialized ) return false;
if( iReceiver <= 0 || iReceiver > iMaxClients || iSender <= 0 || iSender > iMaxClients )
{
MsgDev( D_ERROR, "Voice_SetClientListening: invalid client indexes (%i, %i).\n", iReceiver, iSender );
return false;
}
if( bListen )
{
2010-11-15 22:00:00 +01:00
svs.clients[iSender-1].listeners |= (1 << iReceiver);
2010-10-19 22:00:00 +02:00
}
else
{
2010-11-15 22:00:00 +01:00
svs.clients[iSender-1].listeners &= ~(1 << iReceiver);
2010-10-19 22:00:00 +02:00
}
return true;
}
2010-08-15 22:00:00 +02:00
/*
=============
pfnGetPlayerAuthId
These function must returns cd-key hashed value
but Xash3D currently doesn't have any security checks
return nullstring for now
=============
*/
const char *pfnGetPlayerAuthId( edict_t *e )
{
2010-10-19 22:00:00 +02:00
sv_client_t *cl;
static string result;
int i;
if( sv.state != ss_active || !SV_IsValidEdict( e ))
{
result[0] = '\0';
return result;
}
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
{
if( cl->edict == e )
{
if( cl->fakeclient )
{
com.strncat( result, "BOT", sizeof( result ));
}
else if( cl->authentication_method == 0 )
{
com.snprintf( result, sizeof( result ), "%u", (uint)cl->WonID );
}
else
{
com.snprintf( result, sizeof( result ), "%s", SV_GetClientIDString( cl ));
}
return result;
}
}
result[0] = '\0';
return result;
2010-08-15 22:00:00 +02:00
}
2009-11-23 22:00:00 +01:00
2008-12-15 22:00:00 +01:00
// engine callbacks
static enginefuncs_t gEngfuncs =
{
pfnPrecacheModel,
pfnPrecacheSound,
pfnSetModel,
pfnModelIndex,
pfnModelFrames,
pfnSetSize,
pfnChangeLevel,
2010-10-26 22:00:00 +02:00
pfnGetSpawnParms,
pfnSaveSpawnParms,
2008-12-15 22:00:00 +01:00
pfnVecToYaw,
2009-11-23 22:00:00 +01:00
pfnVecToAngles,
2008-12-15 22:00:00 +01:00
pfnMoveToOrigin,
pfnChangeYaw,
pfnChangePitch,
pfnFindEntityByString,
pfnGetEntityIllum,
pfnFindEntityInSphere,
pfnFindClientInPVS,
pfnEntitiesInPVS,
pfnMakeVectors,
2008-12-25 22:00:00 +01:00
AngleVectors,
2008-12-15 22:00:00 +01:00
pfnCreateEntity,
pfnRemoveEntity,
2009-09-22 22:00:00 +02:00
pfnCreateNamedEntity,
2008-12-15 22:00:00 +01:00
pfnMakeStatic,
2010-10-17 22:00:00 +02:00
pfnEntIsOnFloor,
2009-11-23 22:00:00 +01:00
pfnDropToFloor,
2008-12-15 22:00:00 +01:00
pfnWalkMove,
pfnSetOrigin,
2009-01-25 22:00:00 +01:00
SV_StartSound,
2008-12-15 22:00:00 +01:00
pfnEmitAmbientSound,
pfnTraceLine,
pfnTraceToss,
2009-09-22 22:00:00 +02:00
pfnTraceMonsterHull,
2008-12-15 22:00:00 +01:00
pfnTraceHull,
pfnTraceModel,
pfnTraceTexture,
2010-10-26 22:00:00 +02:00
pfnTraceSphere,
2009-09-22 22:00:00 +02:00
pfnGetAimVector,
2008-12-15 22:00:00 +01:00
pfnServerCommand,
pfnServerExecute,
pfnClientCommand,
pfnParticleEffect,
pfnLightStyle,
pfnDecalIndex,
pfnPointContents,
pfnMessageBegin,
pfnMessageEnd,
pfnWriteByte,
pfnWriteChar,
pfnWriteShort,
pfnWriteLong,
pfnWriteAngle,
pfnWriteCoord,
pfnWriteString,
pfnWriteEntity,
pfnCVarRegister,
2009-12-04 22:00:00 +01:00
pfnCVarGetValue,
2008-12-15 22:00:00 +01:00
pfnCVarGetString,
2009-12-04 22:00:00 +01:00
pfnCVarSetValue,
2008-12-15 22:00:00 +01:00
pfnCVarSetString,
2009-11-23 22:00:00 +01:00
pfnAlertMessage,
2010-10-26 22:00:00 +02:00
pfnEngineFprintf,
2008-12-15 22:00:00 +01:00
pfnPvAllocEntPrivateData,
2009-09-22 22:00:00 +02:00
pfnPvEntPrivateData,
2008-12-15 22:00:00 +01:00
pfnFreeEntPrivateData,
2008-12-26 22:00:00 +01:00
SV_GetString,
2009-09-22 22:00:00 +02:00
SV_AllocString,
pfnGetVarsOfEnt,
2008-12-15 22:00:00 +01:00
pfnPEntityOfEntOffset,
pfnEntOffsetOfPEntity,
pfnIndexOfEdict,
2009-11-23 22:00:00 +01:00
pfnPEntityOfEntIndex,
2008-12-15 22:00:00 +01:00
pfnFindEntityByVars,
pfnGetModelPtr,
pfnRegUserMsg,
2010-05-22 22:00:00 +02:00
pfnAnimationAutomove,
2009-11-23 22:00:00 +01:00
pfnGetBonePosition,
2008-12-15 22:00:00 +01:00
pfnFunctionFromName,
pfnNameForFunction,
2009-09-22 22:00:00 +02:00
pfnClientPrintf,
2008-12-15 22:00:00 +01:00
pfnServerPrint,
2009-09-22 22:00:00 +02:00
pfnCmd_Args,
2008-12-15 22:00:00 +01:00
pfnCmd_Argv,
2009-09-22 22:00:00 +02:00
pfnCmd_Argc,
2008-12-15 22:00:00 +01:00
pfnGetAttachment,
2009-11-23 22:00:00 +01:00
pfnCRC32_Init,
pfnCRC32_ProcessBuffer,
pfnCRC32_ProcessByte,
pfnCRC32_Final,
2008-12-15 22:00:00 +01:00
pfnRandomLong,
pfnRandomFloat,
2009-09-18 22:00:00 +02:00
pfnSetView,
2009-09-22 22:00:00 +02:00
pfnTime,
2008-12-15 22:00:00 +01:00
pfnCrosshairAngle,
pfnLoadFile,
2009-09-22 22:00:00 +02:00
pfnFreeFile,
2010-06-23 22:00:00 +02:00
pfnEndSection,
2008-12-15 22:00:00 +01:00
pfnCompareFileTime,
2010-04-09 22:00:00 +02:00
pfnGetGameDir,
2010-10-22 22:00:00 +02:00
pfnCVarRegister,
2009-11-23 22:00:00 +01:00
pfnFadeClientVolume,
2009-09-22 22:00:00 +02:00
pfnSetClientMaxspeed,
pfnCreateFakeClient,
2010-06-23 22:00:00 +02:00
pfnRunPlayerMove,
2010-08-15 22:00:00 +02:00
pfnNumberOfEntities,
2009-09-22 22:00:00 +02:00
pfnGetInfoKeyBuffer,
pfnInfoKeyValue,
pfnSetKeyValue,
pfnSetClientKeyValue,
pfnIsMapValid,
2008-12-15 22:00:00 +01:00
pfnStaticDecal,
pfnPrecacheGeneric,
2010-06-23 22:00:00 +02:00
pfnGetPlayerUserId,
2010-10-20 22:00:00 +02:00
pfnBuildSoundMsg,
2009-09-22 22:00:00 +02:00
pfnIsDedicatedServer,
2010-08-15 22:00:00 +02:00
pfnCVarGetPointer,
pfnGetPlayerWONId,
2008-12-15 22:00:00 +01:00
pfnInfo_RemoveKey,
2009-11-10 22:00:00 +01:00
pfnGetPhysicsKeyValue,
pfnSetPhysicsKeyValue,
pfnGetPhysicsInfoString,
2009-01-11 22:00:00 +01:00
pfnPrecacheEvent,
2010-08-12 22:00:00 +02:00
SV_PlaybackEventFull,
2010-05-22 22:00:00 +02:00
pfnSetFatPVS,
pfnSetFatPAS,
2009-11-23 22:00:00 +01:00
pfnCheckVisibility,
2010-08-06 22:00:00 +02:00
Delta_SetField,
Delta_UnsetField,
Delta_AddEncoder,
2010-06-23 22:00:00 +02:00
pfnGetCurrentPlayer,
2009-11-23 22:00:00 +01:00
pfnCanSkipPlayer,
2010-08-06 22:00:00 +02:00
Delta_FindField,
Delta_SetFieldByIndex,
Delta_UnsetFieldByIndex,
2009-11-23 22:00:00 +01:00
pfnSetGroupMask,
2010-08-15 22:00:00 +02:00
pfnCreateInstancedBaseline,
pfnCvar_DirectSet,
pfnForceUnmodified,
2009-11-23 22:00:00 +01:00
pfnGetPlayerStats,
2009-11-25 22:00:00 +01:00
pfnAddServerCommand,
2010-10-19 22:00:00 +02:00
pfnVoice_GetClientListening,
pfnVoice_SetClientListening,
2010-06-23 22:00:00 +02:00
pfnGetPlayerAuthId,
2008-12-15 22:00:00 +01:00
};
/*
====================
SV_ParseEdict
Parses an edict out of the given string, returning the new position
ed should be a properly initialized empty edict.
====================
*/
2010-10-26 22:00:00 +02:00
qboolean SV_ParseEdict( script_t *script, edict_t *ent )
2008-12-15 22:00:00 +01:00
{
KeyValueData pkvd[256]; // per one entity
int i, numpairs = 0;
const char *classname = NULL;
2010-10-26 22:00:00 +02:00
qboolean anglehack;
2008-12-15 22:00:00 +01:00
token_t token;
// go through all the dictionary pairs
while( 1 )
{
string keyname;
// parse key
2010-06-08 22:00:00 +02:00
if( !Com_ReadToken( script, SC_ALLOW_NEWLINES|SC_ALLOW_PATHNAMES2, &token ))
2010-04-13 22:00:00 +02:00
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
2008-12-15 22:00:00 +01:00
if( token.string[0] == '}' ) break; // end of desc
2010-03-09 22:00:00 +01:00
// anglehack is to allow QuakeEd to write single scalar angles
2010-03-30 22:00:00 +02:00
// and allow them to be turned into vectors.
2010-03-09 22:00:00 +01:00
if( !com.strcmp( token.string, "angle" ))
{
com.strncpy( token.string, "angles", sizeof( token.string ));
anglehack = true;
}
else anglehack = false;
com.strncpy( keyname, token.string, sizeof( keyname ));
2008-12-15 22:00:00 +01:00
// parse value
if( !Com_ReadToken( script, SC_ALLOW_PATHNAMES2, &token ))
2010-04-13 22:00:00 +02:00
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
2008-12-15 22:00:00 +01:00
if( token.string[0] == '}' )
2010-04-13 22:00:00 +02:00
Host_Error( "ED_ParseEdict: closing brace without data\n" );
2008-12-15 22:00:00 +01:00
2010-03-09 22:00:00 +01:00
// ignore attempts to set key ""
if( !keyname[0] ) continue;
2010-06-08 22:00:00 +02:00
// "wad" field is completely ignored
2010-03-28 22:00:00 +02:00
if( !com.strcmp( keyname, "wad" ))
continue;
2008-12-15 22:00:00 +01:00
// keynames with a leading underscore are used for utility comments,
// and are immediately discarded by engine
if( keyname[0] == '_' ) continue;
2010-03-09 22:00:00 +01:00
if( anglehack )
{
string temp;
com.strncpy( temp, token.string, sizeof( temp ));
com.snprintf( token.string, sizeof( token.string ), "0 %s 0", temp );
}
2008-12-15 22:00:00 +01:00
// create keyvalue strings
pkvd[numpairs].szClassName = (char *)classname; // unknown at this moment
2010-03-28 22:00:00 +02:00
pkvd[numpairs].szKeyName = copystring( keyname );
pkvd[numpairs].szValue = copystring( token.string );
2008-12-15 22:00:00 +01:00
pkvd[numpairs].fHandled = false;
if( !com.strcmp( keyname, "classname" ) && classname == NULL )
classname = pkvd[numpairs].szValue;
if( ++numpairs >= 256 ) break;
}
2009-01-02 22:00:00 +01:00
2009-01-10 22:00:00 +01:00
ent = SV_AllocPrivateData( ent, MAKE_STRING( classname ));
2008-12-15 22:00:00 +01:00
2009-01-18 22:00:00 +01:00
if( ent->v.flags & FL_KILLME )
return false;
2008-12-15 22:00:00 +01:00
for( i = 0; i < numpairs; i++ )
{
2010-03-28 22:00:00 +02:00
if( !pkvd[i].fHandled )
{
pkvd[i].szClassName = (char *)classname;
svgame.dllFuncs.pfnKeyValue( ent, &pkvd[i] );
}
// no reason to keep this data
Mem_Free( pkvd[i].szKeyName );
Mem_Free( pkvd[i].szValue );
2008-12-15 22:00:00 +01:00
}
return true;
}
/*
================
SV_LoadFromFile
The entities are directly placed in the array, rather than allocated with
ED_Alloc, because otherwise an error loading the map would have entity
number references out of order.
Creates a server's entity / program execution context by
parsing textual entity definitions out of an ent file.
================
*/
void SV_LoadFromFile( script_t *entities )
{
token_t token;
int inhibited, spawned, died;
2009-09-24 22:00:00 +02:00
int current_skill = Cvar_VariableInteger( "skill" ); // lock skill level
2010-12-11 22:00:00 +01:00
qboolean inhibits_ents = (world.version == Q1BSP_VERSION) ? true : false;
2010-10-26 22:00:00 +02:00
qboolean deathmatch = Cvar_VariableInteger( "deathmatch" );
qboolean create_world = true;
2008-12-16 22:00:00 +01:00
edict_t *ent;
2008-12-15 22:00:00 +01:00
inhibited = 0;
spawned = 0;
died = 0;
// parse ents
2010-06-08 22:00:00 +02:00
while( Com_ReadToken( entities, SC_ALLOW_NEWLINES|SC_ALLOW_PATHNAMES2, &token ))
2008-12-15 22:00:00 +01:00
{
if( token.string[0] != '{' )
2010-04-13 22:00:00 +02:00
Host_Error( "ED_LoadFromFile: found %s when expecting {\n", token.string );
2008-12-15 22:00:00 +01:00
2008-12-16 22:00:00 +01:00
if( create_world )
{
create_world = false;
2009-09-24 22:00:00 +02:00
ent = EDICT_NUM( 0 ); // already initialized
2008-12-16 22:00:00 +01:00
}
else ent = SV_AllocEdict();
2008-12-15 22:00:00 +01:00
if( !SV_ParseEdict( entities, ent ))
continue;
2008-12-16 22:00:00 +01:00
2010-12-11 22:00:00 +01:00
// remove things from different skill levels or deathmatch
if( inhibits_ents && deathmatch )
{
if( ent->v.spawnflags & (1<<11))
{
SV_FreeEdict( ent );
inhibited++;
continue;
}
}
else if( inhibits_ents && current_skill == 0 && ent->v.spawnflags & (1<<8))
{
SV_FreeEdict( ent );
inhibited++;
continue;
}
else if( inhibits_ents && current_skill == 1 && ent->v.spawnflags & (1<<9))
{
SV_FreeEdict( ent );
inhibited++;
continue;
}
else if( inhibits_ents && current_skill >= 2 && ent->v.spawnflags & (1<<10))
{
SV_FreeEdict( ent );
inhibited++;
continue;
}
2008-12-26 22:00:00 +01:00
if( svgame.dllFuncs.pfnSpawn( ent ) == -1 )
2008-12-16 22:00:00 +01:00
died++;
else spawned++;
2008-12-15 22:00:00 +01:00
}
2010-09-09 22:00:00 +02:00
2010-12-11 22:00:00 +01:00
MsgDev( D_INFO, "\n%i entities inhibited\n", inhibited );
2008-12-15 22:00:00 +01:00
}
/*
==============
SpawnEntities
Creates a server's entity / program execution context by
parsing textual entity definitions out of an ent file.
==============
*/
void SV_SpawnEntities( const char *mapname, script_t *entities )
{
edict_t *ent;
MsgDev( D_NOTE, "SV_SpawnEntities()\n" );
2010-06-23 22:00:00 +02:00
// reset misc parms
2010-06-16 22:00:00 +02:00
Cvar_Reset( "sv_zmax" );
Cvar_Reset( "sv_wateramp" );
2010-06-23 22:00:00 +02:00
// reset sky parms
2010-06-16 22:00:00 +02:00
Cvar_Reset( "sv_skycolor_r" );
Cvar_Reset( "sv_skycolor_g" );
Cvar_Reset( "sv_skycolor_b" );
Cvar_Reset( "sv_skyvec_x" );
Cvar_Reset( "sv_skyvec_y" );
Cvar_Reset( "sv_skyvec_z" );
2010-06-23 22:00:00 +02:00
Cvar_Reset( "sv_skyname" );
2010-06-16 22:00:00 +02:00
2008-12-17 22:00:00 +01:00
ent = EDICT_NUM( 0 );
2009-09-28 22:00:00 +02:00
if( ent->free ) SV_InitEdict( ent );
2010-10-26 22:00:00 +02:00
ent->v.model = MAKE_STRING( sv.model_precache[1] );
2008-12-15 22:00:00 +01:00
ent->v.modelindex = 1; // world model
ent->v.solid = SOLID_BSP;
ent->v.movetype = MOVETYPE_PUSH;
2009-01-11 22:00:00 +01:00
2009-11-27 22:00:00 +01:00
svgame.globals->maxEntities = GI->max_edicts;
svgame.globals->maxClients = sv_maxclients->integer;
2008-12-26 22:00:00 +01:00
svgame.globals->mapname = MAKE_STRING( sv.name );
2009-09-28 22:00:00 +02:00
svgame.globals->startspot = MAKE_STRING( sv.startspot );
2010-07-23 22:00:00 +02:00
svgame.globals->time = sv_time();
2008-12-15 22:00:00 +01:00
// spawn the rest of the entities on the map
SV_LoadFromFile( entities );
2010-12-08 22:00:00 +01:00
Com_CloseScript( entities );
2008-12-15 22:00:00 +01:00
2010-08-15 22:00:00 +02:00
MsgDev( D_NOTE, "Total %i entities spawned\n", svgame.numEntities );
2008-12-15 22:00:00 +01:00
}
void SV_UnloadProgs( void )
{
2009-09-28 22:00:00 +02:00
SV_DeactivateServer ();
2010-08-04 22:00:00 +02:00
Delta_Shutdown ();
2010-11-19 22:00:00 +01:00
Mem_FreePool( &svgame.stringspool );
2009-01-22 22:00:00 +01:00
2010-10-23 22:00:00 +02:00
if( svgame.dllFuncs2.pfnGameShutdown )
{
svgame.dllFuncs2.pfnGameShutdown ();
}
2010-10-22 22:00:00 +02:00
// now we can unload cvars
Cvar_FullSet( "host_gameloaded", "0", CVAR_INIT );
// must unlink all game cvars,
// before pointers on them will be lost...
Cmd_ExecuteString( "@unlink\n" );
2010-03-27 22:00:00 +01:00
FS_FreeLibrary( svgame.hInstance );
2008-12-26 22:00:00 +01:00
Mem_FreePool( &svgame.mempool );
2009-09-28 22:00:00 +02:00
Mem_Set( &svgame, 0, sizeof( svgame ));
2008-12-15 22:00:00 +01:00
}
2010-10-26 22:00:00 +02:00
qboolean SV_LoadProgs( const char *name )
2008-12-15 22:00:00 +01:00
{
2010-09-02 22:00:00 +02:00
int i, version;
2010-06-23 22:00:00 +02:00
static APIFUNCTION GetEntityAPI;
2010-08-15 22:00:00 +02:00
static APIFUNCTION2 GetEntityAPI2;
2010-06-23 22:00:00 +02:00
static GIVEFNPTRSTODLL GiveFnptrsToDll;
2010-09-02 22:00:00 +02:00
static NEW_DLL_FUNCTIONS_FN GiveNewDllFuncs;
2010-10-23 22:00:00 +02:00
static enginefuncs_t gpEngfuncs;
2008-12-15 22:00:00 +01:00
static globalvars_t gpGlobals;
2009-11-10 22:00:00 +01:00
static playermove_t gpMove;
2008-12-15 22:00:00 +01:00
edict_t *e;
2008-12-26 22:00:00 +01:00
if( svgame.hInstance ) SV_UnloadProgs();
2008-12-15 22:00:00 +01:00
// fill it in
2009-11-10 22:00:00 +01:00
svgame.pmove = &gpMove;
2008-12-26 22:00:00 +01:00
svgame.globals = &gpGlobals;
svgame.mempool = Mem_AllocPool( "Server Edicts Zone" );
2010-03-27 22:00:00 +01:00
svgame.hInstance = FS_LoadLibrary( name, true );
2010-03-28 22:00:00 +02:00
if( !svgame.hInstance ) return false;
2008-12-15 22:00:00 +01:00
2010-08-15 22:00:00 +02:00
// make sure what new dll functions is cleared
Mem_Set( &svgame.dllFuncs2, 0, sizeof( svgame.dllFuncs2 ));
2010-10-23 22:00:00 +02:00
// make local copy of engfuncs to prevent overwrite it with bots.dll
Mem_Copy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs ));
2010-06-23 22:00:00 +02:00
GetEntityAPI = (APIFUNCTION)FS_GetProcAddress( svgame.hInstance, "GetEntityAPI" );
2010-09-02 22:00:00 +02:00
GetEntityAPI2 = (APIFUNCTION2)FS_GetProcAddress( svgame.hInstance, "GetEntityAPI2" );
GiveNewDllFuncs = (NEW_DLL_FUNCTIONS_FN)FS_GetProcAddress( svgame.hInstance, "GetNewDLLFunctions" );
2010-03-27 22:00:00 +01:00
2008-12-15 22:00:00 +01:00
if( !GetEntityAPI )
{
2010-03-28 22:00:00 +02:00
FS_FreeLibrary( svgame.hInstance );
2010-06-23 22:00:00 +02:00
MsgDev( D_NOTE, "SV_LoadProgs: failed to get address of GetEntityAPI proc\n" );
svgame.hInstance = NULL;
return false;
}
GiveFnptrsToDll = (GIVEFNPTRSTODLL)FS_GetProcAddress( svgame.hInstance, "GiveFnptrsToDll" );
if( !GiveFnptrsToDll )
{
FS_FreeLibrary( svgame.hInstance );
MsgDev( D_NOTE, "SV_LoadProgs: failed to get address of GiveFnptrsToDll proc\n" );
2010-03-28 22:00:00 +02:00
svgame.hInstance = NULL;
2010-03-25 22:00:00 +01:00
return false;
2008-12-15 22:00:00 +01:00
}
2010-10-23 22:00:00 +02:00
GiveFnptrsToDll( &gpEngfuncs, svgame.globals );
2010-10-22 22:00:00 +02:00
// get extended callbacks
if( GiveNewDllFuncs )
{
version = NEW_DLL_FUNCTIONS_VERSION;
if( !GiveNewDllFuncs( &svgame.dllFuncs2, &version ))
{
2010-10-26 22:00:00 +02:00
if( version != NEW_DLL_FUNCTIONS_VERSION )
MsgDev( D_WARN, "SV_LoadProgs: new interface version %i should be %i\n", NEW_DLL_FUNCTIONS_VERSION, version );
2010-10-22 22:00:00 +02:00
Mem_Set( &svgame.dllFuncs2, 0, sizeof( svgame.dllFuncs2 ));
}
}
2010-09-02 22:00:00 +02:00
version = INTERFACE_VERSION;
if( !GetEntityAPI( &svgame.dllFuncs, version ))
2008-12-15 22:00:00 +01:00
{
2010-09-02 22:00:00 +02:00
if( !GetEntityAPI2 )
{
FS_FreeLibrary( svgame.hInstance );
MsgDev( D_ERROR, "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
return false;
}
else if( !GetEntityAPI2( &svgame.dllFuncs, &version ))
{
FS_FreeLibrary( svgame.hInstance );
MsgDev( D_ERROR, "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
svgame.hInstance = NULL;
return false;
}
2008-12-15 22:00:00 +01:00
}
2010-11-15 22:00:00 +01:00
if( !SV_InitStudioAPI( ))
{
FS_FreeLibrary( svgame.hInstance );
MsgDev( D_ERROR, "SV_LoadProgs: couldn't get studio API\n" );
svgame.hInstance = NULL;
return false;
}
2010-08-15 22:00:00 +02:00
svgame.globals->pStringBase = ""; // setup string base
2010-03-30 22:00:00 +02:00
2009-09-24 22:00:00 +02:00
svgame.globals->maxEntities = GI->max_edicts;
2009-09-25 22:00:00 +02:00
svgame.globals->maxClients = sv_maxclients->integer;
2008-12-26 22:00:00 +01:00
svgame.edicts = Mem_Alloc( svgame.mempool, sizeof( edict_t ) * svgame.globals->maxEntities );
2010-08-15 22:00:00 +02:00
svgame.numEntities = svgame.globals->maxClients + 1; // clients + world
2008-12-26 22:00:00 +01:00
for( i = 0, e = svgame.edicts; i < svgame.globals->maxEntities; i++, e++ )
2008-12-15 22:00:00 +01:00
e->free = true; // mark all edicts as freed
2010-07-19 22:00:00 +02:00
// clear user messages
svgame.gmsgHudText = -1;
2010-10-22 22:00:00 +02:00
Cvar_FullSet( "host_gameloaded", "1", CVAR_INIT );
2010-11-19 22:00:00 +01:00
svgame.stringspool = Mem_AllocPool( "Server Strings" );
2010-10-22 22:00:00 +02:00
2008-12-26 22:00:00 +01:00
// all done, initialize game
svgame.dllFuncs.pfnGameInit();
2009-11-10 22:00:00 +01:00
// initialize pm_shared
SV_InitClientMove();
2009-12-10 22:00:00 +01:00
2010-08-04 22:00:00 +02:00
Delta_Init ();
2010-08-05 22:00:00 +02:00
// register custom encoders
svgame.dllFuncs.pfnRegisterEncoders();
2009-12-10 22:00:00 +01:00
// fire once
MsgDev( D_INFO, "Dll loaded for mod %s\n", svgame.dllFuncs.pfnGetGameDescription() );
2010-03-25 22:00:00 +01:00
return true;
2008-12-15 22:00:00 +01:00
}