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

676 lines
21 KiB
C
Raw Normal View History

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
}