2009-02-28 14:41:18 +00:00
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* =======================================================================
|
|
|
|
*
|
|
|
|
* Server main function and correspondig stuff
|
|
|
|
*
|
|
|
|
* =======================================================================
|
2012-04-29 13:57:33 +00:00
|
|
|
*/
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-25 15:46:36 +00:00
|
|
|
#include "header/server.h"
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
#define HEARTBEAT_SECONDS 300
|
|
|
|
|
|
|
|
netadr_t master_adr [ MAX_MASTERS ]; /* address of group servers */
|
|
|
|
|
|
|
|
client_t *sv_client; /* current client */
|
|
|
|
|
|
|
|
cvar_t *sv_paused;
|
|
|
|
cvar_t *sv_timedemo;
|
|
|
|
cvar_t *sv_enforcetime;
|
|
|
|
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;
|
|
|
|
cvar_t *allow_download_players;
|
|
|
|
cvar_t *allow_download_models;
|
|
|
|
cvar_t *allow_download_sounds;
|
|
|
|
cvar_t *allow_download_maps;
|
|
|
|
cvar_t *sv_airaccelerate;
|
|
|
|
cvar_t *sv_noreload; /* don't reload level state when reentering */
|
|
|
|
cvar_t *maxclients; /* FIXME: rename sv_maxclients */
|
|
|
|
cvar_t *sv_showclamp;
|
|
|
|
cvar_t *hostname;
|
|
|
|
cvar_t *public_server; /* should heartbeats be sent */
|
|
|
|
|
|
|
|
void Master_Shutdown ( void );
|
2010-11-26 08:12:50 +00:00
|
|
|
void SV_ConnectionlessPacket ( void );
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Called when the player is totally leaving the server, either willingly
|
|
|
|
* or unwillingly. This is NOT called if the entire server is quiting
|
|
|
|
* or crashing.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_DropClient ( client_t *drop )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
/* add the disconnect */
|
|
|
|
MSG_WriteByte( &drop->netchan.message, svc_disconnect );
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( drop->state == cs_spawned )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2012-04-29 13:57:33 +00:00
|
|
|
/* call the prog function for removing a client
|
2010-11-26 07:33:49 +00:00
|
|
|
this will remove the body, among other things */
|
|
|
|
ge->ClientDisconnect( drop->edict );
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( drop->download )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
FS_FreeFile( drop->download );
|
2009-02-28 14:41:18 +00:00
|
|
|
drop->download = NULL;
|
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
drop->state = cs_zombie; /* become free in a few seconds */
|
|
|
|
drop->name [ 0 ] = 0;
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Builds the string that is sent as heartbeats and status replies
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
SV_StatusString ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
char player [ 1024 ];
|
|
|
|
static char status [ MAX_MSGLEN - 16 ];
|
|
|
|
int i;
|
|
|
|
client_t *cl;
|
|
|
|
int statusLength;
|
|
|
|
int playerLength;
|
|
|
|
|
|
|
|
strcpy( status, Cvar_Serverinfo() );
|
|
|
|
strcat( status, "\n" );
|
|
|
|
statusLength = (int) strlen( status );
|
|
|
|
|
|
|
|
for ( i = 0; i < maxclients->value; i++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
cl = &svs.clients [ i ];
|
|
|
|
|
|
|
|
if ( ( cl->state == cs_connected ) || ( cl->state == cs_spawned ) )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
Com_sprintf( player, sizeof ( player ), "%i %i \"%s\"\n",
|
|
|
|
cl->edict->client->ps.stats [ STAT_FRAGS ], cl->ping, cl->name );
|
|
|
|
playerLength = (int) strlen( player );
|
|
|
|
|
|
|
|
if ( statusLength + playerLength >= sizeof ( status ) )
|
|
|
|
{
|
|
|
|
break; /* can't hold any more */
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy( status + statusLength, player );
|
2009-02-28 14:41:18 +00:00
|
|
|
statusLength += playerLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
return ( status );
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Updates the cl->ping variables
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_CalcPings ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
int i, j;
|
|
|
|
client_t *cl;
|
|
|
|
int total, count;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
for ( i = 0; i < maxclients->value; i++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
cl = &svs.clients [ i ];
|
|
|
|
|
|
|
|
if ( cl->state != cs_spawned )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
total = 0;
|
|
|
|
count = 0;
|
2010-11-26 07:33:49 +00:00
|
|
|
|
|
|
|
for ( j = 0; j < LATENCY_COUNTS; j++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( cl->frame_latency [ j ] > 0 )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
|
|
|
count++;
|
2010-11-26 07:33:49 +00:00
|
|
|
total += cl->frame_latency [ j ];
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
|
|
|
|
if ( !count )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->ping = 0;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
else
|
2010-11-26 07:33:49 +00:00
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->ping = total / count;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* let the game dll know about the ping */
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->edict->client->ping = cl->ping;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Every few frames, gives all clients an allotment of milliseconds
|
|
|
|
* for their command moves. If they exceed it, assume cheating.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_GiveMsec ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
int i;
|
|
|
|
client_t *cl;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( sv.framenum & 15 )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
return;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
for ( i = 0; i < maxclients->value; i++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
cl = &svs.clients [ i ];
|
|
|
|
|
|
|
|
if ( cl->state == cs_free )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cl->commandMsec = 1800; /* 1600 + some slop */
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
void
|
|
|
|
SV_ReadPackets ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
int i;
|
|
|
|
client_t *cl;
|
|
|
|
int qport;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
while ( NET_GetPacket( NS_SERVER, &net_from, &net_message ) )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
/* check for connectionless packet (0xffffffff) first */
|
|
|
|
if ( *(int *) net_message.data == -1 )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
SV_ConnectionlessPacket();
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-04-29 13:57:33 +00:00
|
|
|
/* read the qport out of the message so we can fix up
|
2010-11-26 07:33:49 +00:00
|
|
|
stupid address translating routers */
|
|
|
|
MSG_BeginReading( &net_message );
|
|
|
|
MSG_ReadLong( &net_message ); /* sequence number */
|
|
|
|
MSG_ReadLong( &net_message ); /* sequence number */
|
|
|
|
qport = MSG_ReadShort( &net_message ) & 0xffff;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* check for packets from connected clients */
|
|
|
|
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( cl->state == cs_free )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( !NET_CompareBaseAdr( net_from, cl->netchan.remote_address ) )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( cl->netchan.qport != qport )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( cl->netchan.remote_address.port != net_from.port )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
Com_Printf( "SV_ReadPackets: fixing up a translated port\n" );
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->netchan.remote_address.port = net_from.port;
|
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( Netchan_Process( &cl->netchan, &net_message ) )
|
2012-04-29 13:57:33 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
/* this is a valid, sequenced packet, so process it */
|
|
|
|
if ( cl->state != cs_zombie )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
cl->lastmessage = svs.realtime; /* don't timeout */
|
|
|
|
|
|
|
|
if ( !( sv.demofile && ( sv.state == ss_demo ) ) )
|
|
|
|
{
|
|
|
|
SV_ExecuteClientMessage( cl );
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
|
|
|
|
if ( i != maxclients->value )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* When a client is normally dropped, the client_t goes into a zombie state
|
|
|
|
* for a few seconds to make sure any final reliable message gets resent
|
|
|
|
* if necessary
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_CheckTimeouts ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
int i;
|
|
|
|
client_t *cl;
|
|
|
|
int droppoint;
|
|
|
|
int zombiepoint;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
droppoint = svs.realtime - 1000 * timeout->value;
|
|
|
|
zombiepoint = svs.realtime - 1000 * zombietime->value;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
/* message times may be wrong across a changelevel */
|
|
|
|
if ( cl->lastmessage > svs.realtime )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->lastmessage = svs.realtime;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( ( cl->state == cs_zombie ) &&
|
|
|
|
( cl->lastmessage < zombiepoint ) )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
cl->state = cs_free; /* can now be reused */
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
|
|
|
|
if ( ( ( cl->state == cs_connected ) || ( cl->state == cs_spawned ) ) &&
|
|
|
|
( cl->lastmessage < droppoint ) )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
SV_BroadcastPrintf( PRINT_HIGH, "%s timed out\n", cl->name );
|
|
|
|
SV_DropClient( cl );
|
|
|
|
cl->state = cs_free; /* don't bother with zombie state */
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* This has to be done before the world logic, because
|
|
|
|
* player processing happens outside RunWorldFrame
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_PrepWorldFrame ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
edict_t *ent;
|
|
|
|
int i;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
for ( i = 0; i < ge->num_edicts; i++, ent++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
ent = EDICT_NUM( i );
|
|
|
|
|
|
|
|
/* events only last for a single message */
|
2009-02-28 14:41:18 +00:00
|
|
|
ent->s.event = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
void
|
|
|
|
SV_RunGameFrame ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2009-03-04 13:41:07 +00:00
|
|
|
#ifndef DEDICATED_ONLY
|
2010-11-26 07:33:49 +00:00
|
|
|
|
|
|
|
if ( host_speeds->value )
|
|
|
|
{
|
|
|
|
time_before_game = Sys_Milliseconds();
|
|
|
|
}
|
|
|
|
|
2009-03-04 13:41:07 +00:00
|
|
|
#endif
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* we always need to bump framenum, even if we
|
2012-04-29 13:57:33 +00:00
|
|
|
don't run the world, otherwise the delta
|
|
|
|
compression can get confused when a client
|
2010-11-26 07:33:49 +00:00
|
|
|
has the "current" frame */
|
2009-02-28 14:41:18 +00:00
|
|
|
sv.framenum++;
|
2010-11-26 07:33:49 +00:00
|
|
|
sv.time = sv.framenum * 100;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* don't run if paused */
|
|
|
|
if ( !sv_paused->value || ( maxclients->value > 1 ) )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
ge->RunFrame();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* never get more than one tic behind */
|
|
|
|
if ( sv.time < svs.realtime )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( sv_showclamp->value )
|
|
|
|
{
|
|
|
|
Com_Printf( "sv highclamp\n" );
|
|
|
|
}
|
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
svs.realtime = sv.time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
#ifndef DEDICATED_ONLY
|
|
|
|
|
|
|
|
if ( host_speeds->value )
|
|
|
|
{
|
|
|
|
time_after_game = Sys_Milliseconds();
|
|
|
|
}
|
|
|
|
|
2009-03-04 13:41:07 +00:00
|
|
|
#endif
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
void
|
|
|
|
SV_Frame ( int msec )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
#ifndef DEDICATED_ONLY
|
2009-02-28 14:41:18 +00:00
|
|
|
time_before_game = time_after_game = 0;
|
2009-03-04 13:41:07 +00:00
|
|
|
#endif
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* if server is not active, do nothing */
|
|
|
|
if ( !svs.initialized )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
return;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
svs.realtime += msec;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* keep the random time dependent */
|
2012-06-02 09:55:12 +00:00
|
|
|
randk();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* check timeouts */
|
|
|
|
SV_CheckTimeouts();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* get packets from clients */
|
|
|
|
SV_ReadPackets();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* move autonomous things around if enough time has passed */
|
|
|
|
if ( !sv_timedemo->value && ( svs.realtime < sv.time ) )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
/* never let the time get too far off */
|
|
|
|
if ( sv.time - svs.realtime > 100 )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( sv_showclamp->value )
|
|
|
|
{
|
|
|
|
Com_Printf( "sv lowclamp\n" );
|
|
|
|
}
|
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
svs.realtime = sv.time - 100;
|
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
|
|
|
|
NET_Sleep( sv.time - svs.realtime );
|
2009-02-28 14:41:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* update ping based on the last known frame from all clients */
|
|
|
|
SV_CalcPings();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* give the clients some timeslices */
|
|
|
|
SV_GiveMsec();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* let everything in the world think and move */
|
|
|
|
SV_RunGameFrame();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* send messages back to the clients that had packets read this frame */
|
|
|
|
SV_SendClientMessages();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* save the entire world state if recording a serverdemo */
|
|
|
|
SV_RecordDemoMessage();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* send a heartbeat to the master if needed */
|
|
|
|
Master_Heartbeat();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* clear teleport flags, etc for next frame */
|
|
|
|
SV_PrepWorldFrame();
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Send a message to the master every few minutes to
|
|
|
|
* let it know we are alive, and log information
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
Master_Heartbeat ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
char *string;
|
|
|
|
int i;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( !dedicated || !dedicated->value )
|
|
|
|
{
|
|
|
|
return; /* only dedicated servers send heartbeats */
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( !public_server || !public_server->value )
|
|
|
|
{
|
|
|
|
return; /* a private dedicated game */
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* check for time wraparound */
|
|
|
|
if ( svs.last_heartbeat > svs.realtime )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
svs.last_heartbeat = svs.realtime;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS * 1000 )
|
|
|
|
{
|
|
|
|
return; /* not time to send yet */
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
svs.last_heartbeat = svs.realtime;
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* send the same string that we would give for a status OOB command */
|
2009-02-28 14:41:18 +00:00
|
|
|
string = SV_StatusString();
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* send to group master */
|
|
|
|
for ( i = 0; i < MAX_MASTERS; i++ )
|
|
|
|
{
|
|
|
|
if ( master_adr [ i ].port )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
Com_Printf( "Sending heartbeat to %s\n", NET_AdrToString( master_adr [ i ] ) );
|
|
|
|
Netchan_OutOfBandPrint( NS_SERVER, master_adr [ i ], "heartbeat\n%s", string );
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Informs all masters that this server is going down
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
Master_Shutdown ( void )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
int i;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( !dedicated || !dedicated->value )
|
|
|
|
{
|
|
|
|
return; /* only dedicated servers send heartbeats */
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( !public_server || !public_server->value )
|
|
|
|
{
|
|
|
|
return; /* a private dedicated game */
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* send to group master */
|
|
|
|
for ( i = 0; i < MAX_MASTERS; i++ )
|
|
|
|
{
|
|
|
|
if ( master_adr [ i ].port )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( i > 0 )
|
|
|
|
{
|
|
|
|
Com_Printf( "Sending heartbeat to %s\n", NET_AdrToString( master_adr [ i ] ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
Netchan_OutOfBandPrint( NS_SERVER, master_adr [ i ], "shutdown" );
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Pull specific info from a newly changed userinfo string
|
|
|
|
* into a more C freindly form.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_UserinfoChanged ( client_t *cl )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
char *val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* call prog code to allow overrides */
|
|
|
|
ge->ClientUserinfoChanged( cl->edict, cl->userinfo );
|
|
|
|
|
|
|
|
/* name for C code */
|
|
|
|
strncpy( cl->name, Info_ValueForKey( cl->userinfo, "name" ), sizeof ( cl->name ) - 1 );
|
|
|
|
|
|
|
|
/* mask off high bit */
|
|
|
|
for ( i = 0; i < sizeof ( cl->name ); i++ )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
cl->name [ i ] &= 127;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* rate command */
|
|
|
|
val = Info_ValueForKey( cl->userinfo, "rate" );
|
|
|
|
|
|
|
|
if ( strlen( val ) )
|
|
|
|
{
|
2012-06-02 07:31:27 +00:00
|
|
|
i = (int)strtol( val, (char **)NULL, 10 );
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->rate = i;
|
2010-11-26 07:33:49 +00:00
|
|
|
|
|
|
|
if ( cl->rate < 100 )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->rate = 100;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( cl->rate > 15000 )
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->rate = 15000;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
else
|
2010-11-26 07:33:49 +00:00
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
cl->rate = 5000;
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* msg command */
|
|
|
|
val = Info_ValueForKey( cl->userinfo, "msg" );
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( strlen( val ) )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2012-06-02 07:31:27 +00:00
|
|
|
cl->messagelevel = (int)strtol( val, (char **)NULL, 10 );
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
2010-11-26 07:33:49 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/*
|
|
|
|
* Only called at quake2.exe startup, not for each game
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_Init ( void )
|
|
|
|
{
|
|
|
|
SV_InitOperatorCommands();
|
|
|
|
|
|
|
|
rcon_password = Cvar_Get( "rcon_password", "", 0 );
|
|
|
|
Cvar_Get( "skill", "1", 0 );
|
|
|
|
Cvar_Get( "deathmatch", "0", CVAR_LATCH );
|
|
|
|
Cvar_Get( "coop", "0", CVAR_LATCH );
|
|
|
|
Cvar_Get( "dmflags", va( "%i", DF_INSTANT_ITEMS ), CVAR_SERVERINFO );
|
|
|
|
Cvar_Get( "fraglimit", "0", CVAR_SERVERINFO );
|
|
|
|
Cvar_Get( "timelimit", "0", CVAR_SERVERINFO );
|
|
|
|
Cvar_Get( "cheats", "0", CVAR_SERVERINFO | CVAR_LATCH );
|
|
|
|
Cvar_Get( "protocol", va( "%i", PROTOCOL_VERSION ), CVAR_SERVERINFO | CVAR_NOSET );
|
|
|
|
maxclients = Cvar_Get( "maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH );
|
|
|
|
hostname = Cvar_Get( "hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE );
|
|
|
|
timeout = Cvar_Get( "timeout", "125", 0 );
|
|
|
|
zombietime = Cvar_Get( "zombietime", "2", 0 );
|
|
|
|
sv_showclamp = Cvar_Get( "showclamp", "0", 0 );
|
|
|
|
sv_paused = Cvar_Get( "paused", "0", 0 );
|
|
|
|
sv_timedemo = Cvar_Get( "timedemo", "0", 0 );
|
|
|
|
sv_enforcetime = Cvar_Get( "sv_enforcetime", "0", 0 );
|
|
|
|
allow_download = Cvar_Get( "allow_download", "1", CVAR_ARCHIVE );
|
|
|
|
allow_download_players = Cvar_Get( "allow_download_players", "0", CVAR_ARCHIVE );
|
|
|
|
allow_download_models = Cvar_Get( "allow_download_models", "1", CVAR_ARCHIVE );
|
|
|
|
allow_download_sounds = Cvar_Get( "allow_download_sounds", "1", CVAR_ARCHIVE );
|
|
|
|
allow_download_maps = Cvar_Get( "allow_download_maps", "1", CVAR_ARCHIVE );
|
|
|
|
|
|
|
|
sv_noreload = Cvar_Get( "sv_noreload", "0", 0 );
|
|
|
|
|
|
|
|
sv_airaccelerate = Cvar_Get( "sv_airaccelerate", "0", CVAR_LATCH );
|
|
|
|
|
|
|
|
public_server = Cvar_Get( "public", "0", 0 );
|
|
|
|
|
|
|
|
SZ_Init( &net_message, net_message_buffer, sizeof ( net_message_buffer ) );
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_FinalMessage ( char *message, qboolean reconnect )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
client_t *cl;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
SZ_Clear( &net_message );
|
|
|
|
MSG_WriteByte( &net_message, svc_print );
|
|
|
|
MSG_WriteByte( &net_message, PRINT_HIGH );
|
|
|
|
MSG_WriteString( &net_message, message );
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( reconnect )
|
|
|
|
{
|
|
|
|
MSG_WriteByte( &net_message, svc_reconnect );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MSG_WriteByte( &net_message, svc_disconnect );
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* stagger the packets to crutch operating system limited buffers */
|
|
|
|
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
|
|
|
|
{
|
|
|
|
if ( cl->state >= cs_connected )
|
|
|
|
{
|
|
|
|
Netchan_Transmit( &cl->netchan, net_message.cursize, net_message.data );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
|
|
|
|
{
|
|
|
|
if ( cl->state >= cs_connected )
|
|
|
|
{
|
|
|
|
Netchan_Transmit( &cl->netchan, net_message.cursize,
|
|
|
|
net_message.data );
|
|
|
|
}
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-26 07:33:49 +00:00
|
|
|
* Called when each game quits,
|
|
|
|
* before Sys_Quit or Sys_Error
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SV_Shutdown ( char *finalmsg, qboolean reconnect )
|
2009-02-28 14:41:18 +00:00
|
|
|
{
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( svs.clients )
|
|
|
|
{
|
|
|
|
SV_FinalMessage( finalmsg, reconnect );
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
Master_Shutdown();
|
|
|
|
SV_ShutdownGameProgs();
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
/* free current level */
|
|
|
|
if ( sv.demofile )
|
|
|
|
{
|
|
|
|
FS_FCloseFile( (size_t) sv.demofile );
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
memset( &sv, 0, sizeof ( sv ) );
|
|
|
|
Com_SetServerState( sv.state );
|
|
|
|
|
|
|
|
/* free server static data */
|
|
|
|
if ( svs.clients )
|
|
|
|
{
|
|
|
|
Z_Free( svs.clients );
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( svs.client_entities )
|
|
|
|
{
|
|
|
|
Z_Free( svs.client_entities );
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
if ( svs.demofile )
|
|
|
|
{
|
|
|
|
FS_FCloseFile( (size_t) svs.demofile );
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-11-26 07:33:49 +00:00
|
|
|
memset( &svs, 0, sizeof ( svs ) );
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|