2010-02-15 23:26:55 +00:00
/*
Copyright ( C ) 1996 - 2001 Id Software , Inc .
Copyright ( C ) 2002 - 2009 John Fitzgibbons and others
2014-09-22 08:55:46 +00:00
Copyright ( C ) 2010 - 2014 QuakeSpasm developers
2017-09-17 02:12:53 +00:00
Copyright ( C ) 2016 Spike
2010-02-15 23:26:55 +00:00
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
// sv_main.c -- server main program
# include "quakedef.h"
2011-07-05 06:28:42 +00:00
server_t sv ;
2010-02-15 23:26:55 +00:00
server_static_t svs ;
2011-07-05 06:28:42 +00:00
static char localmodels [ MAX_MODELS ] [ 8 ] ; // inline model names for precache
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
int sv_protocol = PROTOCOL_FITZQUAKE ; //johnfitz
unsigned int sv_protocol_pext2 ; //spike
2010-02-15 23:26:55 +00:00
//============================================================================
2017-09-17 02:12:53 +00:00
void SV_CalcStats ( client_t * client , int * statsi , float * statsf )
{
2018-05-01 00:35:14 +00:00
size_t i ;
2017-09-17 02:12:53 +00:00
edict_t * ent = client - > edict ;
//FIXME: string stats!
int items ;
2018-05-01 00:35:14 +00:00
eval_t * val = GetEdictFieldValue ( ent , qcvm - > extfields . items2 ) ;
2017-09-17 02:12:53 +00:00
if ( val )
items = ( int ) ent - > v . items | ( ( int ) val - > _float < < 23 ) ;
else
items = ( int ) ent - > v . items | ( ( int ) pr_global_struct - > serverflags < < 28 ) ;
memset ( statsi , 0 , sizeof ( * statsi ) * MAX_CL_STATS ) ;
memset ( statsf , 0 , sizeof ( * statsf ) * MAX_CL_STATS ) ;
statsf [ STAT_HEALTH ] = ent - > v . health ;
// statsf[STAT_FRAGS] = ent->v.frags; //obsolete
statsi [ STAT_WEAPON ] = SV_ModelIndex ( PR_GetString ( ent - > v . weaponmodel ) ) ;
if ( ( unsigned int ) statsi [ STAT_WEAPON ] > = client - > limit_models )
statsi [ STAT_WEAPON ] = 0 ;
statsf [ STAT_AMMO ] = ent - > v . currentammo ;
statsf [ STAT_ARMOR ] = ent - > v . armorvalue ;
statsf [ STAT_WEAPONFRAME ] = ent - > v . weaponframe ;
statsf [ STAT_SHELLS ] = ent - > v . ammo_shells ;
statsf [ STAT_NAILS ] = ent - > v . ammo_nails ;
statsf [ STAT_ROCKETS ] = ent - > v . ammo_rockets ;
statsf [ STAT_CELLS ] = ent - > v . ammo_cells ;
statsf [ STAT_ACTIVEWEAPON ] = ent - > v . weapon ; //sent in a way that does NOT depend upon the current mod...
// statsf[STAT_TOTALSECRETS] = pr_global_struct->total_secrets; //don't bother with these, the qc sends extra updates and we don't want to end up with a race condition.
// statsf[STAT_TOTALMONSTERS] = pr_global_struct->total_monsters;
// statsf[STAT_SECRETS] = pr_global_struct->found_secrets;
// statsf[STAT_MONSTERS] = pr_global_struct->killed_monsters;
// statsi[STAT_TIME] = sv.time*1000; //in ms, this was a hack to work around vanilla QW clients not having any concept of serverside time.
// statsi[STAT_MATCHSTARTTIME] = 0*1000; //in ms, set by the mod to when the current match actually starts (so pregame stuff doesn't interfere with game clocks).
// stats[STAT_VIEW2] = NUM_FOR_EDICT(PROG_TO_EDICT(ent->v.view2));
2018-05-01 00:35:14 +00:00
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . viewzoom ) ) & & val - > _float )
2017-09-17 02:12:53 +00:00
{
statsf [ STAT_VIEWZOOM ] = val - > _float * 255 ;
if ( statsf [ STAT_VIEWZOOM ] < 1 )
statsf [ STAT_VIEWZOOM ] = 1 ;
}
//FIXME: add support for clientstat/globalstat qc builtins.
if ( client - > protocol_pext2 & PEXT2_PREDINFO )
{ //predinfo also kills clc_clientdata
statsi [ STAT_ITEMS ] = items ;
statsf [ STAT_VIEWHEIGHT ] = ent - > v . view_ofs [ 2 ] ;
statsf [ STAT_IDEALPITCH ] = ent - > v . idealpitch ;
statsf [ STAT_PUNCHANGLE_X ] = ent - > v . punchangle [ 0 ] ;
statsf [ STAT_PUNCHANGLE_Y ] = ent - > v . punchangle [ 1 ] ;
statsf [ STAT_PUNCHANGLE_Z ] = ent - > v . punchangle [ 2 ] ;
}
/*
if ( client - > protocol_pext2 & PEXT2_PREDINFO )
{ //prediction needs some info on the server's rules
statsf [ STAT_MOVEVARS_FRICTION ] = sv_friction . value ;
statsf [ STAT_MOVEVARS_WATERFRICTION ] = sv_waterfriction . value ;
statsf [ STAT_MOVEVARS_TICRATE ] = host_maxfps . value ;
statsf [ STAT_MOVEVARS_TIMESCALE ] = sv_gamespeed . value ;
statsf [ STAT_MOVEVARS_GRAVITY ] = sv_gravity . value ;
statsf [ STAT_MOVEVARS_STOPSPEED ] = sv_stopspeed . value ;
statsf [ STAT_MOVEVARS_MAXSPEED ] = client - > maxspeed ;
statsf [ STAT_MOVEVARS_SPECTATORMAXSPEED ] = sv_spectatormaxspeed . value ;
statsf [ STAT_MOVEVARS_ACCELERATE ] = sv_accelerate . value ;
statsf [ STAT_MOVEVARS_AIRACCELERATE ] = sv_airaccelerate . value ;
statsf [ STAT_MOVEVARS_WATERACCELERATE ] = sv_wateraccelerate . value ;
statsf [ STAT_MOVEVARS_ENTGRAVITY ] = client - > entgravity / sv_gravity . value ;
statsf [ STAT_MOVEVARS_JUMPVELOCITY ] = sv_jumpvelocity . value ; //bah
statsf [ STAT_MOVEVARS_EDGEFRICTION ] = sv_edgefriction . value ;
statsf [ STAT_MOVEVARS_MAXAIRSPEED ] = client - > maxspeed ;
statsf [ STAT_MOVEVARS_STEPHEIGHT ] = 18 ;
// statsf[STAT_MOVEVARS_AIRACCEL_QW] = 0;
// statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_gravity.value;
}
*/
2018-05-01 00:35:14 +00:00
for ( i = 0 ; i < sv . numcustomstats ; i + + )
{
eval_t * eval = sv . customstats [ i ] . ptr ;
if ( ! eval )
eval = GetEdictFieldValue ( ent , sv . customstats [ i ] . fld ) ;
switch ( sv . customstats [ i ] . type )
{
case ev_ext_integer :
statsi [ sv . customstats [ i ] . idx ] = eval - > _int ;
break ;
case ev_entity :
statsi [ sv . customstats [ i ] . idx ] = NUM_FOR_EDICT ( PROG_TO_EDICT ( eval - > edict ) ) ;
break ;
case ev_float :
statsf [ sv . customstats [ i ] . idx ] = eval - > _float ;
break ;
case ev_vector :
statsf [ sv . customstats [ i ] . idx + 0 ] = eval - > vector [ 0 ] ;
statsf [ sv . customstats [ i ] . idx + 1 ] = eval - > vector [ 1 ] ;
statsf [ sv . customstats [ i ] . idx + 2 ] = eval - > vector [ 2 ] ;
break ;
case ev_string : //not supported in this build... send with svcfte_updatestatstring on change, which is annoying.
case ev_void : //nothing...
case ev_field : //panic! everyone panic!
case ev_function : //doesn't make much sense
case ev_pointer : //doesn't make sense
default :
break ;
}
}
2017-09-17 02:12:53 +00:00
}
/*server-side-only flags that re-use encoding bits*/
# define UF_REMOVE UF_16BIT /*says we removed the entity in this frame*/
# define UF_MOVETYPE UF_EFFECTS2 /*this flag isn't present in the header itself*/
# define UF_RESET2 UF_EXTEND1 /*so new ents are reset multiple times to avoid weird baselines*/
//#define UF_UNUSED UF_EXTEND2 /**/
# define UF_WEAPONFRAME_OLD UF_EXTEND2
# define UF_VIEWANGLES UF_EXTEND3 /**/
static unsigned int SVFTE_DeltaPredCalcBits ( entity_state_t * from , entity_state_t * to )
{
unsigned int bits = 0 ;
// if (from && from->pmovetype != to->pmovetype)
// bits |= UFP_MOVETYPE;
// if (to->movement[0])
// bits |= UFP_FORWARD;
// if (to->movement[1])
// bits |= UFP_SIDE;
// if (to->movement[2])
// bits |= UFP_UP;
if ( to - > velocity [ 0 ] )
bits | = UFP_VELOCITYXY ;
if ( to - > velocity [ 1 ] )
bits | = UFP_VELOCITYXY ;
if ( to - > velocity [ 2 ] )
bits | = UFP_VELOCITYZ ;
// if (to->msec)
// bits |= UFP_MSEC;
return bits ;
}
static unsigned int MSGFTE_DeltaCalcBits ( entity_state_t * from , entity_state_t * to )
{
unsigned int bits = 0 ;
if ( from - > pmovetype ! = to - > pmovetype )
bits | = UF_PREDINFO | UF_MOVETYPE ;
/* if (from->weaponframe != to->weaponframe)
bits | = UF_PREDINFO | UF_WEAPONFRAME_OLD ;
*/ // if (to->pmovetype) //we don't support prediction, but we still need to network the player's velocity for bob etc.
{
if ( SVFTE_DeltaPredCalcBits ( from , to ) )
bits | = UF_PREDINFO ;
//moving players get extra data forced upon them which is not deltatracked
if ( ( bits & UF_PREDINFO ) & & ( from - > velocity [ 0 ] | | from - > velocity [ 1 ] | | from - > velocity [ 2 ] ) )
{
//if we've got player movement then write the origin anyway, to cover packetloss
bits | = UF_ORIGINXY | UF_ORIGINZ ;
//and force angles too, if its not us
// if (host_client != svs.clients + to->number-1)
// bits |= UF_ANGLESXZ | UF_ANGLESY;
}
}
if ( to - > origin [ 0 ] ! = from - > origin [ 0 ] )
bits | = UF_ORIGINXY ;
if ( to - > origin [ 1 ] ! = from - > origin [ 1 ] )
bits | = UF_ORIGINXY ;
if ( to - > origin [ 2 ] ! = from - > origin [ 2 ] )
bits | = UF_ORIGINZ ;
if ( to - > angles [ 0 ] ! = from - > angles [ 0 ] )
bits | = UF_ANGLESXZ ;
if ( to - > angles [ 1 ] ! = from - > angles [ 1 ] )
bits | = UF_ANGLESY ;
if ( to - > angles [ 2 ] ! = from - > angles [ 2 ] )
bits | = UF_ANGLESXZ ;
if ( to - > modelindex ! = from - > modelindex )
bits | = UF_MODEL ;
if ( to - > frame ! = from - > frame )
bits | = UF_FRAME ;
if ( to - > skin ! = from - > skin )
bits | = UF_SKIN ;
if ( to - > colormap ! = from - > colormap )
bits | = UF_COLORMAP ;
if ( to - > effects ! = from - > effects )
bits | = UF_EFFECTS ;
if ( to - > eflags ! = from - > eflags )
bits | = UF_FLAGS ;
// if (to->solidsize != from->solidsize)
// bits |= UF_SOLID;
if ( to - > scale ! = from - > scale )
bits | = UF_SCALE ;
if ( to - > alpha ! = from - > alpha )
bits | = UF_ALPHA ;
// if (to->fatness != from->fatness)
// bits |= UF_FATNESS;
// if (to->hexen2flags != from->hexen2flags || to->abslight != from->abslight)
// bits |= UF_DRAWFLAGS;
// if (to->bonecount != from->bonecount || (to->bonecount && memcmp(frombonedata+from->boneoffset, tobonedata+to->boneoffset, to->bonecount*sizeof(short)*7)))
// bits |= UF_BONEDATA;
// if (!to->bonecount && (to->basebone != from->basebone || to->baseframe != from->baseframe))
// bits |= UF_BONEDATA;
if ( to - > colormod [ 0 ] ! = from - > colormod [ 0 ] | | to - > colormod [ 1 ] ! = from - > colormod [ 1 ] | | to - > colormod [ 2 ] ! = from - > colormod [ 2 ] )
bits | = UF_COLORMOD ;
// if (to->glowsize!=from->glowsize||to->glowcolour!=from->glowcolour||to->glowmod[0]!=from->glowmod[0]||to->glowmod[1]!=from->glowmod[1]||to->glowmod[2]!=from->glowmod[2])
// bits |= UF_GLOW;
if ( to - > tagentity ! = from - > tagentity | | to - > tagindex ! = from - > tagindex )
bits | = UF_TAGINFO ;
// if (to->light[0] != from->light[0] || to->light[1] != from->light[1] || to->light[2] != from->light[2] || to->light[3] != from->light[3] || to->lightstyle != from->lightstyle || to->lightpflags != from->lightpflags)
// bits |= UF_LIGHT;
if ( to - > traileffectnum ! = from - > traileffectnum | | to - > emiteffectnum ! = from - > emiteffectnum )
bits | = UF_TRAILEFFECT ;
// if (to->modelindex2 != from->modelindex2)
// bits |= UF_MODELINDEX2;
// if (to->gravitydir[0] != from->gravitydir[0] || to->gravitydir[1] != from->gravitydir[1])
// bits |= UF_GRAVITYDIR;
return bits ;
}
2018-05-01 00:35:14 +00:00
//#undef MSG_WriteEntity
void MSG_WriteEntity ( sizebuf_t * sb , int c , unsigned int pext2 )
{
//high short, low byte
if ( c > 0x7fff & & ( pext2 & PEXT2_REPLACEMENTDELTAS ) )
{
MSG_WriteShort ( sb , 0x8000 | ( c > > 8 ) ) ;
MSG_WriteByte ( sb , c & 0xff ) ;
}
else
MSG_WriteShort ( sb , c ) ;
}
2017-09-17 02:12:53 +00:00
static void MSGFTE_WriteEntityUpdate ( unsigned int bits , entity_state_t * state , sizebuf_t * msg , unsigned int pext2 , unsigned int protocolflags )
{
unsigned int predbits = 0 ;
if ( bits & UF_MOVETYPE )
{
bits & = ~ UF_MOVETYPE ;
predbits | = UFP_MOVETYPE ;
}
if ( pext2 & PEXT2_PREDINFO )
{
if ( bits & UF_VIEWANGLES )
{
bits & = ~ UF_VIEWANGLES ;
bits | = UF_PREDINFO ;
predbits | = UFP_VIEWANGLE ;
}
}
else
{
if ( bits & UF_VIEWANGLES )
{
bits & = ~ UF_VIEWANGLES ;
bits | = UF_PREDINFO ;
}
if ( bits & UF_WEAPONFRAME_OLD )
{
bits & = ~ UF_WEAPONFRAME_OLD ;
predbits | = UFP_WEAPONFRAME_OLD ;
}
}
// if (!(pext2 & PEXT2_NEWSIZEENCODING)) //was added at the same time
bits & = ~ UF_BONEDATA ;
/*check if we need more precision for some things*/
if ( ( bits & UF_MODEL ) & & state - > modelindex > 255 )
bits | = UF_16BIT ;
// if ((bits & UF_SKIN) && state->skin > 255)
// bits |= UF_16BIT;
if ( ( bits & UF_FRAME ) & & state - > frame > 255 )
bits | = UF_16BIT ;
/*convert effects bits to higher lengths if needed*/
if ( bits & UF_EFFECTS )
{
if ( state - > effects & 0xffff0000 ) /*both*/
bits | = UF_EFFECTS | UF_EFFECTS2 ;
else if ( state - > effects & 0x0000ff00 ) /*2 only*/
bits = ( bits & ~ UF_EFFECTS ) | UF_EFFECTS2 ;
}
if ( bits & 0xff000000 )
bits | = UF_EXTEND3 ;
if ( bits & 0x00ff0000 )
bits | = UF_EXTEND2 ;
if ( bits & 0x0000ff00 )
bits | = UF_EXTEND1 ;
MSG_WriteByte ( msg , ( bits > > 0 ) & 0xff ) ;
if ( bits & UF_EXTEND1 )
MSG_WriteByte ( msg , ( bits > > 8 ) & 0xff ) ;
if ( bits & UF_EXTEND2 )
MSG_WriteByte ( msg , ( bits > > 16 ) & 0xff ) ;
if ( bits & UF_EXTEND3 )
MSG_WriteByte ( msg , ( bits > > 24 ) & 0xff ) ;
if ( bits & UF_FRAME )
{
if ( bits & UF_16BIT )
MSG_WriteShort ( msg , state - > frame ) ;
else
MSG_WriteByte ( msg , state - > frame ) ;
}
if ( bits & UF_ORIGINXY )
{
MSG_WriteCoord ( msg , state - > origin [ 0 ] , protocolflags ) ;
MSG_WriteCoord ( msg , state - > origin [ 1 ] , protocolflags ) ;
}
if ( bits & UF_ORIGINZ )
MSG_WriteCoord ( msg , state - > origin [ 2 ] , protocolflags ) ;
if ( ( bits & UF_PREDINFO ) & & ! ( pext2 & PEXT2_PREDINFO ) )
{ /*if we have pred info, (always) use more precise angles*/
if ( bits & UF_ANGLESXZ )
{
MSG_WriteAngle16 ( msg , state - > angles [ 0 ] , protocolflags ) ;
MSG_WriteAngle16 ( msg , state - > angles [ 2 ] , protocolflags ) ;
}
if ( bits & UF_ANGLESY )
MSG_WriteAngle16 ( msg , state - > angles [ 1 ] , protocolflags ) ;
}
else
{
if ( bits & UF_ANGLESXZ )
{
MSG_WriteAngle ( msg , state - > angles [ 0 ] , protocolflags ) ;
MSG_WriteAngle ( msg , state - > angles [ 2 ] , protocolflags ) ;
}
if ( bits & UF_ANGLESY )
MSG_WriteAngle ( msg , state - > angles [ 1 ] , protocolflags ) ;
}
if ( ( bits & ( UF_EFFECTS | UF_EFFECTS2 ) ) = = ( UF_EFFECTS | UF_EFFECTS2 ) )
MSG_WriteLong ( msg , state - > effects ) ;
else if ( bits & UF_EFFECTS2 )
MSG_WriteShort ( msg , state - > effects ) ;
else if ( bits & UF_EFFECTS )
MSG_WriteByte ( msg , state - > effects ) ;
if ( bits & UF_PREDINFO )
{
/*movetype is set above somewhere*/
predbits | = SVFTE_DeltaPredCalcBits ( NULL , state ) ;
MSG_WriteByte ( msg , predbits ) ;
/*
if ( predbits & UFP_FORWARD )
MSG_WriteShort ( msg , state - > movement [ 0 ] ) ;
if ( predbits & UFP_SIDE )
MSG_WriteShort ( msg , state - > movement [ 1 ] ) ;
if ( predbits & UFP_UP )
MSG_WriteShort ( msg , state - > movement [ 2 ] ) ;
*/ if ( predbits & UFP_MOVETYPE )
MSG_WriteByte ( msg , state - > pmovetype ) ;
if ( predbits & UFP_VELOCITYXY )
{
MSG_WriteShort ( msg , state - > velocity [ 0 ] ) ;
MSG_WriteShort ( msg , state - > velocity [ 1 ] ) ;
}
if ( predbits & UFP_VELOCITYZ )
MSG_WriteShort ( msg , state - > velocity [ 2 ] ) ;
/* if (predbits & UFP_MSEC)
MSG_WriteByte ( msg , state - > msec ) ;
if ( pext2 & PEXT2_PREDINFO )
{
if ( predbits & UFP_VIEWANGLE )
{ //if we have pred info, use more precise angles
if ( bits & UF_ANGLESXZ )
{
MSG_WriteShort ( msg , state - > vangle [ 0 ] ) ;
MSG_WriteShort ( msg , state - > vangle [ 2 ] ) ;
}
if ( bits & UF_ANGLESY )
MSG_WriteShort ( msg , state - > vangle [ 1 ] ) ;
}
}
else
{
if ( predbits & UFP_WEAPONFRAME_OLD )
{
if ( state - > weaponframe > 127 )
{
MSG_WriteByte ( msg , 128 | ( state - > weaponframe & 127 ) ) ;
MSG_WriteByte ( msg , state - > weaponframe > > 7 ) ;
}
else
MSG_WriteByte ( msg , state - > weaponframe ) ;
}
}
*/
}
if ( bits & UF_MODEL )
{
if ( bits & UF_16BIT )
MSG_WriteShort ( msg , state - > modelindex ) ;
else
MSG_WriteByte ( msg , state - > modelindex ) ;
}
if ( bits & UF_SKIN )
{
if ( bits & UF_16BIT )
MSG_WriteShort ( msg , state - > skin ) ;
else
MSG_WriteByte ( msg , state - > skin ) ;
}
if ( bits & UF_COLORMAP )
MSG_WriteByte ( msg , state - > colormap & 0xff ) ;
if ( bits & UF_SOLID )
{
/* if (pext2 & PEXT2_NEWSIZEENCODING)
{
if ( ! state - > solidsize )
MSG_WriteByte ( msg , 0 ) ;
else if ( state - > solidsize = = ES_SOLID_BSP )
MSG_WriteByte ( msg , 1 ) ;
else if ( state - > solidsize = = ES_SOLID_HULL1 )
MSG_WriteByte ( msg , 2 ) ;
else if ( state - > solidsize = = ES_SOLID_HULL2 )
MSG_WriteByte ( msg , 3 ) ;
else if ( ! ES_SOLID_HAS_EXTRA_BITS ( state - > solidsize ) )
{
MSG_WriteByte ( msg , 16 ) ;
MSG_WriteSize16 ( msg , state - > solidsize ) ;
}
else
{
MSG_WriteByte ( msg , 32 ) ;
MSG_WriteLong ( msg , state - > solidsize ) ;
}
}
else
MSG_WriteSize16 ( msg , state - > solidsize ) ;
*/ }
if ( bits & UF_FLAGS )
MSG_WriteByte ( msg , state - > eflags ) ;
if ( bits & UF_ALPHA )
MSG_WriteByte ( msg , ( state - > alpha - 1 ) & 0xff ) ;
if ( bits & UF_SCALE )
MSG_WriteByte ( msg , state - > scale ) ;
/* if (bits & UF_BONEDATA)
{
short * bonedata ;
int i ;
byte bfl = 0 ;
if ( state - > bonecount & & boneptr )
bfl | = 0x80 ;
if ( state - > basebone | | state - > baseframe )
bfl | = 0x40 ;
MSG_WriteByte ( msg , bfl ) ;
if ( bfl & 0x80 )
{
//this is NOT finalized
MSG_WriteByte ( msg , state - > bonecount ) ;
bonedata = ( short * ) ( boneptr + state - > boneoffset ) ;
for ( i = 0 ; i < state - > bonecount * 7 ; i + + )
MSG_WriteShort ( msg , bonedata [ i ] ) ;
}
if ( bfl & 0x40 )
{
MSG_WriteByte ( msg , state - > basebone ) ;
MSG_WriteShort ( msg , state - > baseframe ) ;
}
}
if ( bits & UF_DRAWFLAGS )
{
MSG_WriteByte ( msg , state - > drawflags ) ;
if ( ( state - > drawflags & MLS_MASK ) = = MLS_ABSLIGHT )
MSG_WriteByte ( msg , state - > abslight ) ;
}
*/ if ( bits & UF_TAGINFO )
{
2018-05-01 00:35:14 +00:00
MSG_WriteEntity ( msg , state - > tagentity , pext2 ) ;
2017-09-17 02:12:53 +00:00
MSG_WriteByte ( msg , state - > tagindex ) ;
}
/* if (bits & UF_LIGHT)
{
MSG_WriteShort ( msg , state - > light [ 0 ] ) ;
MSG_WriteShort ( msg , state - > light [ 1 ] ) ;
MSG_WriteShort ( msg , state - > light [ 2 ] ) ;
MSG_WriteShort ( msg , state - > light [ 3 ] ) ;
MSG_WriteByte ( msg , state - > lightstyle ) ;
MSG_WriteByte ( msg , state - > lightpflags ) ;
}
*/ if ( bits & UF_TRAILEFFECT )
{
if ( state - > emiteffectnum )
{ //3 spare bits. so that's nice (this is guarenteed to be 14 bits max due to precaches using the upper two bits).
MSG_WriteShort ( msg , ( state - > traileffectnum & 0x3fff ) | 0x8000 ) ;
MSG_WriteShort ( msg , state - > emiteffectnum & 0x3fff ) ;
}
else
MSG_WriteShort ( msg , state - > traileffectnum & 0x3fff ) ;
}
if ( bits & UF_COLORMOD )
{
MSG_WriteByte ( msg , state - > colormod [ 0 ] ) ;
MSG_WriteByte ( msg , state - > colormod [ 1 ] ) ;
MSG_WriteByte ( msg , state - > colormod [ 2 ] ) ;
}
/* if (bits & UF_GLOW)
{
MSG_WriteByte ( msg , state - > glowsize ) ;
MSG_WriteByte ( msg , state - > glowcolour ) ;
MSG_WriteByte ( msg , state - > glowmod [ 0 ] ) ;
MSG_WriteByte ( msg , state - > glowmod [ 1 ] ) ;
MSG_WriteByte ( msg , state - > glowmod [ 2 ] ) ;
}
if ( bits & UF_FATNESS )
MSG_WriteByte ( msg , state - > fatness ) ;
if ( bits & UF_MODELINDEX2 )
{
if ( bits & UF_16BIT )
MSG_WriteShort ( msg , state - > modelindex2 ) ;
else
MSG_WriteByte ( msg , state - > modelindex2 ) ;
}
if ( bits & UF_GRAVITYDIR )
{
MSG_WriteByte ( msg , state - > gravitydir [ 0 ] ) ;
MSG_WriteByte ( msg , state - > gravitydir [ 1 ] ) ;
}
*/
}
static struct entity_num_state_s * snapshot_entstate ;
static size_t snapshot_numents ;
static size_t snapshot_maxents ;
void SVFTE_DestroyFrames ( client_t * client )
{
if ( client - > previousentities )
free ( client - > previousentities ) ;
client - > previousentities = NULL ;
client - > numpreviousentities = 0 ;
client - > maxpreviousentities = 0 ;
if ( client - > pendingentities_bits )
free ( client - > pendingentities_bits ) ;
client - > pendingentities_bits = NULL ;
client - > numpendingentities = 0 ;
while ( client - > numframes > 0 )
{
client - > numframes - - ;
free ( client - > frames [ client - > numframes ] . ents ) ;
}
if ( client - > frames )
free ( client - > frames ) ;
client - > frames = NULL ;
client - > lastacksequence = 0 ;
}
static void SVFTE_SetupFrames ( client_t * client )
{
size_t fr ;
//the client will clear out their stats on receipt of the svc_serverinfo packet.
//we won't send any reliables until they receive it
//so it should be enough to just clear these here, and they'll get their new stats with the first entity update once they're spawned
memset ( client - > oldstats_i , 0 , sizeof ( client - > oldstats_i ) ) ;
memset ( client - > oldstats_f , 0 , sizeof ( client - > oldstats_f ) ) ;
client - > lastmovemessage = 0 ; //it'll clear this too
if ( ! client - > protocol_pext2 )
{
SVFTE_DestroyFrames ( client ) ;
return ;
}
client - > numframes = 64 ; //must be power-of-two
client - > frames = malloc ( sizeof ( * client - > frames ) * client - > numframes ) ;
client - > lastacksequence = ( int ) 0x80000000 ;
memset ( client - > frames , 0 , sizeof ( * client - > frames ) * client - > numframes ) ;
for ( fr = 0 ; fr < client - > numframes ; fr + + )
client - > frames [ fr ] . sequence = client - > lastacksequence ;
2018-05-01 00:35:14 +00:00
client - > numpendingentities = qcvm - > num_edicts ;
2017-09-17 02:12:53 +00:00
client - > pendingentities_bits = calloc ( client - > numpendingentities , sizeof ( * client - > pendingentities_bits ) ) ;
client - > pendingentities_bits [ 0 ] = UF_REMOVE ;
}
static void SVFTE_DroppedFrame ( client_t * client , int sequence )
{
int i ;
struct deltaframe_s * frame = & client - > frames [ sequence & ( client - > numframes - 1 ) ] ;
if ( frame - > sequence ! = sequence )
return ; //this frame was stale... client is running too far behind. we'll probably be spamming resends as a result.
frame - > sequence = - 1 ;
//flag their stats for resend
for ( i = 0 ; i < MAX_CL_STATS / 32 ; i + + )
client - > resendstats [ i ] | = frame - > resendstats [ i ] ;
//flag their various entities as needing a resend too.
for ( i = 0 ; i < frame - > numents ; i + + )
client - > pendingentities_bits [ frame - > ents [ i ] . num ] | = frame - > ents [ i ] . bits ;
}
void SVFTE_Ack ( client_t * client , int sequence )
{ //any gaps in the sequence need to considered dropped
struct deltaframe_s * frame ;
int dropseq = client - > lastacksequence + 1 ;
if ( ! client - > numframes )
return ; //client shouldn't be using this.
if ( sequence = = - 1 )
client - > pendingentities_bits [ 0 ] | = UF_REMOVE ; //client wants a full resend. which might happen from it just starting to record a demo, saving it from writing all the deltas out.
if ( sequence < client - > lastacksequence )
{
// else Con_SafePrintf("dupe or stale ack (%s, %i->%i)\n", client->name, client->lastacksequence, sequence);
return ; //panic
}
if ( ( unsigned ) ( dropseq - sequence ) > = client - > numframes )
dropseq = sequence - client - > numframes ;
while ( dropseq < sequence )
SVFTE_DroppedFrame ( client , dropseq + + ) ;
client - > lastacksequence = sequence ;
frame = & client - > frames [ sequence & ( client - > numframes - 1 ) ] ;
if ( frame - > sequence > = 0 )
{
frame - > sequence = - 1 ;
2018-05-01 00:35:14 +00:00
host_client - > ping_times [ host_client - > num_pings % NUM_PING_TIMES ] = qcvm - > time - frame - > timestamp ;
2017-09-17 02:12:53 +00:00
host_client - > num_pings + + ;
}
}
static void SVFTE_WriteStats ( client_t * client , sizebuf_t * msg )
{
int statsi [ MAX_CL_STATS ] ;
float statsf [ MAX_CL_STATS ] ;
int i ;
struct deltaframe_s * frame ;
int sequence = NET_QSocketGetSequenceOut ( client - > netconnection ) ;
frame = & client - > frames [ sequence & ( client - > numframes - 1 ) ] ;
if ( frame - > sequence = = sequence - ( int ) client - > numframes ) //client is getting behind... this may get really spammy, lets hope it clears up at some point
SVFTE_DroppedFrame ( client , frame - > sequence ) ;
//figure out the current values in a nice easy way (yay for copying to make arrays easier!)
SV_CalcStats ( client , statsi , statsf ) ;
for ( i = 0 ; i < MAX_CL_STATS ; i + + )
{
//small cleanup
if ( ! statsi [ i ] )
statsi [ i ] = statsf [ i ] ;
else
statsf [ i ] = 0 ; //statsi[i];
//if it changed flag for sending
if ( statsi [ i ] ! = client - > oldstats_i [ i ] | | statsf [ i ] ! = client - > oldstats_f [ i ] )
{
client - > oldstats_i [ i ] = statsi [ i ] ;
client - > oldstats_f [ i ] = statsf [ i ] ;
client - > resendstats [ i / 32 ] | = 1u < < ( i & 31 ) ;
}
//if its flagged then unflag it, log it, and send it
if ( client - > resendstats [ i / 32 ] & ( 1u < < ( i & 31 ) ) )
{
client - > resendstats [ i / 32 ] & = ~ ( 1u < < ( i & 31 ) ) ;
frame - > resendstats [ i / 32 ] | = 1u < < ( i & 31 ) ;
if ( ( double ) statsi [ i ] ! = statsf [ i ] & & statsf [ i ] )
{ //didn't round nicely, so send as a float
MSG_WriteByte ( msg , svcfte_updatestatfloat ) ;
MSG_WriteByte ( msg , i ) ;
MSG_WriteFloat ( msg , statsf [ i ] ) ;
}
else
{
if ( statsi [ i ] < 0 | | statsi [ i ] > 255 )
{ //needs to be big
MSG_WriteByte ( msg , svc_updatestat ) ;
MSG_WriteByte ( msg , i ) ;
MSG_WriteLong ( msg , statsi [ i ] ) ;
}
else
{ //can be fairly small
MSG_WriteByte ( msg , svcdp_updatestatbyte ) ;
MSG_WriteByte ( msg , i ) ;
MSG_WriteByte ( msg , statsi [ i ] ) ;
}
}
}
}
}
static void SVFTE_CalcEntityDeltas ( client_t * client )
{
struct entity_num_state_s * olds , * news , * oldstop , * newstop ;
2018-05-01 00:35:14 +00:00
if ( ( int ) client - > numpendingentities < qcvm - > num_edicts )
2017-09-17 02:12:53 +00:00
{
2018-05-01 00:35:14 +00:00
int newmax = qcvm - > num_edicts + 64 ;
2017-09-17 02:12:53 +00:00
client - > pendingentities_bits = realloc ( client - > pendingentities_bits , sizeof ( * client - > pendingentities_bits ) * newmax ) ;
memset ( client - > pendingentities_bits + client - > numpendingentities , 0 , sizeof ( * client - > pendingentities_bits ) * ( newmax - client - > numpendingentities ) ) ;
client - > numpendingentities = newmax ;
}
//if we're clearing the list and starting from scratch, just wipe all lingering state
if ( client - > pendingentities_bits [ 0 ] & UF_REMOVE )
{
client - > numpreviousentities = 0 ;
client - > pendingentities_bits [ 0 ] = UF_REMOVE ;
}
news = snapshot_entstate ;
newstop = news + snapshot_numents ;
olds = client - > previousentities ;
oldstop = olds + client - > numpreviousentities ;
//we have two sets of entity state, pvs culled etc already.
//figure out which flags changed,
for ( ; ; )
{
if ( olds = = oldstop & & news = = newstop )
break ;
if ( news = = newstop | | ( olds ! = oldstop & & olds - > num < news - > num ) )
{
//old ent is no longer visible, so flag for removal.
client - > pendingentities_bits [ olds - > num ] = UF_REMOVE ;
olds + + ;
}
else if ( olds = = oldstop | | ( news ! = newstop & & news - > num < olds - > num ) )
{
//new ent is new this frame, so reset everything.
client - > pendingentities_bits [ news - > num ] = UF_RESET | UF_RESET2 ;
//don't need to calc the other bits here, resets are enough
news + + ;
}
else
{ //simple entity delta
//its flagged for removing, that's weird... must be some killer packetloss. turn that back into a reset or something
if ( client - > pendingentities_bits [ news - > num ] & UF_REMOVE )
client - > pendingentities_bits [ news - > num ] = ( client - > pendingentities_bits [ news - > num ] & ~ UF_REMOVE ) | UF_RESET2 ;
client - > pendingentities_bits [ news - > num ] | = MSGFTE_DeltaCalcBits ( & olds - > state , & news - > state ) ;
news + + ;
olds + + ;
}
}
//now we know what flags to apply, the client needs a copy of that state for the next frame too.
//outgoing data can just read off these states too, instead of needing to hit the edicts memory (which may be spread over multiple allocations, yay cache).
//to avoid a potentially large memcopy, I'm just going to swap these buffers.
olds = client - > previousentities ;
oldstop = olds + client - > maxpreviousentities ;
client - > previousentities = snapshot_entstate ;
client - > numpreviousentities = snapshot_numents ;
client - > maxpreviousentities = snapshot_maxents ;
snapshot_entstate = olds ;
snapshot_numents = 0 ;
snapshot_maxents = oldstop - olds ;
}
static void SVFTE_WriteEntitiesToClient ( client_t * client , sizebuf_t * msg , size_t overflowsize )
{
struct entity_num_state_s * state , * stateend ;
unsigned int bits , logbits ;
size_t entnum ;
int sequence = NET_QSocketGetSequenceOut ( client - > netconnection ) ;
size_t origmaxsize = msg - > maxsize ;
size_t rollbacksize ; //I'm too lazy to figure out sizes (especially if someone updates this for bone states or whatever)
struct deltaframe_s * frame = & client - > frames [ sequence & ( client - > numframes - 1 ) ] ;
frame - > sequence = sequence ; //so we know that it wasn't stale later.
2018-05-01 00:35:14 +00:00
frame - > timestamp = qcvm - > time ;
2017-09-17 02:12:53 +00:00
msg - > maxsize = overflowsize ;
state = client - > previousentities ;
stateend = state + client - > numpreviousentities ;
MSG_WriteByte ( msg , svcfte_updateentities ) ;
frame - > numents = 0 ;
if ( client - > protocol_pext2 & PEXT2_PREDINFO )
MSG_WriteShort ( msg , ( client - > lastmovemessage & 0xffff ) ) ;
2018-05-01 00:35:14 +00:00
MSG_WriteFloat ( msg , qcvm - > time ) ; //should be the time the last physics frame was run.
2017-09-17 02:12:53 +00:00
for ( entnum = client - > snapshotresume ; entnum < client - > numpendingentities ; entnum + + )
{
bits = client - > pendingentities_bits [ entnum ] ;
if ( ! ( bits & ~ UF_RESET2 ) )
continue ; //nothing to send (if reset2 is still set, then leave it pending until there's more data
rollbacksize = msg - > cursize ;
client - > pendingentities_bits [ entnum ] = 0 ;
logbits = 0 ;
if ( bits & UF_REMOVE )
{
if ( entnum > 0x3fff )
{
MSG_WriteShort ( msg , 0xc000 | ( entnum & 0x3fff ) ) ;
MSG_WriteShort ( msg , entnum > > 14 ) ;
}
else
MSG_WriteShort ( msg , 0x8000 | entnum ) ;
logbits = UF_REMOVE ;
}
else
{
while ( state < stateend & & state - > num < entnum )
state + + ;
if ( state < stateend & & state - > num = = entnum )
{
if ( bits & UF_RESET2 )
{
/*if reset2, then this is the second packet sent to the client and should have a forced reset (but which isn't tracked)*/
logbits = bits & ~ ( UF_RESET | UF_RESET2 ) ;
bits = UF_RESET | MSGFTE_DeltaCalcBits ( & EDICT_NUM ( entnum ) - > baseline , & state - > state ) ;
// Con_Printf("RESET2 %i @ %i\n", j, sequence);
}
else if ( bits & UF_RESET )
{
/*flag the entity for the next packet, so we always get two resets when it appears, to reduce the effects of packetloss on seeing rockets etc*/
client - > pendingentities_bits [ entnum ] = UF_RESET2 ;
bits = UF_RESET | MSGFTE_DeltaCalcBits ( & EDICT_NUM ( entnum ) - > baseline , & state - > state ) ;
logbits = UF_RESET ;
// Con_Printf("RESET %i @ %i\n", j, sequence);
}
else
logbits = bits ;
if ( entnum > = 0x4000 )
{
MSG_WriteShort ( msg , 0x4000 | ( entnum & 0x3fff ) ) ;
MSG_WriteShort ( msg , entnum > > 14 ) ;
}
else
MSG_WriteShort ( msg , entnum ) ;
// SV_EmitDeltaEntIndex(msg, j, false, true);
MSGFTE_WriteEntityUpdate ( bits , & state - > state , msg , client - > protocol_pext2 , sv . protocolflags ) ;
}
}
if ( ( size_t ) msg - > cursize + 2 > origmaxsize )
{
msg - > cursize = rollbacksize ; //roll back
client - > pendingentities_bits [ entnum ] | = logbits ; //make sure those bits get re-applied later.
break ;
}
if ( frame - > numents = = frame - > maxents )
{
frame - > maxents + = 64 ;
frame - > ents = realloc ( frame - > ents , sizeof ( * frame - > ents ) * frame - > maxents ) ;
}
frame - > ents [ frame - > numents ] . num = entnum ;
frame - > ents [ frame - > numents ] . bits = logbits ;
frame - > numents + + ;
}
msg - > maxsize = origmaxsize ;
MSG_WriteShort ( msg , 0 ) ; //eom
//remember how far we got, so we can keep things flushed, instead of only updating the first N entities.
client - > snapshotresume = ( entnum < client - > numpendingentities ? entnum : 0 ) ;
if ( msg - > cursize > 1024 & & dev_peakstats . packetsize < = 1024 )
Con_DWarning ( " %i byte packet exceeds standard limit of 1024. \n " , msg - > cursize ) ;
dev_stats . packetsize = msg - > cursize ;
dev_peakstats . packetsize = q_max ( msg - > cursize , dev_peakstats . packetsize ) ;
}
/*
SV_BuildEntityState
copies edict state into a more compact entity_state_t with all the extension fields etc sorted out and neatened up for network precision .
note : ignores viewmodelforclient and other client - specific stuff .
*/
void SV_BuildEntityState ( edict_t * ent , entity_state_t * state )
{
eval_t * val ;
state - > eflags = 0 ;
VectorCopy ( ent - > v . origin , state - > origin ) ;
VectorCopy ( ent - > v . angles , state - > angles ) ;
state - > modelindex = ent - > v . modelindex ;
state - > frame = ent - > v . frame ;
state - > colormap = ent - > v . colormap ;
state - > skin = ent - > v . skin ;
2018-05-01 00:35:14 +00:00
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . scale ) ) & & val - > _float )
2017-09-17 02:12:53 +00:00
state - > scale = val - > _float * 16 ;
else
state - > scale = 16 ;
2018-05-01 00:35:14 +00:00
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . alpha ) ) )
2017-09-17 02:12:53 +00:00
state - > alpha = ENTALPHA_ENCODE ( val - > _float ) ;
else
state - > alpha = ent - > alpha ;
2018-05-01 00:35:14 +00:00
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . colormod ) ) & & ( val - > vector [ 0 ] | | val - > vector [ 1 ] | | val - > vector [ 2 ] ) )
2017-09-17 02:12:53 +00:00
{
state - > colormod [ 0 ] = val - > vector [ 0 ] * 32 ;
state - > colormod [ 1 ] = val - > vector [ 1 ] * 32 ;
state - > colormod [ 2 ] = val - > vector [ 2 ] * 32 ;
}
else
state - > colormod [ 0 ] = state - > colormod [ 1 ] = state - > colormod [ 2 ] = 32 ;
2018-05-01 00:35:14 +00:00
state - > traileffectnum = GetEdictFieldValue ( ent , qcvm - > extfields . traileffectnum ) - > _float ;
state - > emiteffectnum = GetEdictFieldValue ( ent , qcvm - > extfields . emiteffectnum ) - > _float ;
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . tag_entity ) ) & & val - > edict )
2017-09-17 02:12:53 +00:00
state - > tagentity = NUM_FOR_EDICT ( PROG_TO_EDICT ( val - > edict ) ) ;
else
state - > tagentity = 0 ;
2018-05-01 00:35:14 +00:00
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . tag_index ) ) )
2017-09-17 02:12:53 +00:00
state - > tagindex = val - > _float ;
else
state - > tagindex = 0 ;
state - > effects = ent - > v . effects ;
2018-05-01 00:35:14 +00:00
if ( ( val = GetEdictFieldValue ( ent , qcvm - > extfields . modelflags ) ) )
2017-09-17 02:12:53 +00:00
state - > effects | = ( ( unsigned int ) val - > _float ) < < 24 ;
if ( ! ent - > v . movetype | | ent - > v . movetype = = MOVETYPE_STEP )
state - > eflags | = EFLAGS_STEP ;
state - > pmovetype = 0 ;
state - > velocity [ 0 ] = state - > velocity [ 1 ] = state - > velocity [ 2 ] = 0 ;
}
byte * SV_FatPVS ( vec3_t org , qmodel_t * worldmodel ) ;
static void SVFTE_BuildSnapshotForClient ( client_t * client )
{
unsigned int e , i ;
byte * pvs ;
vec3_t org ;
edict_t * ent , * parent ;
unsigned int maxentities = client - > limit_entities ;
edict_t * clent = client - > edict ;
eval_t * val ;
unsigned char eflags ;
int proged = EDICT_TO_PROG ( clent ) ;
struct entity_num_state_s * ents = snapshot_entstate ;
size_t numents = 0 ;
size_t maxents = snapshot_maxents ;
int emiteffect ;
// find the client's PVS
VectorAdd ( clent - > v . origin , clent - > v . view_ofs , org ) ;
2018-05-01 00:35:14 +00:00
pvs = SV_FatPVS ( org , qcvm - > worldmodel ) ;
2017-09-17 02:12:53 +00:00
2018-05-01 00:35:14 +00:00
if ( maxentities > ( unsigned int ) qcvm - > num_edicts )
maxentities = ( unsigned int ) qcvm - > num_edicts ;
2017-09-17 02:12:53 +00:00
// send over all entities (excpet the client) that touch the pvs
2018-05-01 00:35:14 +00:00
ent = NEXT_EDICT ( qcvm - > edicts ) ;
2017-09-17 02:12:53 +00:00
for ( e = 1 ; e < maxentities ; e + + , ent = NEXT_EDICT ( ent ) )
{
eflags = 0 ;
2018-05-01 00:35:14 +00:00
emiteffect = GetEdictFieldValue ( ent , qcvm - > extfields . emiteffectnum ) - > _float ;
2017-09-17 02:12:53 +00:00
if ( ent ! = clent ) // clent is ALLWAYS sent
{
// ignore ents without visible models
if ( ( ! ent - > v . modelindex | | ! PR_GetString ( ent - > v . model ) [ 0 ] ) & & ! emiteffect )
continue ;
2018-05-01 00:35:14 +00:00
val = GetEdictFieldValue ( ent , qcvm - > extfields . viewmodelforclient ) ;
2017-09-17 02:12:53 +00:00
if ( val & & val - > edict = = proged )
eflags | = EFLAGS_VIEWMODEL ;
else if ( val & & val - > edict )
continue ;
else
{
//attached entities should use the pvs of the parent rather than the child (because the child will typically be bugging out around '0 0 0', so won't be useful)
parent = ent ;
2018-05-01 00:35:14 +00:00
while ( ( val = GetEdictFieldValue ( parent , qcvm - > extfields . tag_entity ) ) & & val - > edict )
2017-09-17 02:12:53 +00:00
parent = PROG_TO_EDICT ( val - > edict ) ;
if ( parent - > num_leafs )
{
// ignore if not touching a PV leaf
for ( i = 0 ; i < parent - > num_leafs ; i + + )
if ( pvs [ parent - > leafnums [ i ] > > 3 ] & ( 1 < < ( parent - > leafnums [ i ] & 7 ) ) )
break ;
// ericw -- added ent->num_leafs < MAX_ENT_LEAFS condition.
//
// if ent->num_leafs == MAX_ENT_LEAFS, the ent is visible from too many leafs
// for us to say whether it's in the PVS, so don't try to vis cull it.
// this commonly happens with rotators, because they often have huge bboxes
// spanning the entire map, or really tall lifts, etc.
if ( i = = parent - > num_leafs & & parent - > num_leafs < MAX_ENT_LEAFS )
continue ; // not visible
}
}
}
//okay, we care about this entity.
if ( numents = = maxents )
{
maxents + = 64 ;
ents = realloc ( ents , maxents * sizeof ( * ents ) ) ;
}
ents [ numents ] . num = e ;
SV_BuildEntityState ( ent , & ents [ numents ] . state ) ;
if ( ( unsigned int ) ents [ numents ] . state . modelindex > = client - > limit_models )
ents [ numents ] . state . modelindex = 0 ;
if ( ent = = clent ) //add velocity, but we only care for the local player (should add prediction for other entities some time too).
{
ents [ numents ] . state . pmovetype = 0 ; //ent->v.movetype; //fixme: we don't do prediction, so don't tell the client that it can try
if ( ( int ) ent - > v . flags & FL_ONGROUND )
eflags | = EFLAGS_ONGROUND ;
ents [ numents ] . state . velocity [ 0 ] = ent - > v . velocity [ 0 ] * 8 ;
ents [ numents ] . state . velocity [ 1 ] = ent - > v . velocity [ 1 ] * 8 ;
ents [ numents ] . state . velocity [ 2 ] = ent - > v . velocity [ 2 ] * 8 ;
}
else if ( ent - > alpha = = ENTALPHA_ZERO & & ! ent - > v . effects ) //don't send invisible entities unless they have effects
continue ;
// val = GetEdictFieldValue(ent, pr_extfields.exteriormodelforclient);
// if (val && val->edict == proged)
// eflags |= EFLAGS_EXTERIORMODEL;
//EFLAGS_VIEWMODEL was handled above
ents [ numents ] . state . eflags | = eflags ;
numents + + ;
}
snapshot_entstate = ents ;
snapshot_numents = numents ;
snapshot_maxents = maxents ;
}
void MSG_WriteStaticOrBaseLine ( sizebuf_t * buf , int idx , entity_state_t * state , unsigned int protocol_pext2 , unsigned int protocol , unsigned int protocolflags )
{
int i ;
if ( protocol_pext2 & PEXT2_REPLACEMENTDELTAS )
{
if ( idx > = 0 )
{
MSG_WriteByte ( buf , svcfte_spawnbaseline2 ) ;
MSG_WriteShort ( buf , idx ) ;
}
else
MSG_WriteByte ( buf , svcfte_spawnstatic2 ) ;
MSGFTE_WriteEntityUpdate ( MSGFTE_DeltaCalcBits ( & nullentitystate , state ) , state , buf , protocol_pext2 , protocolflags ) ;
}
else
{
int bits = 0 ;
if ( protocol = = PROTOCOL_VERSION_DP7 )
{
if ( ( state - > modelindex & 0xFF00 ) | | ( state - > frame & 0xFF00 ) )
bits = B_LARGEMODEL | B_LARGEFRAME ;
//no alpha etc, no idea how that's meant to work with static ents.
if ( idx > = 0 )
{
MSG_WriteByte ( buf , bits ? svcdp_spawnbaseline2 : svc_spawnbaseline ) ;
2018-05-01 00:35:14 +00:00
MSG_WriteEntity ( buf , idx , protocol_pext2 ) ;
2017-09-17 02:12:53 +00:00
}
else
MSG_WriteByte ( buf , bits ? svcdp_spawnstatic2 : svc_spawnstatic ) ;
}
else
{
if ( protocol = = PROTOCOL_FITZQUAKE ) //still want to send baseline in PROTOCOL_NETQUAKE, so reset these values
{
if ( state - > modelindex & 0xFF00 )
bits | = B_LARGEMODEL ;
if ( state - > frame & 0xFF00 )
bits | = B_LARGEFRAME ;
if ( state - > alpha ! = ENTALPHA_DEFAULT )
bits | = B_ALPHA ;
}
if ( idx > = 0 )
{
MSG_WriteByte ( buf , bits ? svc_spawnbaseline2 : svc_spawnbaseline ) ;
2018-05-01 00:35:14 +00:00
MSG_WriteEntity ( buf , idx , protocol_pext2 ) ;
2017-09-17 02:12:53 +00:00
}
else
MSG_WriteByte ( buf , bits ? svc_spawnstatic2 : svc_spawnstatic ) ;
if ( bits )
MSG_WriteByte ( buf , bits ) ;
}
if ( ( bits & B_LARGEMODEL ) | | protocol = = PROTOCOL_VERSION_BJP3 )
MSG_WriteShort ( buf , state - > modelindex ) ;
else
MSG_WriteByte ( buf , state - > modelindex ) ;
if ( bits & B_LARGEFRAME )
MSG_WriteShort ( buf , state - > frame ) ;
else
MSG_WriteByte ( buf , state - > frame ) ;
MSG_WriteByte ( buf , state - > colormap ) ;
MSG_WriteByte ( buf , state - > skin ) ;
for ( i = 0 ; i < 3 ; i + + )
{
MSG_WriteCoord ( buf , state - > origin [ i ] , protocolflags ) ;
MSG_WriteAngle ( buf , state - > angles [ i ] , protocolflags ) ;
}
if ( bits & B_ALPHA )
MSG_WriteByte ( buf , state - > alpha ) ;
}
}
static void SV_Pext_f ( void ) ;
2010-02-15 23:26:55 +00:00
/*
= = = = = = = = = = = = = = =
SV_Protocol_f
= = = = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
static void SV_Protocol_f ( void )
2010-02-15 23:26:55 +00:00
{
int i ;
2017-09-17 02:12:53 +00:00
const char * s ;
int prot , pext2 ;
prot = sv_protocol ;
pext2 = sv_protocol_pext2 ;
2010-02-15 23:26:55 +00:00
switch ( Cmd_Argc ( ) )
{
case 1 :
2017-09-17 02:12:53 +00:00
//"FTE+15" or "15", just to be explicit about it
Con_Printf ( " \" sv_protocol \" is \" %s%i \" \n " , sv_protocol_pext2 ? " fte " : " " , sv_protocol ) ;
2010-02-15 23:26:55 +00:00
break ;
case 2 :
2017-09-17 02:12:53 +00:00
s = Cmd_Argv ( 1 ) ;
if ( ! q_strncasecmp ( s , " FTE " , 3 ) )
{
s + = 3 ;
if ( * s = = ' + ' | | * s = = ' - ' )
s + + ;
pext2 = PEXT2_SUPPORTED_SERVER ;
}
else if ( ! q_strncasecmp ( s , " + " , 3 ) )
{
s + = 1 ;
pext2 = PEXT2_SUPPORTED_SERVER ;
}
else if ( ! q_strncasecmp ( s , " Base " , 4 ) )
{
s + = 4 ;
if ( * s = = ' + ' | | * s = = ' - ' )
s + + ;
pext2 = 0 ;
}
else if ( * s = = ' - ' )
{
s + + ;
pext2 = 0 ;
}
i = strtol ( s , ( char * * ) & s , 0 ) ;
if ( * s = = ' - ' )
pext2 = 0 ;
else if ( * s = = ' + ' )
pext2 = PEXT2_SUPPORTED_SERVER ;
if ( i ! = PROTOCOL_NETQUAKE & & i ! = PROTOCOL_FITZQUAKE & & i ! = PROTOCOL_RMQ & & i ! = PROTOCOL_VERSION_BJP3 )
Con_Printf ( " sv_protocol must be %i or %i or %i or %i. \n Protocol may be prefixed with FTE+ or Base- to enable/disable FTE extensions. \n " , PROTOCOL_NETQUAKE , PROTOCOL_FITZQUAKE , PROTOCOL_RMQ , PROTOCOL_VERSION_BJP3 ) ;
2010-02-15 23:26:55 +00:00
else
{
sv_protocol = i ;
2017-09-17 02:12:53 +00:00
sv_protocol_pext2 = pext2 ;
2010-02-15 23:26:55 +00:00
if ( sv . active )
2017-09-17 02:12:53 +00:00
{
if ( prot = = sv_protocol & & pext2 = = sv_protocol_pext2 )
Con_Printf ( " specified protocol already active. \n " ) ;
else
Con_Printf ( " changes will not take effect until the next level load. \n " ) ;
}
2010-02-15 23:26:55 +00:00
}
break ;
default :
Con_SafePrintf ( " usage: sv_protocol <protocol> \n " ) ;
break ;
}
}
2011-06-28 11:33:23 +00:00
2010-02-15 23:26:55 +00:00
/*
= = = = = = = = = = = = = = =
SV_Init
= = = = = = = = = = = = = = =
*/
void SV_Init ( void )
{
int i ;
2016-06-24 06:15:47 +00:00
const char * p ;
2010-02-15 23:26:55 +00:00
extern cvar_t sv_maxvelocity ;
extern cvar_t sv_gravity ;
extern cvar_t sv_nostep ;
2011-06-10 03:04:51 +00:00
extern cvar_t sv_freezenonclients ;
2017-09-17 02:12:53 +00:00
extern cvar_t sv_gameplayfix_spawnbeforethinks ;
2018-05-01 00:35:14 +00:00
extern cvar_t sv_gameplayfix_setmodelrealbox ; //spike: 1 to replicate a quakespasm bug, 0 for actual vanilla compat.
2010-02-15 23:26:55 +00:00
extern cvar_t sv_friction ;
extern cvar_t sv_edgefriction ;
extern cvar_t sv_stopspeed ;
extern cvar_t sv_maxspeed ;
extern cvar_t sv_accelerate ;
extern cvar_t sv_idealpitchscale ;
extern cvar_t sv_aim ;
extern cvar_t sv_altnoclip ; //johnfitz
2017-09-17 02:12:53 +00:00
extern cvar_t sv_public ; //spike
extern cvar_t sv_reportheartbeats ; //spike
extern cvar_t com_protocolname ; //spike
extern cvar_t net_masters [ ] ; //spike
extern cvar_t rcon_password ; //spike, proquake-compatible rcon
2011-12-28 22:01:33 +00:00
Cvar_RegisterVariable ( & sv_maxvelocity ) ;
Cvar_RegisterVariable ( & sv_gravity ) ;
Cvar_RegisterVariable ( & sv_friction ) ;
Cvar_SetCallback ( & sv_gravity , Host_Callback_Notify ) ;
Cvar_SetCallback ( & sv_friction , Host_Callback_Notify ) ;
Cvar_RegisterVariable ( & sv_edgefriction ) ;
Cvar_RegisterVariable ( & sv_stopspeed ) ;
Cvar_RegisterVariable ( & sv_maxspeed ) ;
Cvar_SetCallback ( & sv_maxspeed , Host_Callback_Notify ) ;
Cvar_RegisterVariable ( & sv_accelerate ) ;
Cvar_RegisterVariable ( & sv_idealpitchscale ) ;
Cvar_RegisterVariable ( & sv_aim ) ;
Cvar_RegisterVariable ( & sv_nostep ) ;
Cvar_RegisterVariable ( & sv_freezenonclients ) ;
2017-09-17 02:12:53 +00:00
Cvar_RegisterVariable ( & sv_gameplayfix_spawnbeforethinks ) ;
2018-05-01 00:35:14 +00:00
Cvar_RegisterVariable ( & sv_gameplayfix_setmodelrealbox ) ;
2017-09-17 02:12:53 +00:00
Cvar_RegisterVariable ( & pr_checkextension ) ;
2011-12-28 22:01:33 +00:00
Cvar_RegisterVariable ( & sv_altnoclip ) ; //johnfitz
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
if ( isDedicated )
sv_public . string = " 1 " ;
else
sv_public . string = " 0 " ;
Cvar_RegisterVariable ( & sv_public ) ;
Cvar_RegisterVariable ( & sv_reportheartbeats ) ;
Cvar_RegisterVariable ( & com_protocolname ) ;
for ( i = 0 ; net_masters [ i ] . name ; i + + )
Cvar_RegisterVariable ( & net_masters [ i ] ) ;
Cvar_RegisterVariable ( & rcon_password ) ;
Cmd_AddCommand_ClientCommand ( " pext " , SV_Pext_f ) ;
2010-02-15 23:26:55 +00:00
Cmd_AddCommand ( " sv_protocol " , & SV_Protocol_f ) ; //johnfitz
for ( i = 0 ; i < MAX_MODELS ; i + + )
sprintf ( localmodels [ i ] , " *%i " , i ) ;
2016-06-24 06:15:47 +00:00
2017-09-17 02:12:53 +00:00
sv_protocol_pext2 = PEXT2_SUPPORTED_SERVER ;
2016-06-24 06:15:47 +00:00
i = COM_CheckParm ( " -protocol " ) ;
if ( i & & i < com_argc - 1 )
sv_protocol = atoi ( com_argv [ i + 1 ] ) ;
switch ( sv_protocol )
{
case PROTOCOL_NETQUAKE :
p = " NetQuake " ;
break ;
case PROTOCOL_FITZQUAKE :
p = " FitzQuake " ;
break ;
case PROTOCOL_RMQ :
p = " RMQ " ;
break ;
default :
Sys_Error ( " Bad protocol version request %i. Accepted values: %i, %i, %i. " ,
sv_protocol , PROTOCOL_NETQUAKE , PROTOCOL_FITZQUAKE , PROTOCOL_RMQ ) ;
2017-07-28 20:00:56 +00:00
return ; /* silence compiler */
2016-06-24 06:15:47 +00:00
}
2017-09-17 02:12:53 +00:00
Sys_Printf ( " Server using protocol %i%s (%s%s) \n " , sv_protocol , sv_protocol_pext2 ? " + " : " " , sv_protocol_pext2 ? " FTE- " : " " , p ) ;
SV_VoiceInit ( ) ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
EVENT MESSAGES
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = = = =
SV_StartParticle
Make sure the event gets sent to all clients
= = = = = = = = = = = = = = = = = =
*/
void SV_StartParticle ( vec3_t org , vec3_t dir , int color , int count )
{
int i , v ;
if ( sv . datagram . cursize > MAX_DATAGRAM - 16 )
return ;
MSG_WriteByte ( & sv . datagram , svc_particle ) ;
2016-06-24 06:15:41 +00:00
MSG_WriteCoord ( & sv . datagram , org [ 0 ] , sv . protocolflags ) ;
MSG_WriteCoord ( & sv . datagram , org [ 1 ] , sv . protocolflags ) ;
MSG_WriteCoord ( & sv . datagram , org [ 2 ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
for ( i = 0 ; i < 3 ; i + + )
{
v = dir [ i ] * 16 ;
if ( v > 127 )
v = 127 ;
else if ( v < - 128 )
v = - 128 ;
MSG_WriteChar ( & sv . datagram , v ) ;
}
MSG_WriteByte ( & sv . datagram , count ) ;
MSG_WriteByte ( & sv . datagram , color ) ;
}
/*
= = = = = = = = = = = = = = = = = =
SV_StartSound
Each entity can have eight independant sound sources , like voice ,
weapon , feet , etc .
Channel 0 is an auto - allocate channel , the others override anything
allready running on that entity / channel pair .
An attenuation of 0 will play full volume everywhere in the level .
Larger attenuations will drop off . ( max 4 attenuation )
= = = = = = = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
void SV_StartSound ( edict_t * entity , float * origin , int channel , const char * sample , int volume , float attenuation )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
unsigned int sound_num , ent ;
2010-08-29 02:22:55 +00:00
int i , field_mask ;
2017-09-17 02:12:53 +00:00
int p ;
client_t * cl ;
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
if ( volume < 0 )
2012-11-15 17:30:43 +00:00
Host_Error ( " SV_StartSound: volume = %i " , volume ) ;
2017-09-17 02:12:53 +00:00
else if ( volume > 255 )
{
volume = 255 ;
Con_Printf ( " SV_StartSound: volume = %i \n " , volume ) ;
}
2010-02-15 23:26:55 +00:00
if ( attenuation < 0 | | attenuation > 4 )
2012-11-15 17:30:43 +00:00
Host_Error ( " SV_StartSound: attenuation = %f " , attenuation ) ;
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
if ( channel < 0 | | channel > 255 )
2012-11-15 17:30:43 +00:00
Host_Error ( " SV_StartSound: channel = %i " , channel ) ;
2017-09-17 02:12:53 +00:00
else if ( channel > 7 )
Con_DPrintf ( " SV_StartSound: channel = %i \n " , channel ) ;
2010-02-15 23:26:55 +00:00
if ( sv . datagram . cursize > MAX_DATAGRAM - 16 )
return ;
// find precache number for sound
2010-08-29 02:22:55 +00:00
for ( sound_num = 1 ; sound_num < MAX_SOUNDS & & sv . sound_precache [ sound_num ] ; sound_num + + )
{
if ( ! strcmp ( sample , sv . sound_precache [ sound_num ] ) )
break ;
}
2010-02-15 23:26:55 +00:00
2010-08-29 02:22:55 +00:00
if ( sound_num = = MAX_SOUNDS | | ! sv . sound_precache [ sound_num ] )
{
Con_Printf ( " SV_StartSound: %s not precacheed \n " , sample ) ;
return ;
}
2010-02-15 23:26:55 +00:00
ent = NUM_FOR_EDICT ( entity ) ;
field_mask = 0 ;
if ( volume ! = DEFAULT_SOUND_PACKET_VOLUME )
field_mask | = SND_VOLUME ;
if ( attenuation ! = DEFAULT_SOUND_PACKET_ATTENUATION )
field_mask | = SND_ATTENUATION ;
//johnfitz -- PROTOCOL_FITZQUAKE
2017-09-17 02:12:53 +00:00
if ( ent > = 8192 | | channel > = 8 )
field_mask | = SND_LARGEENTITY ;
if ( sound_num > = 256 )
field_mask | = SND_LARGESOUND ;
//johnfitz
for ( p = 0 ; p < svs . maxclients ; p + + )
chase.c, cl_input.c, cl_parse.c, client.h, common.c, common.h, console.h,
cvar.h, draw.h, gl_draw.c, gl_fog.c, gl_mesh.c, gl_model.c, gl_model.h,
gl_rmain.c, gl_rmisc.c, gl_screen.c, gl_sky.c, gl_texmgr.c, glquake.h,
host.c, keys.c, keys.h, main.c, menu.c, menu.h, pr_cmds.c, quakedef.h,
r_alias.c, r_brush.c, r_part.c, r_sprite.c, r_world.c, sbar.c, sbar.h,
screen.h, snd_dma.c, snd_mem.c, snd_mix.c, sv_main.c, sys_sdl.c, vid.h,
view.h, world.c, world.h: Loads of warning fixes about missing function
prototypes, missing parens around &, missing braces leading to ambiguous
else statements and unused and uninitialized variables. There are still a
couple of unitialised variables here and there, but not much. The warnings
about strict aliasing violations need taking care of.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@21 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-16 12:01:07 +00:00
{
2017-09-17 02:12:53 +00:00
cl = & svs . clients [ p ] ;
if ( ! cl - > active | | ! cl - > spawned )
continue ;
if ( ent > = cl - > limit_entities )
continue ;
if ( sound_num > = cl - > limit_sounds )
continue ;
if ( ( field_mask & ( SND_LARGEENTITY | SND_LARGESOUND ) ) & & ( ! cl - > protocol_pext2 | | sv . protocol = = PROTOCOL_NETQUAKE ) )
continue ;
// directed messages go only to the entity the are targeted on
MSG_WriteByte ( & cl - > datagram , svc_sound ) ;
MSG_WriteByte ( & cl - > datagram , field_mask ) ;
if ( field_mask & SND_VOLUME )
MSG_WriteByte ( & cl - > datagram , volume ) ;
if ( field_mask & SND_ATTENUATION )
MSG_WriteByte ( & cl - > datagram , attenuation * 64 ) ;
//johnfitz -- PROTOCOL_FITZQUAKE
if ( field_mask & SND_LARGEENTITY )
{
if ( ( cl - > protocol_pext2 & PEXT2_REPLACEMENTDELTAS ) & & ent > 0x7fff )
{
MSG_WriteShort ( & cl - > datagram , ( ent > > 8 ) | 0x8000 ) ;
MSG_WriteByte ( & cl - > datagram , ent & 0xff ) ;
}
else
MSG_WriteShort ( & cl - > datagram , ent ) ;
MSG_WriteByte ( & cl - > datagram , channel ) ;
}
2010-02-15 23:26:55 +00:00
else
2017-09-17 02:12:53 +00:00
MSG_WriteShort ( & cl - > datagram , ( ent < < 3 ) | channel ) ;
if ( ( field_mask & SND_LARGESOUND ) | | sv . protocol = = PROTOCOL_VERSION_BJP3 )
MSG_WriteShort ( & cl - > datagram , sound_num ) ;
2010-02-15 23:26:55 +00:00
else
2017-09-17 02:12:53 +00:00
MSG_WriteByte ( & cl - > datagram , sound_num ) ;
//johnfitz
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
for ( i = 0 ; i < 3 ; i + + )
{
if ( origin )
MSG_WriteCoord ( & cl - > datagram , origin [ i ] , sv . protocolflags ) ;
else
MSG_WriteCoord ( & cl - > datagram , entity - > v . origin [ i ] + 0.5 * ( entity - > v . mins [ i ] + entity - > v . maxs [ i ] ) , sv . protocolflags ) ;
}
2010-02-15 23:26:55 +00:00
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
CLIENT SPAWNING
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = =
SV_SendServerinfo
Sends the first message from the server to a connected client .
This will be sent on the initial connection and upon each server load .
= = = = = = = = = = = = = = = =
*/
void SV_SendServerinfo ( client_t * client )
{
2010-08-29 02:22:55 +00:00
const char * * s ;
2010-02-15 23:26:55 +00:00
char message [ 2048 ] ;
2017-09-17 02:12:53 +00:00
unsigned int i ; //johnfitz
qboolean cantruncate ;
SV_VoiceInitClient ( client ) ;
client - > spawned = false ; // need prespawn, spawn, etc
//assume some safe defaults if we early out.
client - > limit_unreliable = 1024 ;
client - > limit_reliable = 8192 ;
client - > limit_entities = 0 ;
client - > limit_models = 0 ;
client - > limit_sounds = 0 ;
if ( ! sv_protocol_pext2 )
{ //server disabled pext completely, don't bother trying.
//make sure we try reenabling it again on the next map though. mwahaha.
client - > pextknown = false ;
}
else if ( ! client - > pextknown )
{
MSG_WriteByte ( & client - > message , svc_stufftext ) ;
MSG_WriteString ( & client - > message , " cmd pext \n " ) ;
client - > sendsignon = true ;
return ;
}
client - > protocol_pext2 & = sv_protocol_pext2 ;
if ( ! ( client - > protocol_pext2 & PEXT2_REPLACEMENTDELTAS ) )
client - > protocol_pext2 & = ~ PEXT2_PREDINFO ; //stats can't be deltaed if there's no deltas, so just pretend its not supported on its own.
client - > limit_entities = ( sv_protocol_pext2 & & NET_QSocketGetProQuakeAngleHack ( client - > netconnection ) ) ? 2048 : 600 ; //vanilla sucks. proquake supports more so assume we can use that limit if angles are also available (but only if we're allowing other non-vanilla extensions)
client - > limit_models = 256 ; //single byte
client - > limit_sounds = 256 ; //single byte
//now we know their protocol, pick some real defaults
if ( sv . protocol ! = PROTOCOL_NETQUAKE | | client - > protocol_pext2 )
{
client - > limit_unreliable = DATAGRAM_MTU ; //some safe ethernet limit. these clients should accept pretty much anything, but any routers will not.
client - > limit_reliable = MAX_DATAGRAM ; //quite large, ip allows 16 bits
2018-05-01 00:35:14 +00:00
client - > limit_entities = qcvm - > max_edicts ; //we don't really know, 8k is probably a save guess but could be 32k, 65k, or even more...
2017-09-17 02:12:53 +00:00
client - > limit_models = MAX_MODELS ; //not really sure, client's problem until >14bits
client - > limit_sounds = MAX_SOUNDS ; //not really sure, client's problem until >14bits
if ( ! Q_strcmp ( NET_QSocketGetTrueAddressString ( client - > netconnection ) , " LOCAL " ) )
{ //override some other limits for localhost, because we can probably get away with it.
//only do this if we're using extensions, so we don't break demos
client - > limit_unreliable = client - > limit_reliable = MAX_DATAGRAM ;
}
}
if ( client - > limit_entities > 0x8000 & & ! ( client - > protocol_pext2 & PEXT2_REPLACEMENTDELTAS ) )
client - > limit_entities = 0x8000 ; //pext2 changes the encoding of entities to support 23 bits instead of dpp7's 15bits or vanilla's 16bits, but our writeentity is lazy.
//unfortunately we can't split this up, so if its oversized, we'll just let the client complain instead of always kicking them
client - > message . maxsize = sizeof ( client - > msgbuf ) ;
if ( client - > message . maxsize > ( int ) client - > limit_reliable )
client - > message . maxsize = client - > limit_reliable ;
NET_QSocketSetMSS ( client - > netconnection , client - > limit_unreliable ) ;
if ( client - > message . cursize )
{ //try and flush the reliable NOW, in case the qc is evil
if ( NET_CanSendMessage ( host_client - > netconnection ) )
{
if ( NET_SendMessage ( host_client - > netconnection , & host_client - > message ) ! = - 1 )
{
SZ_Clear ( & host_client - > message ) ;
host_client - > last_message = realtime ;
}
}
}
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
cantruncate = client - > message . cursize = = 0 ;
retry :
2010-02-15 23:26:55 +00:00
MSG_WriteByte ( & client - > message , svc_print ) ;
2017-09-17 02:12:53 +00:00
// sprintf (message, "%c\nFITZQUAKE %1.2f SERVER (%i CRC)\n", 2, FITZQUAKE_VERSION, pr_crc); //johnfitz -- include fitzquake version
2018-05-01 00:35:14 +00:00
sprintf ( message , " %c \n " ENGINE_NAME_AND_VER " Server (%i CRC) \n " , 2 , qcvm - > crc ) ; //spike -- quakespasm has moved on, and has its own server capabilities now. Advertising = good, right?
2010-02-15 23:26:55 +00:00
MSG_WriteString ( & client - > message , message ) ;
2017-09-17 02:12:53 +00:00
// lack of serverinfo means any csqc info might as well be sent the lame dp way
/* if (sv.csqc_progsize)
{
MSG_WriteByte ( & client - > netconnection - > message , svc_stufftext ) ;
MSG_WriteString ( & client - > netconnection - > message , va ( " csqc_progname %s \n " , sv . csqc_progname ) ) ;
MSG_WriteByte ( & client - > netconnection - > message , svc_stufftext ) ;
MSG_WriteString ( & client - > netconnection - > message , va ( " csqc_progsize %i \n " , sv . csqc_progsize ) ) ;
MSG_WriteByte ( & client - > netconnection - > message , svc_stufftext ) ;
MSG_WriteString ( & client - > netconnection - > message , va ( " csqc_progcrc %i \n " , sv . csqc_progcrc ) ) ;
} */
//let clients know that we support downloads
//if (client->protocol_pext2 || sv.protocol == PROTOCOL_VERSION_DP7)
{
MSG_WriteByte ( & client - > message , svc_stufftext ) ;
MSG_WriteString ( & client - > message , " cl_serverextension_download 1 \n " ) ;
}
2010-02-15 23:26:55 +00:00
MSG_WriteByte ( & client - > message , svc_serverinfo ) ;
2017-09-17 02:12:53 +00:00
if ( client - > protocol_pext2 )
{ //pext stuff takes the form of modifiers to an underlaying protocol
MSG_WriteLong ( & client - > message , PROTOCOL_FTE_PEXT2 ) ;
MSG_WriteLong ( & client - > message , client - > protocol_pext2 ) ; //active extensions that the client needs to look out for
}
2010-02-15 23:26:55 +00:00
MSG_WriteLong ( & client - > message , sv . protocol ) ; //johnfitz -- sv.protocol instead of PROTOCOL_VERSION
2016-06-24 06:15:41 +00:00
if ( sv . protocol = = PROTOCOL_RMQ )
{
// mh - now send protocol flags so that the client knows the protocol features to expect
MSG_WriteLong ( & client - > message , sv . protocolflags ) ;
}
2017-09-17 02:12:53 +00:00
if ( client - > protocol_pext2 & PEXT2_PREDINFO )
{
//if multiple gamedirs were used, we should list all the active ones eg: "id1;hipnotic;rogue;quoth;mod".
//fixme: engine-specific forced gamedirs like id1/ or qw/ or fte/ are redundant, so don't bother listing them
//we don't really track that stuff, so I'm just going to report the last one
MSG_WriteString ( & client - > message , COM_GetGameNames ( false ) ) ;
}
2010-02-15 23:26:55 +00:00
MSG_WriteByte ( & client - > message , svs . maxclients ) ;
if ( ! coop . value & & deathmatch . value )
MSG_WriteByte ( & client - > message , GAME_DEATHMATCH ) ;
else
MSG_WriteByte ( & client - > message , GAME_COOP ) ;
2018-05-01 00:35:14 +00:00
MSG_WriteString ( & client - > message , PR_GetString ( qcvm - > edicts - > v . message ) ) ;
2010-02-15 23:26:55 +00:00
//johnfitz -- only send the first 256 model and sound precaches if protocol is 15
2017-09-17 02:12:53 +00:00
for ( i = 1 , s = sv . model_precache + 1 ; * s & & i < client - > limit_models ; s + + , i + + )
MSG_WriteString ( & client - > message , * s ) ;
2010-02-15 23:26:55 +00:00
MSG_WriteByte ( & client - > message , 0 ) ;
2017-09-17 02:12:53 +00:00
for ( i = 1 , s = sv . sound_precache + 1 ; * s & & i < client - > limit_sounds ; s + + , i + + )
MSG_WriteString ( & client - > message , * s ) ;
2010-02-15 23:26:55 +00:00
MSG_WriteByte ( & client - > message , 0 ) ;
//johnfitz
// send music
MSG_WriteByte ( & client - > message , svc_cdtrack ) ;
2018-05-01 00:35:14 +00:00
MSG_WriteByte ( & client - > message , qcvm - > edicts - > v . sounds ) ;
MSG_WriteByte ( & client - > message , qcvm - > edicts - > v . sounds ) ;
2010-02-15 23:26:55 +00:00
// set view
MSG_WriteByte ( & client - > message , svc_setview ) ;
MSG_WriteShort ( & client - > message , NUM_FOR_EDICT ( client - > edict ) ) ;
MSG_WriteByte ( & client - > message , svc_signonnum ) ;
MSG_WriteByte ( & client - > message , 1 ) ;
client - > sendsignon = true ;
2017-09-17 02:12:53 +00:00
SVFTE_SetupFrames ( client ) ;
if ( client - > message . overflowed & & client - > limit_sounds > 64 & & cantruncate )
{
if ( client - > limit_models > client - > limit_sounds )
client - > limit_models / = 2 ;
else
client - > limit_sounds / = 2 ;
SZ_Clear ( & client - > message ) ;
Con_Printf ( " Serverinfo too large for %s, truncating. \n " , NET_QSocketGetTrueAddressString ( client - > netconnection ) ) ;
goto retry ;
}
//try and flush the reliable NOW, in case the qc is evil
if ( NET_CanSendMessage ( client - > netconnection ) )
{
if ( NET_SendMessage ( client - > netconnection , & client - > message ) ! = - 1 )
{
SZ_Clear ( & client - > message ) ;
client - > last_message = realtime ;
client - > sendsignon = false ;
}
}
//protocol 15 is too limited. let people know that they'll get a crappy experience.
2018-05-01 00:35:14 +00:00
if ( client - > limit_entities < = ( unsigned ) qcvm - > num_edicts )
2017-09-17 02:12:53 +00:00
{
Con_Warning ( " Protocol limitation (entities) for %s \n " , NET_QSocketGetTrueAddressString ( client - > netconnection ) ) ;
MSG_WriteByte ( & client - > message , svc_print ) ;
MSG_WriteByte ( & client - > message , 2 ) ;
MSG_WriteString ( & client - > message , " WARNING: " ) ;
MSG_WriteByte ( & client - > message , svc_print ) ;
MSG_WriteString ( & client - > message , " The protocol in use is too limited. You will not be able to see all entities \n " ) ;
}
if ( client - > limit_models < MAX_MODELS & & sv . model_precache [ client - > limit_models ] )
{
Con_Warning ( " Protocol limitation (models) for %s \n " , NET_QSocketGetTrueAddressString ( client - > netconnection ) ) ;
MSG_WriteByte ( & client - > message , svc_print ) ;
MSG_WriteByte ( & client - > message , 2 ) ;
MSG_WriteString ( & client - > message , " WARNING: " ) ;
MSG_WriteByte ( & client - > message , svc_print ) ;
MSG_WriteString ( & client - > message , " The protocol in use is too limited. You will not be able to see all models \n " ) ;
}
if ( client - > limit_sounds < MAX_SOUNDS & & sv . sound_precache [ client - > limit_sounds ] )
{
Con_Warning ( " Protocol limitation (sounds) for %s \n " , NET_QSocketGetTrueAddressString ( client - > netconnection ) ) ;
MSG_WriteByte ( & client - > message , svc_print ) ;
MSG_WriteByte ( & client - > message , 2 ) ;
MSG_WriteString ( & client - > message , " WARNING: " ) ;
MSG_WriteByte ( & client - > message , svc_print ) ;
MSG_WriteString ( & client - > message , " The protocol in use is too limited. You will not be able to hear all sounds \n " ) ;
}
}
void SV_Pext_f ( void )
{
//this only makes sense on the server. the clientside part only takes the form of 'cmd pext', for compat with clients that don't support this.
if ( cmd_source = = src_command )
{
if ( ! cls . state )
{
Con_Printf ( " Not connected \n " ) ;
return ;
}
Con_Printf ( " Current Protocols: \n " ) ;
if ( cl . protocol_pext2 & PEXT2_REPLACEMENTDELTAS )
Con_Printf ( " Replacement Entity Deltas \n " ) ;
if ( cl . protocol_pext2 & PEXT2_PREDINFO )
Con_Printf ( " Replacement Stats ('predinfo') \n " ) ;
if ( cl . protocol = = PROTOCOL_NETQUAKE )
Con_Printf ( " vanilla(15) \n " ) ;
else if ( cl . protocol = = PROTOCOL_FITZQUAKE )
Con_Printf ( " fitzquake(666) \n " ) ;
else if ( cl . protocol = = PROTOCOL_RMQ )
Con_Printf ( " rmq(999) \n " ) ;
else
Con_Printf ( " unknown protocol(%i) \n " , cl . protocol ) ;
return ;
}
if ( ! host_client - > pextknown & & ! host_client - > spawned )
{
int i ;
int key ;
int value ;
for ( i = 1 ; i < Cmd_Argc ( ) ; i + = 2 )
{
key = strtoul ( Cmd_Argv ( i ) , NULL , 0 ) ;
value = strtoul ( Cmd_Argv ( i + 1 ) , NULL , 0 ) ;
if ( key = = PROTOCOL_FTE_PEXT2 )
host_client - > protocol_pext2 = value & PEXT2_SUPPORTED_SERVER ;
//else some other extension that we don't know
}
host_client - > pextknown = true ;
SV_SendServerinfo ( host_client ) ;
}
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = =
SV_ConnectClient
Initializes a client_t for a new net connection . This will only be called
once for a player each game , not once for each level change .
= = = = = = = = = = = = = = = =
*/
void SV_ConnectClient ( int clientnum )
{
edict_t * ent ;
client_t * client ;
int edictnum ;
struct qsocket_s * netconnection ;
int i ;
float spawn_parms [ NUM_SPAWN_PARMS ] ;
client = svs . clients + clientnum ;
2017-09-17 02:12:53 +00:00
if ( client - > netconnection )
Con_DPrintf ( " Client %s connected \n " , NET_QSocketGetTrueAddressString ( client - > netconnection ) ) ;
else
Con_DPrintf ( " Bot connected \n " ) ;
2010-02-15 23:26:55 +00:00
edictnum = clientnum + 1 ;
ent = EDICT_NUM ( edictnum ) ;
// set up the client_t
netconnection = client - > netconnection ;
2017-09-17 02:12:53 +00:00
net_activeconnections + + ;
2010-02-15 23:26:55 +00:00
if ( sv . loadgame )
memcpy ( spawn_parms , client - > spawn_parms , sizeof ( spawn_parms ) ) ;
memset ( client , 0 , sizeof ( * client ) ) ;
client - > netconnection = netconnection ;
strcpy ( client - > name , " unconnected " ) ;
client - > active = true ;
client - > spawned = false ;
client - > edict = ent ;
client - > message . data = client - > msgbuf ;
client - > message . maxsize = sizeof ( client - > msgbuf ) ;
client - > message . allowoverflow = true ; // we can catch it
2017-09-17 02:12:53 +00:00
client - > datagram . data = client - > datagram_buf ;
client - > datagram . maxsize = sizeof ( client - > datagram_buf ) ;
client - > datagram . allowoverflow = true ; //simply ignored on overflow
client - > pextknown = false ;
client - > protocol_pext2 = 0 ;
2010-02-15 23:26:55 +00:00
if ( sv . loadgame )
memcpy ( client - > spawn_parms , spawn_parms , sizeof ( spawn_parms ) ) ;
else
{
// call the progs to get default spawn parms for the new client
PR_ExecuteProgram ( pr_global_struct - > SetNewParms ) ;
for ( i = 0 ; i < NUM_SPAWN_PARMS ; i + + )
client - > spawn_parms [ i ] = ( & pr_global_struct - > parm1 ) [ i ] ;
}
SV_SendServerinfo ( client ) ;
}
/*
= = = = = = = = = = = = = = = = = = =
SV_CheckForNewClients
= = = = = = = = = = = = = = = = = = =
*/
void SV_CheckForNewClients ( void )
{
struct qsocket_s * ret ;
int i ;
//
// check for new connections
//
while ( 1 )
{
ret = NET_CheckNewConnections ( ) ;
if ( ! ret )
break ;
//
// init a new client structure
//
for ( i = 0 ; i < svs . maxclients ; i + + )
if ( ! svs . clients [ i ] . active )
break ;
if ( i = = svs . maxclients )
Sys_Error ( " Host_CheckForNewClients: no free clients " ) ;
svs . clients [ i ] . netconnection = ret ;
SV_ConnectClient ( i ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
FRAME UPDATES
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = = = =
SV_ClearDatagram
= = = = = = = = = = = = = = = = = =
*/
void SV_ClearDatagram ( void )
{
SZ_Clear ( & sv . datagram ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
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 .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2017-07-26 04:27:16 +00:00
static int fatbytes ;
static byte * fatpvs ;
static int fatpvs_capacity ;
2010-02-15 23:26:55 +00:00
2012-05-30 08:56:06 +00:00
void SV_AddToFatPVS ( vec3_t org , mnode_t * node , qmodel_t * worldmodel ) //johnfitz -- added worldmodel as a parameter
2010-02-15 23:26:55 +00:00
{
int i ;
byte * pvs ;
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 )
{
pvs = Mod_LeafPVS ( ( mleaf_t * ) node , worldmodel ) ; //johnfitz -- worldmodel as a parameter
for ( i = 0 ; i < fatbytes ; i + + )
fatpvs [ i ] | = pvs [ 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 , node - > children [ 0 ] , worldmodel ) ; //johnfitz -- worldmodel as a parameter
node = node - > children [ 1 ] ;
}
}
}
/*
= = = = = = = = = = = = =
SV_FatPVS
Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the
given point .
= = = = = = = = = = = = =
*/
2012-05-30 08:56:06 +00:00
byte * SV_FatPVS ( vec3_t org , qmodel_t * worldmodel ) //johnfitz -- added worldmodel as a parameter
2010-02-15 23:26:55 +00:00
{
2017-07-26 04:27:16 +00:00
fatbytes = ( worldmodel - > numleafs + 7 ) > > 3 ; // ericw -- was +31, assumed to be a bug/typo
if ( fatpvs = = NULL | | fatbytes > fatpvs_capacity )
{
fatpvs_capacity = fatbytes ;
fatpvs = ( byte * ) realloc ( fatpvs , fatpvs_capacity ) ;
if ( ! fatpvs )
Sys_Error ( " SV_FatPVS: realloc() failed on %d bytes " , fatpvs_capacity ) ;
}
2010-02-15 23:26:55 +00:00
Q_memset ( fatpvs , 0 , fatbytes ) ;
SV_AddToFatPVS ( org , worldmodel - > nodes , worldmodel ) ; //johnfitz -- worldmodel as a parameter
return fatpvs ;
}
/*
= = = = = = = = = = = = =
SV_VisibleToClient - - johnfitz
PVS test encapsulated in a nice function
= = = = = = = = = = = = =
*/
2012-05-30 08:56:06 +00:00
qboolean SV_VisibleToClient ( edict_t * client , edict_t * test , qmodel_t * worldmodel )
2010-02-15 23:26:55 +00:00
{
byte * pvs ;
vec3_t org ;
2017-09-17 02:12:53 +00:00
unsigned int i ;
2010-02-15 23:26:55 +00:00
VectorAdd ( client - > v . origin , client - > v . view_ofs , org ) ;
pvs = SV_FatPVS ( org , worldmodel ) ;
for ( i = 0 ; i < test - > num_leafs ; i + + )
if ( pvs [ test - > leafnums [ i ] > > 3 ] & ( 1 < < ( test - > leafnums [ i ] & 7 ) ) )
return true ;
return false ;
}
//=============================================================================
/*
= = = = = = = = = = = = =
SV_WriteEntitiesToClient
= = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
void SV_WriteEntitiesToClient ( client_t * client , sizebuf_t * msg )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
edict_t * clent = client - > edict ;
2018-05-01 00:35:14 +00:00
unsigned int e , i , maxedict = qcvm - > num_edicts ;
2010-02-15 23:26:55 +00:00
int bits ;
byte * pvs ;
vec3_t org ;
float miss ;
edict_t * ent ;
2017-09-17 02:12:53 +00:00
eval_t * val ;
int maxsize = msg - > maxsize ;
//try to avoid sounds getting lost. flickering entities are weird, but missing sounds+particles are just eerie.
maxsize - = client - > datagram . cursize ;
maxsize - = sv . datagram . cursize ;
if ( maxedict > client - > limit_entities )
maxedict = client - > limit_entities ;
2010-02-15 23:26:55 +00:00
// find the client's PVS
VectorAdd ( clent - > v . origin , clent - > v . view_ofs , org ) ;
2018-05-01 00:35:14 +00:00
pvs = SV_FatPVS ( org , qcvm - > worldmodel ) ;
2017-07-28 20:00:56 +00:00
2010-02-15 23:26:55 +00:00
// send over all entities (excpet the client) that touch the pvs
2018-05-01 00:35:14 +00:00
ent = NEXT_EDICT ( qcvm - > edicts ) ;
2017-09-17 02:12:53 +00:00
for ( e = 1 ; e < maxedict ; e + + , ent = NEXT_EDICT ( ent ) )
2010-02-15 23:26:55 +00:00
{
if ( ent ! = clent ) // clent is ALLWAYS sent
{
// ignore ents without visible models
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
if ( ! ent - > v . modelindex | | ! PR_GetString ( ent - > v . model ) [ 0 ] )
2010-02-15 23:26:55 +00:00
continue ;
//johnfitz -- don't send model>255 entities if protocol is 15
2017-09-17 02:12:53 +00:00
if ( ( unsigned int ) ent - > v . modelindex > = client - > limit_models )
2010-02-15 23:26:55 +00:00
continue ;
// ignore if not touching a PV leaf
for ( i = 0 ; i < ent - > num_leafs ; i + + )
if ( pvs [ ent - > leafnums [ i ] > > 3 ] & ( 1 < < ( ent - > leafnums [ i ] & 7 ) ) )
break ;
2014-09-28 19:02:27 +00:00
// ericw -- added ent->num_leafs < MAX_ENT_LEAFS condition.
//
// if ent->num_leafs == MAX_ENT_LEAFS, the ent is visible from too many leafs
// for us to say whether it's in the PVS, so don't try to vis cull it.
// this commonly happens with rotators, because they often have huge bboxes
// spanning the entire map, or really tall lifts, etc.
if ( i = = ent - > num_leafs & & ent - > num_leafs < MAX_ENT_LEAFS )
2010-02-15 23:26:55 +00:00
continue ; // not visible
}
//johnfitz -- max size for protocol 15 is 18 bytes, not 16 as originally
//assumed here. And, for protocol 85 the max size is actually 24 bytes.
2017-09-17 02:12:53 +00:00
if ( msg - > cursize + 24 > maxsize )
2010-02-15 23:26:55 +00:00
{
//johnfitz -- less spammy overflow message
if ( ! dev_overflows . packetsize | | dev_overflows . packetsize + CONSOLE_RESPAM_TIME < realtime )
{
Con_Printf ( " Packet overflow! \n " ) ;
dev_overflows . packetsize = realtime ;
}
goto stats ;
//johnfitz
}
// send an update
bits = 0 ;
for ( i = 0 ; i < 3 ; i + + )
{
miss = ent - > v . origin [ i ] - ent - > baseline . origin [ i ] ;
if ( miss < - 0.1 | | miss > 0.1 )
bits | = U_ORIGIN1 < < i ;
}
if ( ent - > v . angles [ 0 ] ! = ent - > baseline . angles [ 0 ] )
bits | = U_ANGLE1 ;
if ( ent - > v . angles [ 1 ] ! = ent - > baseline . angles [ 1 ] )
bits | = U_ANGLE2 ;
if ( ent - > v . angles [ 2 ] ! = ent - > baseline . angles [ 2 ] )
bits | = U_ANGLE3 ;
if ( ent - > v . movetype = = MOVETYPE_STEP )
bits | = U_STEP ; // don't mess up the step animation
if ( ent - > baseline . colormap ! = ent - > v . colormap )
bits | = U_COLORMAP ;
if ( ent - > baseline . skin ! = ent - > v . skin )
bits | = U_SKIN ;
if ( ent - > baseline . frame ! = ent - > v . frame )
bits | = U_FRAME ;
if ( ent - > baseline . effects ! = ent - > v . effects )
bits | = U_EFFECTS ;
if ( ent - > baseline . modelindex ! = ent - > v . modelindex )
bits | = U_MODEL ;
//johnfitz -- alpha
2017-09-17 02:12:53 +00:00
// TODO: find a cleaner place to put this code
2018-05-01 00:35:14 +00:00
val = GetEdictFieldValue ( ent , qcvm - > extfields . alpha ) ;
2017-09-17 02:12:53 +00:00
if ( val )
ent - > alpha = ENTALPHA_ENCODE ( val - > _float ) ;
2010-02-15 23:26:55 +00:00
//don't send invisible entities unless they have effects
if ( ent - > alpha = = ENTALPHA_ZERO & & ! ent - > v . effects )
continue ;
//johnfitz
2017-09-17 02:12:53 +00:00
//spike -- PROTOCOL_VERSION_BJP3
if ( sv . protocol = = PROTOCOL_VERSION_BJP3 )
{
//alpha+fullbright can be sent, but they're too hideous...
if ( ent - > baseline . alpha ! = ent - > alpha ) bits | = U_TRANS ;
}
else
2010-02-15 23:26:55 +00:00
//johnfitz -- PROTOCOL_FITZQUAKE
if ( sv . protocol ! = PROTOCOL_NETQUAKE )
{
if ( ent - > baseline . alpha ! = ent - > alpha ) bits | = U_ALPHA ;
if ( bits & U_FRAME & & ( int ) ent - > v . frame & 0xFF00 ) bits | = U_FRAME2 ;
if ( bits & U_MODEL & & ( int ) ent - > v . modelindex & 0xFF00 ) bits | = U_MODEL2 ;
if ( ent - > sendinterval ) bits | = U_LERPFINISH ;
if ( bits > = 65536 ) bits | = U_EXTEND1 ;
if ( bits > = 16777216 ) bits | = U_EXTEND2 ;
}
//johnfitz
if ( e > = 256 )
bits | = U_LONGENTITY ;
if ( bits > = 256 )
bits | = U_MOREBITS ;
//
// write the message
//
MSG_WriteByte ( msg , bits | U_SIGNAL ) ;
if ( bits & U_MOREBITS )
MSG_WriteByte ( msg , bits > > 8 ) ;
2017-09-17 02:12:53 +00:00
//spike -- nehahra protocols are awkward
if ( sv . protocol = = PROTOCOL_VERSION_BJP3 )
;
else
{
//johnfitz -- PROTOCOL_FITZQUAKE
if ( bits & U_EXTEND1 )
MSG_WriteByte ( msg , bits > > 16 ) ;
if ( bits & U_EXTEND2 )
MSG_WriteByte ( msg , bits > > 24 ) ;
//johnfitz
}
2010-02-15 23:26:55 +00:00
if ( bits & U_LONGENTITY )
MSG_WriteShort ( msg , e ) ;
else
MSG_WriteByte ( msg , e ) ;
if ( bits & U_MODEL )
2017-09-17 02:12:53 +00:00
{
if ( sv . protocol = = PROTOCOL_VERSION_BJP3 )
MSG_WriteShort ( msg , ent - > v . modelindex ) ;
else
MSG_WriteByte ( msg , ent - > v . modelindex ) ;
}
2010-02-15 23:26:55 +00:00
if ( bits & U_FRAME )
MSG_WriteByte ( msg , ent - > v . frame ) ;
if ( bits & U_COLORMAP )
MSG_WriteByte ( msg , ent - > v . colormap ) ;
if ( bits & U_SKIN )
MSG_WriteByte ( msg , ent - > v . skin ) ;
if ( bits & U_EFFECTS )
MSG_WriteByte ( msg , ent - > v . effects ) ;
if ( bits & U_ORIGIN1 )
2016-06-24 06:15:41 +00:00
MSG_WriteCoord ( msg , ent - > v . origin [ 0 ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
if ( bits & U_ANGLE1 )
2016-06-24 06:15:41 +00:00
MSG_WriteAngle ( msg , ent - > v . angles [ 0 ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
if ( bits & U_ORIGIN2 )
2016-06-24 06:15:41 +00:00
MSG_WriteCoord ( msg , ent - > v . origin [ 1 ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
if ( bits & U_ANGLE2 )
2016-06-24 06:15:41 +00:00
MSG_WriteAngle ( msg , ent - > v . angles [ 1 ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
if ( bits & U_ORIGIN3 )
2016-06-24 06:15:41 +00:00
MSG_WriteCoord ( msg , ent - > v . origin [ 2 ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
if ( bits & U_ANGLE3 )
2016-06-24 06:15:41 +00:00
MSG_WriteAngle ( msg , ent - > v . angles [ 2 ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
//spike -- nehahra protocols are awkward
if ( sv . protocol = = PROTOCOL_VERSION_BJP3 )
{
//this protocol is shite
if ( ( int ) ent - > v . effects & EF_FULLBRIGHT )
{
MSG_WriteFloat ( msg , 2 ) ;
MSG_WriteFloat ( msg , ENTALPHA_DECODE ( ent - > alpha ) ) ;
MSG_WriteFloat ( msg , 1 ) ;
}
else if ( bits & U_TRANS )
{
MSG_WriteFloat ( msg , 1 ) ;
MSG_WriteFloat ( msg , ENTALPHA_DECODE ( ent - > alpha ) ) ;
}
}
else
{
//johnfitz -- PROTOCOL_FITZQUAKE
if ( bits & U_ALPHA )
MSG_WriteByte ( msg , ent - > alpha ) ;
if ( bits & U_FRAME2 )
MSG_WriteByte ( msg , ( int ) ent - > v . frame > > 8 ) ;
if ( bits & U_MODEL2 )
MSG_WriteByte ( msg , ( int ) ent - > v . modelindex > > 8 ) ;
if ( bits & U_LERPFINISH )
2018-05-01 00:35:14 +00:00
MSG_WriteByte ( msg , ( byte ) ( Q_rint ( ( ent - > v . nextthink - qcvm - > time ) * 255 ) ) ) ;
2017-09-17 02:12:53 +00:00
//johnfitz
}
2010-02-15 23:26:55 +00:00
}
//johnfitz -- devstats
stats :
if ( msg - > cursize > 1024 & & dev_peakstats . packetsize < = 1024 )
2017-03-30 06:26:16 +00:00
Con_DWarning ( " %i byte packet exceeds standard limit of 1024 (max = %d). \n " , msg - > cursize , msg - > maxsize ) ;
2010-02-15 23:26:55 +00:00
dev_stats . packetsize = msg - > cursize ;
2011-01-10 10:35:40 +00:00
dev_peakstats . packetsize = q_max ( msg - > cursize , dev_peakstats . packetsize ) ;
2010-02-15 23:26:55 +00:00
//johnfitz
}
/*
= = = = = = = = = = = = =
SV_CleanupEnts
= = = = = = = = = = = = =
*/
void SV_CleanupEnts ( void )
{
int e ;
edict_t * ent ;
2018-05-01 00:35:14 +00:00
ent = NEXT_EDICT ( qcvm - > edicts ) ;
for ( e = 1 ; e < qcvm - > num_edicts ; e + + , ent = NEXT_EDICT ( ent ) )
2010-02-15 23:26:55 +00:00
{
ent - > v . effects = ( int ) ent - > v . effects & ~ EF_MUZZLEFLASH ;
}
}
/*
= = = = = = = = = = = = = = = = = =
2017-09-17 02:12:53 +00:00
SV_WriteDamageToMessage
2010-02-15 23:26:55 +00:00
= = = = = = = = = = = = = = = = = =
*/
2017-09-17 02:12:53 +00:00
void SV_WriteDamageToMessage ( edict_t * ent , sizebuf_t * msg )
2010-02-15 23:26:55 +00:00
{
edict_t * other ;
2017-09-17 02:12:53 +00:00
int i ;
2010-02-15 23:26:55 +00:00
//
// send a damage message
//
if ( ent - > v . dmg_take | | ent - > v . dmg_save )
{
other = PROG_TO_EDICT ( ent - > v . dmg_inflictor ) ;
MSG_WriteByte ( msg , svc_damage ) ;
MSG_WriteByte ( msg , ent - > v . dmg_save ) ;
MSG_WriteByte ( msg , ent - > v . dmg_take ) ;
for ( i = 0 ; i < 3 ; i + + )
2016-06-24 06:15:41 +00:00
MSG_WriteCoord ( msg , other - > v . origin [ i ] + 0.5 * ( other - > v . mins [ i ] + other - > v . maxs [ i ] ) , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
ent - > v . dmg_take = 0 ;
ent - > v . dmg_save = 0 ;
}
//
// send the current viewpos offset from the view entity
//
SV_SetIdealPitch ( ) ; // how much to look up / down ideally
// a fixangle might get lost in a dropped packet. Oh well.
if ( ent - > v . fixangle )
{
MSG_WriteByte ( msg , svc_setangle ) ;
for ( i = 0 ; i < 3 ; i + + )
2016-06-24 06:15:41 +00:00
MSG_WriteAngle ( msg , ent - > v . angles [ i ] , sv . protocolflags ) ;
2010-02-15 23:26:55 +00:00
ent - > v . fixangle = 0 ;
}
2017-09-17 02:12:53 +00:00
}
/*
= = = = = = = = = = = = = = = = = =
SV_WriteClientdataToMessage
= = = = = = = = = = = = = = = = = =
*/
void SV_WriteClientdataToMessage ( client_t * client , sizebuf_t * msg )
{
edict_t * ent = client - > edict ;
int bits ;
int i ;
int items ;
eval_t * val ;
unsigned int weaponmodelindex = SV_ModelIndex ( PR_GetString ( ent - > v . weaponmodel ) ) ;
if ( weaponmodelindex > = client - > limit_models )
weaponmodelindex = 0 ;
2010-02-15 23:26:55 +00:00
bits = 0 ;
if ( ent - > v . view_ofs [ 2 ] ! = DEFAULT_VIEWHEIGHT )
bits | = SU_VIEWHEIGHT ;
if ( ent - > v . idealpitch )
bits | = SU_IDEALPITCH ;
// stuff the sigil bits into the high bits of items for sbar, or else
// mix in items2
2018-05-01 00:35:14 +00:00
val = GetEdictFieldValue ( ent , qcvm - > extfields . items2 ) ;
2010-02-15 23:26:55 +00:00
if ( val )
items = ( int ) ent - > v . items | ( ( int ) val - > _float < < 23 ) ;
else
items = ( int ) ent - > v . items | ( ( int ) pr_global_struct - > serverflags < < 28 ) ;
bits | = SU_ITEMS ;
if ( ( int ) ent - > v . flags & FL_ONGROUND )
bits | = SU_ONGROUND ;
if ( ent - > v . waterlevel > = 2 )
bits | = SU_INWATER ;
for ( i = 0 ; i < 3 ; i + + )
{
if ( ent - > v . punchangle [ i ] )
bits | = ( SU_PUNCH1 < < i ) ;
if ( ent - > v . velocity [ i ] )
bits | = ( SU_VELOCITY1 < < i ) ;
}
if ( ent - > v . weaponframe )
bits | = SU_WEAPONFRAME ;
if ( ent - > v . armorvalue )
bits | = SU_ARMOR ;
// if (ent->v.weapon)
bits | = SU_WEAPON ;
//johnfitz -- PROTOCOL_FITZQUAKE
if ( sv . protocol ! = PROTOCOL_NETQUAKE )
{
2017-09-17 02:12:53 +00:00
if ( bits & SU_WEAPON & & weaponmodelindex & 0xFF00 ) bits | = SU_WEAPON2 ;
2010-02-15 23:26:55 +00:00
if ( ( int ) ent - > v . armorvalue & 0xFF00 ) bits | = SU_ARMOR2 ;
if ( ( int ) ent - > v . currentammo & 0xFF00 ) bits | = SU_AMMO2 ;
if ( ( int ) ent - > v . ammo_shells & 0xFF00 ) bits | = SU_SHELLS2 ;
if ( ( int ) ent - > v . ammo_nails & 0xFF00 ) bits | = SU_NAILS2 ;
if ( ( int ) ent - > v . ammo_rockets & 0xFF00 ) bits | = SU_ROCKETS2 ;
if ( ( int ) ent - > v . ammo_cells & 0xFF00 ) bits | = SU_CELLS2 ;
if ( bits & SU_WEAPONFRAME & & ( int ) ent - > v . weaponframe & 0xFF00 ) bits | = SU_WEAPONFRAME2 ;
if ( bits & SU_WEAPON & & ent - > alpha ! = ENTALPHA_DEFAULT ) bits | = SU_WEAPONALPHA ; //for now, weaponalpha = client entity alpha
if ( bits > = 65536 ) bits | = SU_EXTEND1 ;
if ( bits > = 16777216 ) bits | = SU_EXTEND2 ;
}
//johnfitz
// send the data
MSG_WriteByte ( msg , svc_clientdata ) ;
MSG_WriteShort ( msg , bits ) ;
//johnfitz -- PROTOCOL_FITZQUAKE
if ( bits & SU_EXTEND1 ) MSG_WriteByte ( msg , bits > > 16 ) ;
if ( bits & SU_EXTEND2 ) MSG_WriteByte ( msg , bits > > 24 ) ;
//johnfitz
if ( bits & SU_VIEWHEIGHT )
MSG_WriteChar ( msg , ent - > v . view_ofs [ 2 ] ) ;
if ( bits & SU_IDEALPITCH )
MSG_WriteChar ( msg , ent - > v . idealpitch ) ;
for ( i = 0 ; i < 3 ; i + + )
{
if ( bits & ( SU_PUNCH1 < < i ) )
MSG_WriteChar ( msg , ent - > v . punchangle [ i ] ) ;
if ( bits & ( SU_VELOCITY1 < < i ) )
MSG_WriteChar ( msg , ent - > v . velocity [ i ] / 16 ) ;
}
// [always sent] if (bits & SU_ITEMS)
MSG_WriteLong ( msg , items ) ;
if ( bits & SU_WEAPONFRAME )
MSG_WriteByte ( msg , ent - > v . weaponframe ) ;
if ( bits & SU_ARMOR )
MSG_WriteByte ( msg , ent - > v . armorvalue ) ;
if ( bits & SU_WEAPON )
2017-09-17 02:12:53 +00:00
{
if ( sv . protocol = = PROTOCOL_VERSION_BJP3 )
MSG_WriteShort ( msg , weaponmodelindex ) ;
else
MSG_WriteByte ( msg , weaponmodelindex ) ;
}
2010-02-15 23:26:55 +00:00
MSG_WriteShort ( msg , ent - > v . health ) ;
MSG_WriteByte ( msg , ent - > v . currentammo ) ;
MSG_WriteByte ( msg , ent - > v . ammo_shells ) ;
MSG_WriteByte ( msg , ent - > v . ammo_nails ) ;
MSG_WriteByte ( msg , ent - > v . ammo_rockets ) ;
MSG_WriteByte ( msg , ent - > v . ammo_cells ) ;
if ( standard_quake )
{
MSG_WriteByte ( msg , ent - > v . weapon ) ;
}
else
{
for ( i = 0 ; i < 32 ; i + + )
{
if ( ( ( int ) ent - > v . weapon ) & ( 1 < < i ) )
{
MSG_WriteByte ( msg , i ) ;
break ;
}
}
}
//johnfitz -- PROTOCOL_FITZQUAKE
if ( bits & SU_WEAPON2 )
2017-09-17 02:12:53 +00:00
MSG_WriteByte ( msg , weaponmodelindex > > 8 ) ;
2010-02-15 23:26:55 +00:00
if ( bits & SU_ARMOR2 )
MSG_WriteByte ( msg , ( int ) ent - > v . armorvalue > > 8 ) ;
if ( bits & SU_AMMO2 )
MSG_WriteByte ( msg , ( int ) ent - > v . currentammo > > 8 ) ;
if ( bits & SU_SHELLS2 )
MSG_WriteByte ( msg , ( int ) ent - > v . ammo_shells > > 8 ) ;
if ( bits & SU_NAILS2 )
MSG_WriteByte ( msg , ( int ) ent - > v . ammo_nails > > 8 ) ;
if ( bits & SU_ROCKETS2 )
MSG_WriteByte ( msg , ( int ) ent - > v . ammo_rockets > > 8 ) ;
if ( bits & SU_CELLS2 )
MSG_WriteByte ( msg , ( int ) ent - > v . ammo_cells > > 8 ) ;
if ( bits & SU_WEAPONFRAME2 )
MSG_WriteByte ( msg , ( int ) ent - > v . weaponframe > > 8 ) ;
if ( bits & SU_WEAPONALPHA )
MSG_WriteByte ( msg , ent - > alpha ) ; //for now, weaponalpha = client entity alpha
//johnfitz
}
/*
= = = = = = = = = = = = = = = = = = = = = = =
SV_SendClientDatagram
= = = = = = = = = = = = = = = = = = = = = = =
*/
qboolean SV_SendClientDatagram ( client_t * client )
{
2017-09-17 02:12:53 +00:00
byte buf [ MAX_DATAGRAM + 1000 ] ;
2010-02-15 23:26:55 +00:00
sizebuf_t msg ;
2017-09-17 02:12:53 +00:00
if ( ! client - > netconnection )
{
//botclient, shouldn't be sent anything.
SZ_Clear ( & client - > datagram ) ;
return true ;
}
msg . allowoverflow = false ;
2010-02-15 23:26:55 +00:00
msg . data = buf ;
2017-09-17 02:12:53 +00:00
msg . maxsize = client - > limit_unreliable ;
2010-02-15 23:26:55 +00:00
msg . cursize = 0 ;
2017-09-17 02:12:53 +00:00
if ( client - > download . file )
msg . maxsize / = 2 ; //make sure there's space for download data
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
host_client = client ;
if ( client - > spawned )
{
sv_player = client - > edict ;
if ( client - > protocol_pext2 & PEXT2_REPLACEMENTDELTAS )
{
SV_WriteDamageToMessage ( client - > edict , & msg ) ;
if ( ! ( client - > protocol_pext2 & PEXT2_PREDINFO ) )
SV_WriteClientdataToMessage ( client , & msg ) ;
else
SVFTE_WriteStats ( client , & msg ) ;
if ( ! client - > snapshotresume )
{
SVFTE_BuildSnapshotForClient ( client ) ;
SVFTE_CalcEntityDeltas ( client ) ;
}
SVFTE_WriteEntitiesToClient ( client , & msg , sizeof ( buf ) ) ; //must always write some data, or the stats will break
//this delta protocol doesn't wipe old state just because there's a new packet.
//the server isn't required to sync with the client frames either
//so we can just spam multiple packets to keep our udp data under the MTU
while ( client - > snapshotresume )
{
NET_SendUnreliableMessage ( client - > netconnection , & msg ) ;
SZ_Clear ( & msg ) ;
SVFTE_WriteEntitiesToClient ( client , & msg , sizeof ( buf ) ) ;
}
}
else
{
MSG_WriteByte ( & msg , svc_time ) ;
2018-05-01 00:35:14 +00:00
MSG_WriteFloat ( & msg , qcvm - > time ) ;
2017-09-17 02:12:53 +00:00
if ( client - > protocol_pext2 & PEXT2_PREDINFO )
MSG_WriteShort ( & msg , ( client - > lastmovemessage & 0xffff ) ) ;
// add the client specific data to the datagram
SV_WriteDamageToMessage ( client - > edict , & msg ) ;
SV_WriteClientdataToMessage ( client , & msg ) ;
SV_WriteEntitiesToClient ( client , & msg ) ;
}
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
// copy the private datagram if there is space
if ( msg . cursize + client - > datagram . cursize < msg . maxsize & & ! client - > datagram . overflowed )
SZ_Write ( & msg , client - > datagram . data , client - > datagram . cursize ) ;
client - > datagram . overflowed = false ;
SZ_Clear ( & client - > datagram ) ;
// copy the server datagram if there is space
if ( msg . cursize + sv . datagram . cursize < msg . maxsize )
SZ_Write ( & msg , sv . datagram . data , sv . datagram . cursize ) ;
}
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
SV_VoiceSendPacket ( client , & msg ) ;
2010-02-15 23:26:55 +00:00
2017-09-17 02:12:53 +00:00
msg . maxsize = client - > limit_unreliable ;
Host_AppendDownloadData ( client , & msg ) ;
2010-02-15 23:26:55 +00:00
// send the datagram
2017-09-17 02:12:53 +00:00
if ( msg . cursize & & NET_SendUnreliableMessage ( client - > netconnection , & msg ) = = - 1 )
2010-02-15 23:26:55 +00:00
{
2017-09-17 02:12:53 +00:00
SV_DropClient ( false ) ; // if the message couldn't send, kick off
2010-02-15 23:26:55 +00:00
return false ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = =
SV_UpdateToReliableMessages
= = = = = = = = = = = = = = = = = = = = = = =
*/
void SV_UpdateToReliableMessages ( void )
{
int i , j ;
client_t * client ;
// check for changes to be sent over the reliable streams
for ( i = 0 , host_client = svs . clients ; i < svs . maxclients ; i + + , host_client + + )
{
if ( host_client - > old_frags ! = host_client - > edict - > v . frags )
{
for ( j = 0 , client = svs . clients ; j < svs . maxclients ; j + + , client + + )
{
2017-09-17 02:12:53 +00:00
if ( ! client - > knowntoqc )
2010-02-15 23:26:55 +00:00
continue ;
MSG_WriteByte ( & client - > message , svc_updatefrags ) ;
MSG_WriteByte ( & client - > message , i ) ;
MSG_WriteShort ( & client - > message , host_client - > edict - > v . frags ) ;
}
host_client - > old_frags = host_client - > edict - > v . frags ;
}
}
for ( j = 0 , client = svs . clients ; j < svs . maxclients ; j + + , client + + )
{
if ( ! client - > active )
continue ;
SZ_Write ( & client - > message , sv . reliable_datagram . data , sv . reliable_datagram . cursize ) ;
}
SZ_Clear ( & sv . reliable_datagram ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = =
SV_SendNop
Send a nop message without trashing or sending the accumulated client
message buffer
= = = = = = = = = = = = = = = = = = = = = = =
*/
void SV_SendNop ( client_t * client )
{
sizebuf_t msg ;
byte buf [ 4 ] ;
msg . data = buf ;
msg . maxsize = sizeof ( buf ) ;
msg . cursize = 0 ;
MSG_WriteChar ( & msg , svc_nop ) ;
if ( NET_SendUnreliableMessage ( client - > netconnection , & msg ) = = - 1 )
2017-09-17 02:12:53 +00:00
SV_DropClient ( false ) ; // if the message couldn't send, kick off
2010-02-15 23:26:55 +00:00
client - > last_message = realtime ;
}
2017-09-17 02:12:53 +00:00
int SV_SendPrespawnParticlePrecaches ( int idx )
{
size_t maxsize = host_client - > message . maxsize ; //we can go quite large
if ( ! host_client - > protocol_pext2 )
return - 1 ; //unsupported by this client.
for ( ; idx < MAX_PARTICLETYPES ; idx + + )
{
if ( ! sv . particle_precache [ idx ] )
continue ;
if ( host_client - > message . cursize + 4 + strlen ( sv . particle_precache [ idx ] ) > maxsize )
break ;
MSG_WriteByte ( & host_client - > message , svcdp_precache ) ;
MSG_WriteShort ( & host_client - > message , 0x4000 | idx ) ;
MSG_WriteString ( & host_client - > message , sv . particle_precache [ idx ] ) ;
}
if ( idx = = MAX_PARTICLETYPES )
return - 1 ;
return idx ;
}
int SV_SendPrespawnStatics ( int idx )
{
entity_state_t * svent ;
int maxsize = host_client - > message . maxsize - 128 ; //we can go quite large
while ( 1 )
{
if ( idx > = sv . num_statics )
return - 1 ;
svent = & sv . static_entities [ idx ] ;
if ( host_client - > message . cursize > maxsize )
break ;
idx + + ;
if ( svent - > modelindex > = host_client - > limit_models )
continue ;
if ( memcmp ( & nullentitystate , svent , sizeof ( nullentitystate ) ) )
MSG_WriteStaticOrBaseLine ( & host_client - > message , - 1 , svent , host_client - > protocol_pext2 , sv . protocol , sv . protocolflags ) ;
}
return idx ;
}
int SV_SendAmbientSounds ( int idx )
{
struct ambientsound_s * snd ;
int maxsize = host_client - > message . maxsize - 128 ; //we can go quite large
qboolean large ;
size_t i ;
while ( 1 )
{
if ( idx > = sv . num_ambients )
return - 1 ;
snd = & sv . ambientsounds [ idx ] ;
if ( host_client - > message . cursize > maxsize )
break ;
idx + + ;
if ( snd - > soundindex > = host_client - > limit_sounds )
continue ;
large = ( snd - > soundindex > 255 ) ;
if ( large )
MSG_WriteByte ( & host_client - > message , svc_spawnstaticsound2 ) ; //johnfitz -- PROTOCOL_FITZQUAKE
else
MSG_WriteByte ( & host_client - > message , svc_spawnstaticsound ) ;
for ( i = 0 ; i < 3 ; i + + )
MSG_WriteCoord ( & host_client - > message , snd - > origin [ i ] , sv . protocolflags ) ;
if ( large )
MSG_WriteShort ( & host_client - > message , snd - > soundindex ) ;
else
MSG_WriteByte ( & host_client - > message , snd - > soundindex ) ;
MSG_WriteByte ( & host_client - > message , snd - > volume * 255 ) ;
MSG_WriteByte ( & host_client - > message , snd - > attenuation * 64 ) ;
}
return idx ;
}
int SV_SendPrespawnBaselines ( int idx )
{
edict_t * svent ;
int maxsize = host_client - > message . maxsize - 128 ; //we can go quite large
while ( 1 )
{
2018-05-01 00:35:14 +00:00
if ( idx > = qcvm - > num_edicts )
2017-09-17 02:12:53 +00:00
return - 1 ;
svent = EDICT_NUM ( idx ) ;
if ( host_client - > message . cursize > maxsize )
break ;
if ( memcmp ( & nullentitystate , & svent - > baseline , sizeof ( nullentitystate ) ) )
MSG_WriteStaticOrBaseLine ( & host_client - > message , idx , & svent - > baseline , host_client - > protocol_pext2 , sv . protocol , sv . protocolflags ) ;
idx + + ;
}
return idx ;
}
2010-02-15 23:26:55 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = =
SV_SendClientMessages
= = = = = = = = = = = = = = = = = = = = = = =
*/
void SV_SendClientMessages ( void )
{
int i ;
// update frags, names, etc
SV_UpdateToReliableMessages ( ) ;
// build individual updates
for ( i = 0 , host_client = svs . clients ; i < svs . maxclients ; i + + , host_client + + )
{
if ( ! host_client - > active )
continue ;
2017-09-17 02:12:53 +00:00
if ( ! SV_SendClientDatagram ( host_client ) )
continue ;
if ( ! host_client - > spawned )
2010-02-15 23:26:55 +00:00
{
// the player isn't totally in the game yet
// send small keepalive messages if too much time has passed
// send a full message when the next signon stage has been requested
// some other message data (name changes, etc) may accumulate
// between signon stages
if ( ! host_client - > sendsignon )
{
if ( realtime - host_client - > last_message > 5 )
SV_SendNop ( host_client ) ;
continue ; // don't send out non-signon messages
}
2017-09-17 02:12:53 +00:00
if ( host_client - > sendsignon = = 2 )
{
host_client - > signonidx = SV_SendPrespawnParticlePrecaches ( host_client - > signonidx ) ;
if ( host_client - > signonidx < 0 )
{
host_client - > signonidx = 0 ;
host_client - > sendsignon + + ;
}
}
if ( host_client - > sendsignon = = 3 )
{
host_client - > signonidx = SV_SendPrespawnBaselines ( host_client - > signonidx ) ;
if ( host_client - > signonidx < 0 )
{
host_client - > signonidx = 0 ;
host_client - > sendsignon + + ;
}
}
if ( host_client - > sendsignon = = 4 )
{
host_client - > signonidx = SV_SendPrespawnStatics ( host_client - > signonidx ) ;
if ( host_client - > signonidx < 0 )
{
host_client - > signonidx = 0 ;
host_client - > sendsignon + + ;
}
}
if ( host_client - > sendsignon = = 5 )
{
host_client - > signonidx = SV_SendAmbientSounds ( host_client - > signonidx ) ;
if ( host_client - > signonidx < 0 )
{
host_client - > signonidx = 0 ;
host_client - > sendsignon + + ;
}
}
if ( host_client - > sendsignon = = 6 )
{
if ( host_client - > message . cursize + sv . signon . cursize + 2 < host_client - > message . maxsize )
{
SZ_Write ( & host_client - > message , sv . signon . data , sv . signon . cursize ) ;
MSG_WriteByte ( & host_client - > message , svc_signonnum ) ;
MSG_WriteByte ( & host_client - > message , 2 ) ;
host_client - > sendsignon = true ;
}
}
2010-02-15 23:26:55 +00:00
}
// check for an overflowed message. Should only happen
// on a very fucked up connection that backs up a lot, then
// changes level
if ( host_client - > message . overflowed )
{
2017-09-17 02:12:53 +00:00
SZ_Clear ( & host_client - > message ) ;
SV_DropClient ( false ) ;
2010-02-15 23:26:55 +00:00
continue ;
}
if ( host_client - > message . cursize | | host_client - > dropasap )
{
if ( ! NET_CanSendMessage ( host_client - > netconnection ) )
{
// I_Printf ("can't write\n");
continue ;
}
if ( host_client - > dropasap )
SV_DropClient ( false ) ; // went to another level
else
{
if ( NET_SendMessage ( host_client - > netconnection
, & host_client - > message ) = = - 1 )
2017-09-17 02:12:53 +00:00
SV_DropClient ( false ) ; // if the message couldn't send, kick off
2010-02-15 23:26:55 +00:00
SZ_Clear ( & host_client - > message ) ;
host_client - > last_message = realtime ;
2017-09-17 02:12:53 +00:00
if ( host_client - > sendsignon = = true )
host_client - > sendsignon = false ;
2010-02-15 23:26:55 +00:00
}
}
}
// clear muzzle flashes
SV_CleanupEnts ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
SERVER SPAWNING
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = =
SV_ModelIndex
= = = = = = = = = = = = = = = =
*/
2010-08-29 02:22:55 +00:00
int SV_ModelIndex ( const char * name )
2010-02-15 23:26:55 +00:00
{
int i ;
if ( ! name | | ! name [ 0 ] )
return 0 ;
for ( i = 0 ; i < MAX_MODELS & & sv . model_precache [ i ] ; i + + )
if ( ! strcmp ( sv . model_precache [ i ] , name ) )
return i ;
if ( i = = MAX_MODELS | | ! sv . model_precache [ i ] )
Sys_Error ( " SV_ModelIndex: model %s not precached " , name ) ;
return i ;
}
/*
= = = = = = = = = = = = = = = =
SV_CreateBaseline
= = = = = = = = = = = = = = = =
*/
void SV_CreateBaseline ( void )
{
edict_t * svent ;
int entnum ;
2017-09-17 02:12:53 +00:00
eval_t * val ;
2010-02-15 23:26:55 +00:00
2018-05-01 00:35:14 +00:00
for ( entnum = 0 ; entnum < qcvm - > num_edicts ; entnum + + )
2010-02-15 23:26:55 +00:00
{
// get the current server version
svent = EDICT_NUM ( entnum ) ;
if ( svent - > free )
continue ;
if ( entnum > svs . maxclients & & ! svent - > v . modelindex )
continue ;
//
// create entity baseline
//
VectorCopy ( svent - > v . origin , svent - > baseline . origin ) ;
VectorCopy ( svent - > v . angles , svent - > baseline . angles ) ;
svent - > baseline . frame = svent - > v . frame ;
svent - > baseline . skin = svent - > v . skin ;
if ( entnum > 0 & & entnum < = svs . maxclients )
{
svent - > baseline . colormap = entnum ;
svent - > baseline . modelindex = SV_ModelIndex ( " progs/player.mdl " ) ;
svent - > baseline . alpha = ENTALPHA_DEFAULT ; //johnfitz -- alpha support
}
else
{
svent - > baseline . colormap = 0 ;
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
svent - > baseline . modelindex = SV_ModelIndex ( PR_GetString ( svent - > v . model ) ) ;
2018-05-01 00:35:14 +00:00
val = GetEdictFieldValue ( svent , qcvm - > extfields . alpha ) ;
2017-09-17 02:12:53 +00:00
if ( val )
svent - > baseline . alpha = ENTALPHA_ENCODE ( val - > _float ) ;
else
svent - > baseline . alpha = svent - > alpha ; //johnfitz -- alpha support
2010-02-15 23:26:55 +00:00
}
2017-09-17 02:12:53 +00:00
//Spike -- baselines are now generated on a per-client basis.
//FIXME: should merge the above with other edict->entity_state copies (updates, baselines, spawnstatics)
//1) this allows per-client extensions.
//2) this avoids pre-generating a single signon buffer, splitting it over multiple packets.
// thereby allowing more than 3k or so entities
2010-02-15 23:26:55 +00:00
}
}
/*
= = = = = = = = = = = = = = = =
SV_SendReconnect
Tell all the clients that the server is changing levels
= = = = = = = = = = = = = = = =
*/
void SV_SendReconnect ( void )
{
chase.c, cl_input.c, cl_parse.c, client.h, common.c, common.h, console.h,
cvar.h, draw.h, gl_draw.c, gl_fog.c, gl_mesh.c, gl_model.c, gl_model.h,
gl_rmain.c, gl_rmisc.c, gl_screen.c, gl_sky.c, gl_texmgr.c, glquake.h,
host.c, keys.c, keys.h, main.c, menu.c, menu.h, pr_cmds.c, quakedef.h,
r_alias.c, r_brush.c, r_part.c, r_sprite.c, r_world.c, sbar.c, sbar.h,
screen.h, snd_dma.c, snd_mem.c, snd_mix.c, sv_main.c, sys_sdl.c, vid.h,
view.h, world.c, world.h: Loads of warning fixes about missing function
prototypes, missing parens around &, missing braces leading to ambiguous
else statements and unused and uninitialized variables. There are still a
couple of unitialised variables here and there, but not much. The warnings
about strict aliasing violations need taking care of.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@21 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-16 12:01:07 +00:00
byte data [ 128 ] ;
2010-02-15 23:26:55 +00:00
sizebuf_t msg ;
msg . data = data ;
msg . cursize = 0 ;
msg . maxsize = sizeof ( data ) ;
MSG_WriteChar ( & msg , svc_stufftext ) ;
MSG_WriteString ( & msg , " reconnect \n " ) ;
2010-06-20 15:00:57 +00:00
NET_SendToAll ( & msg , 5.0 ) ;
2010-02-15 23:26:55 +00:00
2011-12-18 09:55:18 +00:00
if ( ! isDedicated )
2017-09-17 02:12:53 +00:00
Cmd_ExecuteString ( " reconnect \n " , src_server ) ;
2010-02-15 23:26:55 +00:00
}
/*
= = = = = = = = = = = = = = = =
SV_SaveSpawnparms
Grabs the current state of each client for saving across the
transition to another level
= = = = = = = = = = = = = = = =
*/
void SV_SaveSpawnparms ( void )
{
int i , j ;
svs . serverflags = pr_global_struct - > serverflags ;
for ( i = 0 , host_client = svs . clients ; i < svs . maxclients ; i + + , host_client + + )
{
if ( ! host_client - > active )
continue ;
// call the progs to get default spawn parms for the new client
pr_global_struct - > self = EDICT_TO_PROG ( host_client - > edict ) ;
PR_ExecuteProgram ( pr_global_struct - > SetChangeParms ) ;
for ( j = 0 ; j < NUM_SPAWN_PARMS ; j + + )
host_client - > spawn_parms [ j ] = ( & pr_global_struct - > parm1 ) [ j ] ;
}
}
2018-05-07 18:32:50 +00:00
//used for sv.qcvm.GetModel (so ssqc+csqc can share builtins)
qmodel_t * SV_ModelForIndex ( int index )
{
if ( index < 0 | | index > = MAX_MODELS )
return NULL ;
return sv . models [ index ] ;
}
2010-02-15 23:26:55 +00:00
/*
= = = = = = = = = = = = = = = =
SV_SpawnServer
This is called at the start of each level
= = = = = = = = = = = = = = = =
*/
extern float scr_centertime_off ;
2010-08-29 02:22:55 +00:00
void SV_SpawnServer ( const char * server )
2010-02-15 23:26:55 +00:00
{
2010-03-09 14:15:34 +00:00
static char dummy [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2010-02-15 23:26:55 +00:00
edict_t * ent ;
int i ;
// let's not have any servers with no name
if ( hostname . string [ 0 ] = = 0 )
Cvar_Set ( " hostname " , " UNNAMED " ) ;
scr_centertime_off = 0 ;
Con_DPrintf ( " SpawnServer: %s \n " , server ) ;
svs . changelevel_issued = false ; // now safe to issue another
//
// tell all connected clients that we are going to a new level
//
if ( sv . active )
{
2018-05-01 00:35:14 +00:00
PR_SwitchQCVM ( NULL ) ;
2010-02-15 23:26:55 +00:00
SV_SendReconnect ( ) ;
2018-05-01 00:35:14 +00:00
PR_SwitchQCVM ( & sv . qcvm ) ;
2010-02-15 23:26:55 +00:00
}
//
// make cvars consistant
//
if ( coop . value )
2011-12-29 21:21:11 +00:00
Cvar_Set ( " deathmatch " , " 0 " ) ;
2010-02-15 23:26:55 +00:00
current_skill = ( int ) ( skill . value + 0.5 ) ;
if ( current_skill < 0 )
current_skill = 0 ;
if ( current_skill > 3 )
current_skill = 3 ;
Cvar_SetValue ( " skill " , ( float ) current_skill ) ;
//
// set up the new server
//
2011-07-05 06:28:42 +00:00
//memset (&sv, 0, sizeof(sv));
2010-02-15 23:26:55 +00:00
Host_ClearMemory ( ) ;
2011-12-27 13:15:31 +00:00
q_strlcpy ( sv . name , server , sizeof ( sv . name ) ) ;
2010-02-15 23:26:55 +00:00
sv . protocol = sv_protocol ; // johnfitz
2016-06-24 06:15:41 +00:00
if ( sv . protocol = = PROTOCOL_RMQ )
{
// set up the protocol flags used by this server
// (note - these could be cvar-ised so that server admins could choose the protocol features used by their servers)
sv . protocolflags = PRFL_INT32COORD | PRFL_SHORTANGLE ;
}
else sv . protocolflags = 0 ;
2010-02-15 23:26:55 +00:00
// load progs to get entity field count
2018-05-01 00:35:14 +00:00
PR_LoadProgs ( " progs.dat " , true , pr_ssqcbuiltins , pr_ssqcnumbuiltins ) ;
2010-02-15 23:26:55 +00:00
// allocate server memory
2011-07-05 06:28:42 +00:00
/* Host_ClearMemory() called above already cleared the whole sv structure */
2018-05-01 00:35:14 +00:00
qcvm - > max_edicts = CLAMP ( MIN_EDICTS , ( int ) max_edicts . value , MAX_EDICTS ) ; //johnfitz -- max_edicts cvar
qcvm - > edicts = ( edict_t * ) malloc ( qcvm - > max_edicts * qcvm - > edict_size ) ; // ericw -- sv.edicts switched to use malloc()
2010-02-15 23:26:55 +00:00
sv . datagram . maxsize = sizeof ( sv . datagram_buf ) ;
sv . datagram . cursize = 0 ;
sv . datagram . data = sv . datagram_buf ;
2017-09-17 02:12:53 +00:00
sv . multicast . maxsize = sizeof ( sv . multicast_buf ) ;
sv . multicast . cursize = 0 ;
sv . multicast . data = sv . multicast_buf ;
2010-02-15 23:26:55 +00:00
sv . reliable_datagram . maxsize = sizeof ( sv . reliable_datagram_buf ) ;
sv . reliable_datagram . cursize = 0 ;
sv . reliable_datagram . data = sv . reliable_datagram_buf ;
sv . signon . maxsize = sizeof ( sv . signon_buf ) ;
sv . signon . cursize = 0 ;
sv . signon . data = sv . signon_buf ;
// leave slots at start for clients only
2018-05-01 00:35:14 +00:00
qcvm - > num_edicts = qcvm - > reserved_edicts = svs . maxclients + 1 ;
memset ( qcvm - > edicts , 0 , qcvm - > num_edicts * qcvm - > edict_size ) ; // ericw -- sv.edicts switched to use malloc()
2010-02-15 23:26:55 +00:00
for ( i = 0 ; i < svs . maxclients ; i + + )
{
ent = EDICT_NUM ( i + 1 ) ;
svs . clients [ i ] . edict = ent ;
}
sv . state = ss_loading ;
sv . paused = false ;
2018-05-01 00:35:14 +00:00
qcvm - > time = 1.0 ;
2010-02-15 23:26:55 +00:00
2011-12-27 13:15:31 +00:00
q_strlcpy ( sv . name , server , sizeof ( sv . name ) ) ;
2011-12-27 10:50:42 +00:00
q_snprintf ( sv . modelname , sizeof ( sv . modelname ) , " maps/%s.bsp " , server ) ;
2018-05-01 00:35:14 +00:00
qcvm - > worldmodel = Mod_ForName ( sv . modelname , false ) ;
if ( ! qcvm - > worldmodel | | qcvm - > worldmodel - > type ! = mod_brush )
2010-02-15 23:26:55 +00:00
{
Con_Printf ( " Couldn't spawn server %s \n " , sv . modelname ) ;
sv . active = false ;
return ;
}
2018-05-01 00:35:14 +00:00
sv . models [ 1 ] = qcvm - > worldmodel ;
2018-05-07 18:32:50 +00:00
qcvm - > GetModel = SV_ModelForIndex ;
2010-02-15 23:26:55 +00:00
//
// clear world interaction links
//
SV_ClearWorld ( ) ;
2010-03-09 14:11:10 +00:00
sv . sound_precache [ 0 ] = dummy ;
sv . model_precache [ 0 ] = dummy ;
2010-02-15 23:26:55 +00:00
sv . model_precache [ 1 ] = sv . modelname ;
2018-05-01 00:35:14 +00:00
if ( qcvm - > worldmodel - > numsubmodels > MAX_MODELS )
2017-09-17 02:12:53 +00:00
{
Con_Printf ( " too many inline models %s \n " , sv . modelname ) ;
sv . active = false ;
return ;
}
2018-05-01 00:35:14 +00:00
for ( i = 1 ; i < qcvm - > worldmodel - > numsubmodels ; i + + )
2010-02-15 23:26:55 +00:00
{
sv . model_precache [ 1 + i ] = localmodels [ i ] ;
sv . models [ i + 1 ] = Mod_ForName ( localmodels [ i ] , false ) ;
}
//
// load the rest of the entities
//
ent = EDICT_NUM ( 0 ) ;
2018-05-01 00:35:14 +00:00
memset ( & ent - > v , 0 , qcvm - > progs - > entityfields * 4 ) ;
2010-02-15 23:26:55 +00:00
ent - > free = false ;
2018-05-01 00:35:14 +00:00
ent - > v . model = PR_SetEngineString ( qcvm - > worldmodel - > name ) ;
2010-02-15 23:26:55 +00:00
ent - > v . modelindex = 1 ; // world model
ent - > v . solid = SOLID_BSP ;
ent - > v . movetype = MOVETYPE_PUSH ;
if ( coop . value )
pr_global_struct - > coop = coop . value ;
else
pr_global_struct - > deathmatch = deathmatch . value ;
64 bit compatibility effort, 4/nn: x86_64 works just fine now, yey!
the QuakeC interpreter used to use string pointer offsets from pr_strings
even when the pointers lead to engine data which is often well out of
32bit range on a 64bit architecture and they lead to crashes. they now
go through the new PR_SetEngineString and PR_GetString functions which
turn any address outside the pr_strings area into an index into a table
of engine string addresses, adding new string addresses to the table as
needed. the engine strings table is allocated with 256 entries at first
(see the PR_STRING_ALLOCSLOTS definition in pr_edict.c) and its size is
incremented by 256 as needed and re-allocated on the zone. managing that
allocation and reallocation is accomplished by the recently added Z_Realloc
function. implementation based on the uhexen2 (hexen2: hammer of thyrion)
engine which, in turn, is loosely based on twilight and quakeforge engines.
pr_strings range check is from tyrquake.
pr_edict.c: added the new PR_SetEngineString, PR_GetString, PR_AllocString
public functions and the new private PR_AllocStringSlots function. made
ED_NewString private to pr_edict.c and reworked it to return an index to a
newly allocated string.
progs.h: added prototypes for the new public PR_SetEngineString, PR_GetString
and PR_AllocString functions.
host_cmd.c, pr_cmds.c, pr_edict.c, pr_exec.c, progs.h, sv_main.c, sv_phys.c:
modifed to use the new PR_SetEngineString and PR_GetString functions.
git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@38 af15c1b1-3010-417e-b628-4374ebc0bcbd
2010-02-17 15:04:50 +00:00
pr_global_struct - > mapname = PR_SetEngineString ( sv . name ) ;
2010-02-15 23:26:55 +00:00
// serverflags are for cross level information (sigils)
pr_global_struct - > serverflags = svs . serverflags ;
2018-05-01 00:35:14 +00:00
ED_LoadFromFile ( qcvm - > worldmodel - > entities ) ;
2010-02-15 23:26:55 +00:00
sv . active = true ;
// all setup is completed, any further precache statements are errors
sv . state = ss_active ;
// run two frames to allow everything to settle
host_frametime = 0.1 ;
SV_Physics ( ) ;
SV_Physics ( ) ;
// create a baseline for more efficient communications
SV_CreateBaseline ( ) ;
//johnfitz -- warn if signon buffer larger than standard server can handle
if ( sv . signon . cursize > 8000 - 2 ) //max size that will fit into 8000-sized client->message buffer with 2 extra bytes on the end
2017-03-30 06:26:16 +00:00
Con_DWarning ( " %i byte signon buffer exceeds standard limit of 7998 (max = %d). \n " , sv . signon . cursize , sv . signon . maxsize ) ;
2010-02-15 23:26:55 +00:00
//johnfitz
// send serverinfo to all connected clients
for ( i = 0 , host_client = svs . clients ; i < svs . maxclients ; i + + , host_client + + )
2018-05-01 00:35:14 +00:00
{
host_client - > knowntoqc = false ;
2010-02-15 23:26:55 +00:00
if ( host_client - > active )
SV_SendServerinfo ( host_client ) ;
2018-05-01 00:35:14 +00:00
}
2010-02-15 23:26:55 +00:00
Con_DPrintf ( " Server spawned. \n " ) ;
}