2008-05-18 22:00:00 +02:00
|
|
|
|
//=======================================================================
|
|
|
|
|
// Copyright XashXT Group 2007 <20>
|
2009-11-23 22:00:00 +01:00
|
|
|
|
// sv_main.c - server main loop
|
2008-05-18 22:00:00 +02:00
|
|
|
|
//=======================================================================
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-06-09 22:00:00 +02:00
|
|
|
|
#include "common.h"
|
2007-06-21 22:00:00 +02:00
|
|
|
|
#include "server.h"
|
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
#define HEARTBEAT_SECONDS 300.0 // 300 seconds
|
2009-09-17 22:00:00 +02:00
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
netadr_t master_adr[MAX_MASTERS]; // address of group servers
|
|
|
|
|
|
2010-06-16 22:00:00 +02:00
|
|
|
|
cvar_t *sv_zmax;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
cvar_t *sv_pausable;
|
2010-03-24 22:00:00 +01:00
|
|
|
|
cvar_t *sv_newunit;
|
2010-06-16 22:00:00 +02:00
|
|
|
|
cvar_t *sv_wateramp;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
cvar_t *timeout; // seconds without any message
|
|
|
|
|
cvar_t *zombietime; // seconds to sink messages after disconnect
|
|
|
|
|
cvar_t *rcon_password; // password for remote server commands
|
|
|
|
|
cvar_t *allow_download;
|
2007-09-02 22:00:00 +02:00
|
|
|
|
cvar_t *sv_airaccelerate;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
cvar_t *sv_wateraccelerate;
|
2009-09-13 22:00:00 +02:00
|
|
|
|
cvar_t *sv_idealpitchscale;
|
2007-09-02 22:00:00 +02:00
|
|
|
|
cvar_t *sv_maxvelocity;
|
|
|
|
|
cvar_t *sv_gravity;
|
2008-07-30 22:00:00 +02:00
|
|
|
|
cvar_t *sv_stepheight;
|
2010-03-30 22:00:00 +02:00
|
|
|
|
cvar_t *sv_noreload; // don't reload level state when reentering
|
2008-07-30 22:00:00 +02:00
|
|
|
|
cvar_t *sv_rollangle;
|
|
|
|
|
cvar_t *sv_rollspeed;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
cvar_t *sv_wallbounce;
|
2008-07-30 22:00:00 +02:00
|
|
|
|
cvar_t *sv_maxspeed;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
cvar_t *sv_spectatormaxspeed;
|
2008-07-30 22:00:00 +02:00
|
|
|
|
cvar_t *sv_accelerate;
|
|
|
|
|
cvar_t *sv_friction;
|
2009-11-02 22:00:00 +01:00
|
|
|
|
cvar_t *sv_edgefriction;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
cvar_t *sv_waterfriction;
|
2009-11-30 22:00:00 +01:00
|
|
|
|
cvar_t *sv_synchthink;
|
2009-11-02 22:00:00 +01:00
|
|
|
|
cvar_t *sv_stopspeed;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
cvar_t *hostname;
|
2009-09-25 22:00:00 +02:00
|
|
|
|
cvar_t *sv_maxclients;
|
2009-11-29 22:00:00 +01:00
|
|
|
|
cvar_t *sv_check_errors;
|
2010-03-30 22:00:00 +02:00
|
|
|
|
cvar_t *public_server; // should heartbeats be sent
|
|
|
|
|
cvar_t *sv_reconnect_limit; // minimum seconds between connect messages
|
|
|
|
|
cvar_t *serverinfo;
|
2010-02-07 22:00:00 +01:00
|
|
|
|
cvar_t *physinfo;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-16 22:00:00 +02:00
|
|
|
|
// sky variables
|
|
|
|
|
cvar_t *sv_skycolor_r;
|
|
|
|
|
cvar_t *sv_skycolor_g;
|
|
|
|
|
cvar_t *sv_skycolor_b;
|
|
|
|
|
cvar_t *sv_skyvec_x;
|
|
|
|
|
cvar_t *sv_skyvec_y;
|
|
|
|
|
cvar_t *sv_skyvec_z;
|
2010-06-23 22:00:00 +02:00
|
|
|
|
cvar_t *sv_skyname;
|
2010-06-16 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
void Master_Shutdown( void );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===================
|
|
|
|
|
SV_CalcPings
|
|
|
|
|
|
|
|
|
|
Updates the cl->ping variables
|
|
|
|
|
===================
|
|
|
|
|
*/
|
2008-08-01 22:00:00 +02:00
|
|
|
|
void SV_CalcPings( void )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-31 22:00:00 +02:00
|
|
|
|
int i, j;
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
2008-07-31 22:00:00 +02:00
|
|
|
|
int total, count;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2009-09-16 22:00:00 +02:00
|
|
|
|
// clamp fps counter
|
2009-09-25 22:00:00 +02:00
|
|
|
|
for( i = 0; i < sv_maxclients->integer; i++ )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
cl = &svs.clients[i];
|
2009-09-16 22:00:00 +02:00
|
|
|
|
if( cl->state != cs_spawned ) continue;
|
2009-09-15 22:00:00 +02:00
|
|
|
|
|
2009-09-16 22:00:00 +02:00
|
|
|
|
total = count = 0;
|
2009-11-25 22:00:00 +01:00
|
|
|
|
|
2010-04-02 22:00:00 +02:00
|
|
|
|
for( j = 0; j < (SV_UPDATE_BACKUP / 2); j++ )
|
2009-09-15 22:00:00 +02:00
|
|
|
|
{
|
2009-11-25 22:00:00 +01:00
|
|
|
|
client_frame_t *frame;
|
|
|
|
|
|
2010-04-02 22:00:00 +02:00
|
|
|
|
frame = &cl->frames[(cl->netchan.incoming_acknowledged - 1 - j) & SV_UPDATE_MASK];
|
2009-11-25 22:00:00 +01:00
|
|
|
|
if( frame->latency > 0 )
|
2009-09-16 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
count++;
|
2009-11-25 22:00:00 +01:00
|
|
|
|
total += frame->latency;
|
2009-09-16 22:00:00 +02:00
|
|
|
|
}
|
2009-09-15 22:00:00 +02:00
|
|
|
|
}
|
2009-11-25 22:00:00 +01:00
|
|
|
|
|
2009-09-16 22:00:00 +02:00
|
|
|
|
if( !count ) cl->ping = 0;
|
|
|
|
|
else cl->ping = total / count;
|
2009-11-25 22:00:00 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===================
|
|
|
|
|
SV_CalcPacketLoss
|
|
|
|
|
|
|
|
|
|
determine % of packets that were not ack'd.
|
|
|
|
|
===================
|
|
|
|
|
*/
|
|
|
|
|
int SV_CalcPacketLoss( sv_client_t *cl )
|
|
|
|
|
{
|
|
|
|
|
int i, lost, count;
|
|
|
|
|
float losspercent;
|
|
|
|
|
register client_frame_t *frame;
|
|
|
|
|
int numsamples;
|
|
|
|
|
|
|
|
|
|
lost = 0;
|
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
|
|
if( cl->edict->v.flags & FL_FAKECLIENT )
|
|
|
|
|
return 0;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-04-02 22:00:00 +02:00
|
|
|
|
numsamples = SV_UPDATE_BACKUP / 2;
|
2009-11-25 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
for( i = 0; i < numsamples; i++ )
|
|
|
|
|
{
|
2010-04-02 22:00:00 +02:00
|
|
|
|
frame = &cl->frames[(cl->netchan.incoming_acknowledged - 1 - i) & SV_UPDATE_MASK];
|
2009-11-25 22:00:00 +01:00
|
|
|
|
count++;
|
|
|
|
|
if( frame->latency == -1 )
|
|
|
|
|
lost++;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2009-11-25 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
if( !count ) return 100;
|
|
|
|
|
losspercent = 100.0 * ( float )lost / ( float )count;
|
|
|
|
|
|
|
|
|
|
return (int)losspercent;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-11-10 22:00:00 +01:00
|
|
|
|
/*
|
|
|
|
|
===================
|
|
|
|
|
SV_UpdateMovevars
|
|
|
|
|
|
|
|
|
|
check movevars for changes every frame
|
|
|
|
|
send updates to client if changed
|
|
|
|
|
===================
|
|
|
|
|
*/
|
|
|
|
|
void SV_UpdateMovevars( void )
|
|
|
|
|
{
|
|
|
|
|
static int oldserverflags = 0;
|
2010-06-16 22:00:00 +02:00
|
|
|
|
string tmp;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
if( svgame.globals->serverflags != oldserverflags )
|
|
|
|
|
{
|
|
|
|
|
// update serverflags
|
|
|
|
|
SV_ConfigString( CS_SERVERFLAGS, va( "%i", svgame.globals->serverflags ));
|
|
|
|
|
oldserverflags = svgame.globals->serverflags;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-16 22:00:00 +02:00
|
|
|
|
if( sv_zmax->modified )
|
|
|
|
|
{
|
|
|
|
|
SV_ConfigString( CS_ZFAR, sv_zmax->string );
|
|
|
|
|
sv_zmax->modified = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( sv_wateramp->modified )
|
|
|
|
|
{
|
|
|
|
|
SV_ConfigString( CS_WATERAMP, sv_wateramp->string );
|
|
|
|
|
sv_wateramp->modified = false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-23 22:00:00 +02:00
|
|
|
|
if( sv_skyname->modified )
|
|
|
|
|
{
|
|
|
|
|
SV_ConfigString( CS_SKYNAME, sv_skyname->string );
|
|
|
|
|
sv_skyname->modified = false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-16 22:00:00 +02:00
|
|
|
|
if( sv_skycolor_r->modified || sv_skycolor_g->modified || sv_skycolor_g->modified )
|
|
|
|
|
{
|
|
|
|
|
com.snprintf( tmp, sizeof( tmp ), "%d %d %d", sv_skycolor_r->integer, sv_skycolor_g->integer, sv_skycolor_b->integer );
|
|
|
|
|
sv_skycolor_r->modified = sv_skycolor_g->modified = sv_skycolor_g->modified = false;
|
|
|
|
|
SV_ConfigString( CS_SKYCOLOR, tmp );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( sv_skyvec_x->modified || sv_skyvec_y->modified || sv_skyvec_z->modified )
|
|
|
|
|
{
|
|
|
|
|
com.snprintf( tmp, sizeof( tmp ), "%f %f %f", sv_skyvec_x->value, sv_skyvec_y->value, sv_skyvec_z->value );
|
|
|
|
|
sv_skyvec_x->modified = sv_skyvec_y->modified = sv_skyvec_z->modified = false;
|
|
|
|
|
SV_ConfigString( CS_SKYVEC, tmp );
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-07 22:00:00 +01:00
|
|
|
|
if( !physinfo->modified ) return;
|
|
|
|
|
|
2009-11-10 22:00:00 +01:00
|
|
|
|
svgame.movevars.gravity = sv_gravity->value;
|
|
|
|
|
svgame.movevars.stopspeed = sv_stopspeed->value;
|
|
|
|
|
svgame.movevars.maxspeed = sv_maxspeed->value;
|
|
|
|
|
svgame.movevars.spectatormaxspeed = sv_spectatormaxspeed->value;
|
|
|
|
|
svgame.movevars.accelerate = sv_accelerate->value;
|
|
|
|
|
svgame.movevars.airaccelerate = sv_airaccelerate->value;
|
|
|
|
|
svgame.movevars.wateraccelerate = sv_wateraccelerate->value;
|
|
|
|
|
svgame.movevars.friction = sv_friction->value;
|
|
|
|
|
svgame.movevars.edgefriction = sv_edgefriction->value;
|
|
|
|
|
svgame.movevars.waterfriction = sv_waterfriction->value;
|
|
|
|
|
svgame.movevars.bounce = sv_wallbounce->value;
|
|
|
|
|
svgame.movevars.stepsize = sv_stepheight->value;
|
|
|
|
|
svgame.movevars.maxvelocity = sv_maxvelocity->value;
|
|
|
|
|
svgame.movevars.footsteps = Cvar_VariableInteger( "mp_footsteps" );
|
|
|
|
|
svgame.movevars.rollangle = sv_rollangle->value;
|
|
|
|
|
svgame.movevars.rollspeed = sv_rollspeed->value;
|
|
|
|
|
|
|
|
|
|
MSG_Clear( &sv.multicast );
|
|
|
|
|
|
|
|
|
|
if( MSG_WriteDeltaMovevars( &sv.multicast, &svgame.oldmovevars, &svgame.movevars ))
|
|
|
|
|
{
|
2009-11-25 22:00:00 +01:00
|
|
|
|
MSG_Send( MSG_ALL, vec3_origin, NULL );
|
2009-11-10 22:00:00 +01:00
|
|
|
|
Mem_Copy( &svgame.oldmovevars, &svgame.movevars, sizeof( movevars_t )); // oldstate changed
|
|
|
|
|
}
|
2010-02-07 22:00:00 +01:00
|
|
|
|
physinfo->modified = false;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-30 22:00:00 +02:00
|
|
|
|
void pfnUpdateServerInfo( const char *szKey, const char *szValue, const char *unused, void *unused2 )
|
|
|
|
|
{
|
|
|
|
|
cvar_t *cv = Cvar_FindVar( szKey );
|
|
|
|
|
|
|
|
|
|
if( !cv || !cv->modified ) return; // this cvar not changed
|
|
|
|
|
|
|
|
|
|
MSG_WriteByte( &sv.multicast, svc_serverinfo );
|
|
|
|
|
MSG_WriteString( &sv.multicast, szKey );
|
|
|
|
|
MSG_WriteString( &sv.multicast, szValue );
|
|
|
|
|
MSG_Send( MSG_ALL, vec3_origin, NULL );
|
|
|
|
|
cv->modified = false; // reset state
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SV_UpdateServerInfo( void )
|
|
|
|
|
{
|
|
|
|
|
if( !serverinfo->modified ) return;
|
|
|
|
|
|
|
|
|
|
Cvar_LookupVars( CVAR_SERVERINFO, NULL, NULL, pfnUpdateServerInfo );
|
|
|
|
|
|
|
|
|
|
serverinfo->modified = false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-24 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
SV_ReadPackets
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
void SV_ReadPackets( void )
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
sv_client_t *cl;
|
|
|
|
|
int qport;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2009-06-24 22:00:00 +02:00
|
|
|
|
while( NET_GetPacket( NS_SERVER, &net_from, &net_message ))
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2009-06-24 22:00:00 +02:00
|
|
|
|
// check for connectionless packet (0xffffffff) first
|
|
|
|
|
if( net_message.cursize >= 4 && *(int *)net_message.data == -1 )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2009-06-24 22:00:00 +02:00
|
|
|
|
SV_ConnectionlessPacket( net_from, &net_message );
|
|
|
|
|
continue;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2009-06-24 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// read the qport out of the message so we can fix up
|
|
|
|
|
// stupid address translating routers
|
|
|
|
|
MSG_BeginReading( &net_message );
|
|
|
|
|
MSG_ReadLong( &net_message ); // sequence number
|
|
|
|
|
MSG_ReadLong( &net_message ); // sequence number
|
|
|
|
|
qport = (int)MSG_ReadShort( &net_message ) & 0xffff;
|
|
|
|
|
|
|
|
|
|
// check for packets from connected clients
|
2009-09-25 22:00:00 +02:00
|
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
2009-06-24 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
if( cl->state == cs_free ) continue;
|
|
|
|
|
if( cl->edict && (cl->edict->v.flags & FL_FAKECLIENT )) continue;
|
|
|
|
|
if( !NET_CompareBaseAdr( net_from, cl->netchan.remote_address )) continue;
|
|
|
|
|
if( cl->netchan.qport != qport ) continue;
|
|
|
|
|
if( cl->netchan.remote_address.port != net_from.port )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2009-06-24 22:00:00 +02:00
|
|
|
|
MsgDev( D_INFO, "SV_ReadPackets: fixing up a translated port\n");
|
|
|
|
|
cl->netchan.remote_address.port = net_from.port;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2010-04-03 22:00:00 +02:00
|
|
|
|
|
2009-06-24 22:00:00 +02:00
|
|
|
|
if( Netchan_Process( &cl->netchan, &net_message ))
|
|
|
|
|
{
|
2010-04-03 22:00:00 +02:00
|
|
|
|
cl->send_message = true; // reply at end of frame
|
|
|
|
|
|
2009-06-24 22:00:00 +02:00
|
|
|
|
// this is a valid, sequenced packet, so process it
|
|
|
|
|
if( cl->state != cs_zombie )
|
|
|
|
|
{
|
2010-06-20 22:00:00 +02:00
|
|
|
|
cl->lastmessage = host.realtime; // don't timeout
|
2009-06-24 22:00:00 +02:00
|
|
|
|
SV_ExecuteClientMessage( cl, &net_message );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2010-06-20 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if( i != sv_maxclients->integer )
|
|
|
|
|
continue;
|
2009-06-22 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
SV_CheckTimeouts
|
|
|
|
|
|
|
|
|
|
If a packet has not been received from a client for timeout->value
|
|
|
|
|
seconds, drop the conneciton. Server frames are used instead of
|
|
|
|
|
realtime to avoid dropping the local client while debugging.
|
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
When a client is normally dropped, the sv_client_t goes into a zombie state
|
2007-06-21 22:00:00 +02:00
|
|
|
|
for a few seconds to make sure any final reliable message gets resent
|
|
|
|
|
if necessary
|
|
|
|
|
==================
|
|
|
|
|
*/
|
2008-07-12 22:00:00 +02:00
|
|
|
|
void SV_CheckTimeouts( void )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
2008-07-12 22:00:00 +02:00
|
|
|
|
float droppoint;
|
|
|
|
|
float zombiepoint;
|
2009-11-10 22:00:00 +01:00
|
|
|
|
int i, numclients = 0;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
droppoint = host.realtime - timeout->value;
|
|
|
|
|
zombiepoint = host.realtime - zombietime->value;
|
2008-07-31 22:00:00 +02:00
|
|
|
|
|
2009-09-25 22:00:00 +02:00
|
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2009-11-10 22:00:00 +01:00
|
|
|
|
if( cl->state >= cs_connected )
|
|
|
|
|
{
|
|
|
|
|
if( cl->edict && !( cl->edict->v.flags & (FL_SPECTATOR|FL_FAKECLIENT)))
|
|
|
|
|
numclients++;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-24 22:00:00 +02:00
|
|
|
|
// fake clients do not timeout
|
2009-11-10 22:00:00 +01:00
|
|
|
|
if( cl->edict && (cl->edict->v.flags & FL_FAKECLIENT ))
|
2010-06-20 22:00:00 +02:00
|
|
|
|
cl->lastmessage = host.realtime;
|
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
// message times may be wrong across a changelevel
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if( cl->lastmessage > host.realtime )
|
|
|
|
|
cl->lastmessage = host.realtime;
|
|
|
|
|
|
2008-07-12 22:00:00 +02:00
|
|
|
|
if( cl->state == cs_zombie && cl->lastmessage < zombiepoint )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-12 22:00:00 +02:00
|
|
|
|
cl->state = cs_free; // can now be reused
|
2007-06-21 22:00:00 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2010-06-17 22:00:00 +02:00
|
|
|
|
if(( cl->state == cs_connected || cl->state == cs_spawned ) && cl->lastmessage < droppoint )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2009-06-24 22:00:00 +02:00
|
|
|
|
SV_BroadcastPrintf( PRINT_HIGH, "%s timed out\n", cl->name );
|
2008-07-12 22:00:00 +02:00
|
|
|
|
SV_DropClient( cl );
|
|
|
|
|
cl->state = cs_free; // don't bother with zombie state
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-11-10 22:00:00 +01:00
|
|
|
|
|
|
|
|
|
if( sv.paused && !numclients )
|
|
|
|
|
{
|
|
|
|
|
// nobody left, unpause the server
|
|
|
|
|
SV_TogglePause( "Pause released since no players are left.\n" );
|
|
|
|
|
}
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-14 22:00:00 +01:00
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
SV_PrepWorldFrame
|
|
|
|
|
|
|
|
|
|
This has to be done before the world logic, because
|
|
|
|
|
player processing happens outside RunWorldFrame
|
|
|
|
|
================
|
|
|
|
|
*/
|
|
|
|
|
void SV_PrepWorldFrame( void )
|
|
|
|
|
{
|
|
|
|
|
edict_t *ent;
|
|
|
|
|
int i;
|
|
|
|
|
|
2010-03-15 22:00:00 +01:00
|
|
|
|
for( i = 1; i < svgame.globals->numEntities; i++ )
|
2009-01-14 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
ent = EDICT_NUM( i );
|
|
|
|
|
if( ent->free ) continue;
|
2010-04-03 22:00:00 +02:00
|
|
|
|
|
2009-01-14 22:00:00 +01:00
|
|
|
|
ent->pvServerData->s.ed_flags = 0;
|
2010-03-15 22:00:00 +01:00
|
|
|
|
ent->v.effects &= ~EF_MUZZLEFLASH;
|
2010-04-03 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// clear NOINTERP flag automatically only for alive creatures
|
2010-06-16 22:00:00 +02:00
|
|
|
|
if( ent->v.flags & ( FL_MONSTER|FL_CLIENT|FL_FAKECLIENT ) && ent->v.deadflag < DEAD_DEAD )
|
2010-04-03 22:00:00 +02:00
|
|
|
|
ent->v.effects &= ~EF_NOINTERP;
|
2009-01-14 22:00:00 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-07-31 22:00:00 +02:00
|
|
|
|
|
2010-04-02 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
SV_HasActivePlayers
|
|
|
|
|
|
|
|
|
|
returns true if server have spawned players
|
|
|
|
|
================
|
|
|
|
|
*/
|
|
|
|
|
bool SV_HasActivePlayers( void )
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
// server inactive
|
|
|
|
|
if( !svs.clients ) return false;
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < sv_maxclients->integer; i++ )
|
|
|
|
|
{
|
|
|
|
|
if( svs.clients[i].state == cs_spawned )
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
SV_RunGameFrame
|
|
|
|
|
=================
|
|
|
|
|
*/
|
2009-02-02 22:00:00 +01:00
|
|
|
|
void SV_RunGameFrame( void )
|
2007-09-06 22:00:00 +02:00
|
|
|
|
{
|
2010-04-03 22:00:00 +02:00
|
|
|
|
if( !SV_HasActivePlayers()) return;
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if( sv.frametime ) SV_Physics();
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==================
|
2010-06-20 22:00:00 +02:00
|
|
|
|
Host_ServerFrame
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
==================
|
|
|
|
|
*/
|
2010-06-20 22:00:00 +02:00
|
|
|
|
void Host_ServerFrame( void )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
// if server is not active, do nothing
|
2008-07-03 22:00:00 +02:00
|
|
|
|
if( !svs.initialized ) return;
|
2009-02-03 22:00:00 +01:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
// advances servertime
|
2010-06-22 22:00:00 +02:00
|
|
|
|
if( !sv.paused && CL_IsInGame( ) && !sv.loadgame )
|
2010-06-20 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
if(!( sv.hostflags & SVF_PLAYERSONLY ))
|
|
|
|
|
sv.time += host.frametime;
|
|
|
|
|
sv.frametime = host.frametime;
|
|
|
|
|
}
|
|
|
|
|
else sv.frametime = 0;
|
2010-04-12 22:00:00 +02:00
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
// check timeouts
|
|
|
|
|
SV_CheckTimeouts ();
|
2007-09-06 22:00:00 +02:00
|
|
|
|
|
2009-06-22 22:00:00 +02:00
|
|
|
|
// read packets from clients
|
|
|
|
|
SV_ReadPackets ();
|
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
// update ping based on the last known frame from all clients
|
|
|
|
|
SV_CalcPings ();
|
|
|
|
|
|
2010-06-24 22:00:00 +02:00
|
|
|
|
// let everything in the world think and move
|
|
|
|
|
SV_RunGameFrame ();
|
|
|
|
|
|
2010-03-30 22:00:00 +02:00
|
|
|
|
// refresh serverinfo on the client side
|
|
|
|
|
SV_UpdateServerInfo ();
|
|
|
|
|
|
2009-11-10 22:00:00 +01:00
|
|
|
|
// refresh physic movevars on the client side
|
|
|
|
|
SV_UpdateMovevars ();
|
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
// send messages back to the clients that had packets read this frame
|
|
|
|
|
SV_SendClientMessages ();
|
|
|
|
|
|
2009-01-14 22:00:00 +01:00
|
|
|
|
// clear edict flags for next frame
|
|
|
|
|
SV_PrepWorldFrame ();
|
2010-04-12 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// send a heartbeat to the master if needed
|
|
|
|
|
Master_Heartbeat ();
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
Master_Heartbeat
|
|
|
|
|
|
|
|
|
|
Send a message to the master every few minutes to
|
|
|
|
|
let it know we are alive, and log information
|
|
|
|
|
================
|
|
|
|
|
*/
|
2008-12-20 22:00:00 +01:00
|
|
|
|
void Master_Heartbeat( void )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-12-20 22:00:00 +01:00
|
|
|
|
char *string;
|
|
|
|
|
int i;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if( host.type != HOST_DEDICATED || !public_server->integer )
|
|
|
|
|
return; // only dedicated servers send heartbeats
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// check for time wraparound
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if( svs.last_heartbeat > host.realtime )
|
|
|
|
|
svs.last_heartbeat = host.realtime;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if(( host.realtime - svs.last_heartbeat ) < HEARTBEAT_SECONDS )
|
2009-02-03 22:00:00 +01:00
|
|
|
|
return; // not time to send yet
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
svs.last_heartbeat = host.realtime;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// send the same string that we would give for a status OOB command
|
2010-06-20 22:00:00 +02:00
|
|
|
|
string = SV_StatusString( );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// send to group master
|
2009-02-03 22:00:00 +01:00
|
|
|
|
for( i = 0; i < MAX_MASTERS; i++ )
|
2007-09-09 22:00:00 +02:00
|
|
|
|
{
|
2009-02-03 22:00:00 +01:00
|
|
|
|
if( master_adr[i].port )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2010-06-20 22:00:00 +02:00
|
|
|
|
MsgDev( D_INFO, "Sending heartbeat to %s\n", NET_AdrToString( master_adr[i] ));
|
2009-02-03 22:00:00 +01:00
|
|
|
|
Netchan_OutOfBandPrint( NS_SERVER, master_adr[i], "heartbeat\n%s", string );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2007-09-09 22:00:00 +02:00
|
|
|
|
}
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
Master_Shutdown
|
|
|
|
|
|
|
|
|
|
Informs all masters that this server is going down
|
|
|
|
|
=================
|
|
|
|
|
*/
|
2010-06-20 22:00:00 +02:00
|
|
|
|
void Master_Shutdown( void )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2010-06-20 22:00:00 +02:00
|
|
|
|
int i;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if( host.type != HOST_DEDICATED || !public_server->integer )
|
|
|
|
|
return; // only dedicated servers send heartbeats
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// send to group master
|
2010-06-20 22:00:00 +02:00
|
|
|
|
for( i = 0; i < MAX_MASTERS; i++ )
|
2008-01-13 22:00:00 +01:00
|
|
|
|
{
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if( master_adr[i].port )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2010-06-20 22:00:00 +02:00
|
|
|
|
if( i ) MsgDev( D_INFO, "Sending heartbeat to %s\n", NET_AdrToString( master_adr[i] ));
|
2008-01-13 22:00:00 +01:00
|
|
|
|
Netchan_OutOfBandPrint( NS_SERVER, master_adr[i], "shutdown" );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2008-01-13 22:00:00 +01:00
|
|
|
|
}
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===============
|
|
|
|
|
SV_Init
|
|
|
|
|
|
2008-12-15 22:00:00 +01:00
|
|
|
|
Only called at startup, not for each game
|
2007-06-21 22:00:00 +02:00
|
|
|
|
===============
|
|
|
|
|
*/
|
2008-08-04 22:00:00 +02:00
|
|
|
|
void SV_Init( void )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2007-12-11 22:00:00 +01:00
|
|
|
|
SV_InitOperatorCommands();
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2010-03-24 22:00:00 +01:00
|
|
|
|
Cvar_Get ("skill", "1", CVAR_LATCH, "game skill level" );
|
2009-01-09 22:00:00 +01:00
|
|
|
|
Cvar_Get ("deathmatch", "0", CVAR_SERVERINFO|CVAR_LATCH, "displays deathmatch state" );
|
|
|
|
|
Cvar_Get ("teamplay", "0", CVAR_SERVERINFO|CVAR_LATCH, "displays teamplay state" );
|
|
|
|
|
Cvar_Get ("coop", "0", CVAR_SERVERINFO|CVAR_LATCH, "displays cooperative state" );
|
2008-07-16 22:00:00 +02:00
|
|
|
|
Cvar_Get ("dmflags", "0", CVAR_SERVERINFO, "setup deathmatch flags" );
|
2008-06-29 22:00:00 +02:00
|
|
|
|
Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO, "multiplayer fraglimit" );
|
|
|
|
|
Cvar_Get ("timelimit", "0", CVAR_SERVERINFO, "multiplayer timelimit" );
|
|
|
|
|
Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_INIT, "displays server protocol version" );
|
2010-01-30 22:00:00 +01:00
|
|
|
|
Cvar_Get ("defaultmap", "", 0, "holds the multiplayer mapname" );
|
2010-06-11 22:00:00 +02:00
|
|
|
|
Cvar_Get ("showtriggers", "0", CVAR_LATCH, "debug cvar shows triggers" );
|
2009-01-23 22:00:00 +01:00
|
|
|
|
Cvar_Get ("sv_aim", "1", 0, "enable auto-aiming" );
|
2008-07-03 22:00:00 +02:00
|
|
|
|
|
2010-06-16 22:00:00 +02:00
|
|
|
|
// half-life shared variables
|
|
|
|
|
sv_zmax = Cvar_Get ("sv_zmax", "0", 0, "zfar server value" );
|
|
|
|
|
sv_wateramp = Cvar_Get ("sv_wateramp", "0", 0, "global water wave height" );
|
|
|
|
|
sv_skycolor_r = Cvar_Get ("sv_skycolor_r", "127", 0, "skycolor red (hl1 compatibility)" );
|
|
|
|
|
sv_skycolor_g = Cvar_Get ("sv_skycolor_g", "127", 0, "skycolor green (hl1 compatibility)" );
|
|
|
|
|
sv_skycolor_b = Cvar_Get ("sv_skycolor_b", "127", 0, "skycolor blue (hl1 compatibility)" );
|
|
|
|
|
sv_skyvec_x = Cvar_Get ("sv_skyvec_x", "1", 0, "sky direction x (hl1 compatibility)" );
|
|
|
|
|
sv_skyvec_y = Cvar_Get ("sv_skyvec_y", "0", 0, "sky direction y (hl1 compatibility)" );
|
|
|
|
|
sv_skyvec_z = Cvar_Get ("sv_skyvec_z", "-1", 0, "sky direction z (hl1 compatibility)" );
|
2010-06-23 22:00:00 +02:00
|
|
|
|
sv_skyname = Cvar_Get ("sv_skyname", "", 0, "skybox name (can be dynamically changed in-game)" );
|
2010-03-24 22:00:00 +01:00
|
|
|
|
|
2010-06-20 22:00:00 +02:00
|
|
|
|
rcon_password = Cvar_Get( "rcon_password", "", 0, "remote connect password" );
|
2010-02-07 22:00:00 +01:00
|
|
|
|
sv_stepheight = Cvar_Get( "sv_stepheight", "18", CVAR_ARCHIVE|CVAR_PHYSICINFO, "how high you can step up" );
|
2010-03-24 22:00:00 +01:00
|
|
|
|
sv_newunit = Cvar_Get( "sv_newunit", "0", 0, "sets to 1 while new unit is loading" );
|
2009-11-10 22:00:00 +01:00
|
|
|
|
hostname = Cvar_Get( "sv_hostname", "unnamed", CVAR_SERVERINFO | CVAR_ARCHIVE, "host name" );
|
|
|
|
|
timeout = Cvar_Get( "timeout", "125", 0, "connection timeout" );
|
|
|
|
|
zombietime = Cvar_Get( "zombietime", "2", 0, "timeout for clients-zombie (who died but not respawned)" );
|
|
|
|
|
sv_pausable = Cvar_Get( "pausable", "1", 0, "allow players to pause or not" );
|
2009-11-23 22:00:00 +01:00
|
|
|
|
allow_download = Cvar_Get( "allow_download", "0", CVAR_ARCHIVE, "allow download resources" );
|
2009-01-22 22:00:00 +01:00
|
|
|
|
sv_noreload = Cvar_Get( "sv_noreload", "0", 0, "ignore savepoints for singleplayer" );
|
2010-02-07 22:00:00 +01:00
|
|
|
|
sv_wallbounce = Cvar_Get( "sv_wallbounce", "1.0", CVAR_PHYSICINFO, "bounce factor for client with MOVETYPE_BOUNCE" );
|
|
|
|
|
sv_spectatormaxspeed = Cvar_Get( "sv_spectatormaxspeed", "500", CVAR_PHYSICINFO, "spectator maxspeed" );
|
|
|
|
|
sv_waterfriction = Cvar_Get( "sv_waterfriction", "4", CVAR_PHYSICINFO, "how fast you slow down in water" );
|
|
|
|
|
sv_wateraccelerate = Cvar_Get( "sv_wateraccelerate", "10", CVAR_PHYSICINFO, "rate at which a player accelerates to sv_maxspeed while in the water" );
|
|
|
|
|
sv_rollangle = Cvar_Get( "sv_rollangle", "2", CVAR_PHYSICINFO, "how much to tilt the view when strafing" );
|
|
|
|
|
sv_rollspeed = Cvar_Get( "sv_rollspeed", "200", CVAR_PHYSICINFO, "how much strafing is necessary to tilt the view" );
|
|
|
|
|
sv_airaccelerate = Cvar_Get("sv_airaccelerate", "1", CVAR_PHYSICINFO, "player accellerate in air" );
|
2009-09-13 22:00:00 +02:00
|
|
|
|
sv_idealpitchscale = Cvar_Get( "sv_idealpitchscale", "0.8", 0, "how much to look up/down slopes and stairs when not using freelook" );
|
2010-03-24 22:00:00 +01:00
|
|
|
|
sv_maxvelocity = Cvar_Get( "sv_maxvelocity", "2000", CVAR_PHYSICINFO, "max world velocity" );
|
2010-02-07 22:00:00 +01:00
|
|
|
|
sv_gravity = Cvar_Get( "sv_gravity", "800", CVAR_PHYSICINFO, "world gravity" );
|
|
|
|
|
sv_maxspeed = Cvar_Get( "sv_maxspeed", "320", CVAR_PHYSICINFO, "maximum speed a player can accelerate to when on ground");
|
|
|
|
|
sv_accelerate = Cvar_Get( "sv_accelerate", "10", CVAR_PHYSICINFO, "rate at which a player accelerates to sv_maxspeed" );
|
|
|
|
|
sv_friction = Cvar_Get( "sv_friction", "4", CVAR_PHYSICINFO, "how fast you slow down" );
|
|
|
|
|
sv_edgefriction = Cvar_Get( "sv_edgefriction", "1", CVAR_PHYSICINFO, "how much you slow down when nearing a ledge you might fall off" );
|
|
|
|
|
sv_stopspeed = Cvar_Get( "sv_stopspeed", "100", CVAR_PHYSICINFO, "how fast you come to a complete stop" );
|
2009-09-25 22:00:00 +02:00
|
|
|
|
sv_maxclients = Cvar_Get( "sv_maxclients", "1", CVAR_SERVERINFO|CVAR_LATCH, "server clients limit" );
|
2009-12-12 22:00:00 +01:00
|
|
|
|
sv_check_errors = Cvar_Get( "sv_check_errors", "0", CVAR_ARCHIVE, "ignore physic engine errors" );
|
2009-11-30 22:00:00 +01:00
|
|
|
|
sv_synchthink = Cvar_Get( "sv_fast_think", "0", CVAR_ARCHIVE, "allows entities to think more often than the server framerate" );
|
2010-02-07 22:00:00 +01:00
|
|
|
|
physinfo = Cvar_Get( "@physinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
|
2010-03-30 22:00:00 +02:00
|
|
|
|
serverinfo = Cvar_Get( "@serverinfo", "0", CVAR_READ_ONLY, "" ); // use ->modified value only
|
2008-06-29 22:00:00 +02:00
|
|
|
|
public_server = Cvar_Get ("public", "0", 0, "change server type from private to public" );
|
2008-07-11 22:00:00 +02:00
|
|
|
|
sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE, "max reconnect attempts" );
|
2008-12-26 22:00:00 +01:00
|
|
|
|
|
2010-03-25 22:00:00 +01:00
|
|
|
|
SV_ClearSaveDir (); // delete all temporary *.hl files
|
2009-06-22 22:00:00 +02:00
|
|
|
|
MSG_Init( &net_message, net_message_buffer, sizeof( net_message_buffer ));
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
SV_FinalMessage
|
|
|
|
|
|
|
|
|
|
Used by SV_Shutdown to send a final message to all
|
|
|
|
|
connected clients before the server goes down. The messages are sent immediately,
|
|
|
|
|
not just stuck on the outgoing message list, because the server is going
|
|
|
|
|
to totally exit after returning from this function.
|
|
|
|
|
==================
|
|
|
|
|
*/
|
2008-08-02 22:00:00 +02:00
|
|
|
|
void SV_FinalMessage( char *message, bool reconnect )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
2008-07-12 22:00:00 +02:00
|
|
|
|
byte msg_buf[MAX_MSGLEN];
|
|
|
|
|
sizebuf_t msg;
|
|
|
|
|
int i;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-12-25 22:00:00 +01:00
|
|
|
|
MSG_Init( &msg, msg_buf, sizeof( msg_buf ));
|
2008-07-12 22:00:00 +02:00
|
|
|
|
MSG_WriteByte( &msg, svc_print );
|
2009-11-23 22:00:00 +01:00
|
|
|
|
MSG_WriteByte( &msg, PRINT_HIGH );
|
2008-07-12 22:00:00 +02:00
|
|
|
|
MSG_WriteString( &msg, message );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-12 22:00:00 +02:00
|
|
|
|
if( reconnect )
|
2007-08-01 22:00:00 +02:00
|
|
|
|
{
|
2010-03-28 22:00:00 +02:00
|
|
|
|
if( sv.loadgame )
|
|
|
|
|
MSG_WriteByte( &msg, svc_changing );
|
|
|
|
|
else MSG_WriteByte( &msg, svc_reconnect );
|
2007-08-01 22:00:00 +02:00
|
|
|
|
}
|
2007-06-21 22:00:00 +02:00
|
|
|
|
else
|
2007-08-01 22:00:00 +02:00
|
|
|
|
{
|
2008-07-12 22:00:00 +02:00
|
|
|
|
MSG_WriteByte( &msg, svc_disconnect );
|
2007-08-01 22:00:00 +02:00
|
|
|
|
}
|
2008-07-12 22:00:00 +02:00
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
// send it twice
|
|
|
|
|
// stagger the packets to crutch operating system limited buffers
|
2009-09-25 22:00:00 +02:00
|
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
2009-06-24 22:00:00 +02:00
|
|
|
|
if( cl->state >= cs_connected && !(cl->edict && cl->edict->v.flags & FL_FAKECLIENT ))
|
2008-07-12 22:00:00 +02:00
|
|
|
|
Netchan_Transmit( &cl->netchan, msg.cursize, msg.data );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2009-09-25 22:00:00 +02:00
|
|
|
|
for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ )
|
2009-06-24 22:00:00 +02:00
|
|
|
|
if( cl->state >= cs_connected && !(cl->edict && cl->edict->v.flags & FL_FAKECLIENT ))
|
2008-07-12 22:00:00 +02:00
|
|
|
|
Netchan_Transmit( &cl->netchan, msg.cursize, msg.data );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
SV_Shutdown
|
|
|
|
|
|
|
|
|
|
Called when each game quits,
|
|
|
|
|
before Sys_Quit or Sys_Error
|
|
|
|
|
================
|
|
|
|
|
*/
|
2007-11-21 22:00:00 +01:00
|
|
|
|
void SV_Shutdown( bool reconnect )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2007-10-29 22:00:00 +01:00
|
|
|
|
// already freed
|
2008-08-04 22:00:00 +02:00
|
|
|
|
if( host.state == HOST_ERROR ) return;
|
2009-09-25 22:00:00 +02:00
|
|
|
|
if( !SV_Active()) return;
|
2007-10-29 22:00:00 +01:00
|
|
|
|
|
2008-08-02 22:00:00 +02:00
|
|
|
|
MsgDev( D_INFO, "SV_Shutdown: %s\n", host.finalmsg );
|
2008-12-15 22:00:00 +01:00
|
|
|
|
if( svs.clients ) SV_FinalMessage( host.finalmsg, reconnect );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-11-21 22:00:00 +01:00
|
|
|
|
Master_Shutdown();
|
2009-09-28 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if( !reconnect ) SV_UnloadProgs ();
|
|
|
|
|
else SV_DeactivateServer ();
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// free current level
|
2008-12-15 22:00:00 +01:00
|
|
|
|
Mem_Set( &sv, 0, sizeof( sv ));
|
2008-08-04 22:00:00 +02:00
|
|
|
|
Host_SetServerState( sv.state );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// free server static data
|
2009-11-03 22:00:00 +01:00
|
|
|
|
if( svs.clients ) Z_Free( svs.clients );
|
|
|
|
|
if( svs.baselines ) Z_Free( svs.baselines );
|
|
|
|
|
if( svs.client_entities ) Z_Free( svs.client_entities );
|
2008-12-15 22:00:00 +01:00
|
|
|
|
Mem_Set( &svs, 0, sizeof( svs ));
|
2009-01-02 22:00:00 +01:00
|
|
|
|
}
|