2008-05-18 22:00:00 +02:00
|
|
|
|
//=======================================================================
|
|
|
|
|
// Copyright XashXT Group 2007 <20>
|
|
|
|
|
// sv_utils.c - server vm utils
|
|
|
|
|
//=======================================================================
|
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"
|
|
|
|
|
|
|
|
|
|
netadr_t master_adr[MAX_MASTERS]; // address of group servers
|
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *sv_client; // current client
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
cvar_t *sv_paused;
|
2008-07-03 22:00:00 +02:00
|
|
|
|
cvar_t *sv_fps;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
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;
|
2007-09-02 22:00:00 +02:00
|
|
|
|
cvar_t *sv_airaccelerate;
|
|
|
|
|
cvar_t *sv_maxvelocity;
|
|
|
|
|
cvar_t *sv_gravity;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
cvar_t *sv_noreload; // don't reload level state when reentering
|
|
|
|
|
|
|
|
|
|
cvar_t *maxclients; // FIXME: rename sv_maxclients
|
|
|
|
|
cvar_t *hostname;
|
2007-09-10 22:00:00 +02:00
|
|
|
|
cvar_t *public_server; // should heartbeats be sent
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-10 22:00:00 +02:00
|
|
|
|
cvar_t *sv_reconnect_limit;// minimum seconds between connect messages
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
void Master_Shutdown (void);
|
|
|
|
|
|
2007-09-10 22:00:00 +02:00
|
|
|
|
static int rd_target;
|
|
|
|
|
static char *rd_buffer;
|
|
|
|
|
static int rd_buffersize;
|
2008-07-10 22:00:00 +02:00
|
|
|
|
static void (*rd_flush)( netadr_t adr, int target, char *buffer);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
//============================================================================
|
2008-07-10 22:00:00 +02:00
|
|
|
|
void SVC_BeginRedirect( netadr_t adr, int target, char *buffer, int buffersize, void (*flush))
|
2007-09-10 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
if (!target || !buffer || !buffersize || !flush) return;
|
|
|
|
|
|
|
|
|
|
host.rd.target = target;
|
|
|
|
|
host.rd.buffer = buffer;
|
|
|
|
|
host.rd.buffersize = buffersize;
|
|
|
|
|
host.rd.flush = flush;
|
2008-07-10 22:00:00 +02:00
|
|
|
|
host.rd.address = adr;
|
2007-09-10 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
*host.rd.buffer = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-10 22:00:00 +02:00
|
|
|
|
void SVC_EndRedirect( netadr_t adr )
|
2007-09-10 22:00:00 +02:00
|
|
|
|
{
|
2008-07-10 22:00:00 +02:00
|
|
|
|
host.rd.flush( adr, rd_target, rd_buffer );
|
2007-09-10 22:00:00 +02:00
|
|
|
|
|
2008-07-10 22:00:00 +02:00
|
|
|
|
memset( &host.rd.address, 0, sizeof(netadr_t));
|
2007-09-10 22:00:00 +02:00
|
|
|
|
host.rd.target = 0;
|
|
|
|
|
host.rd.buffer = NULL;
|
|
|
|
|
host.rd.buffersize = 0;
|
|
|
|
|
host.rd.flush = NULL;
|
|
|
|
|
}
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
|
|
CONNECTIONLESS COMMANDS
|
|
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===============
|
|
|
|
|
SV_StatusString
|
|
|
|
|
|
|
|
|
|
Builds the string that is sent as heartbeats and status replies
|
|
|
|
|
===============
|
|
|
|
|
*/
|
2007-09-10 22:00:00 +02:00
|
|
|
|
char *SV_StatusString (void)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
char player[1024];
|
|
|
|
|
static char status[MAX_MSGLEN - 16];
|
|
|
|
|
int i;
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
int statusLength;
|
|
|
|
|
int playerLength;
|
|
|
|
|
|
2008-06-22 22:00:00 +02:00
|
|
|
|
com.strcpy( status, Cvar_Serverinfo());
|
|
|
|
|
com.strcat( status, "\n" );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
statusLength = strlen(status);
|
|
|
|
|
|
2008-06-22 22:00:00 +02:00
|
|
|
|
for( i = 0; i < maxclients->value; i++ )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
cl = &svs.clients[i];
|
|
|
|
|
if (cl->state == cs_connected || cl->state == cs_spawned )
|
|
|
|
|
{
|
2008-06-09 22:00:00 +02:00
|
|
|
|
com.sprintf (player, "%i %i \"%s\"\n", cl->edict->priv.sv->client->ps.stats[STAT_FRAGS], cl->ping, cl->name);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
playerLength = strlen(player);
|
2008-06-22 22:00:00 +02:00
|
|
|
|
if( statusLength + playerLength >= sizeof(status))
|
|
|
|
|
break; // can't hold any more
|
|
|
|
|
com.strcpy( status + statusLength, player );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
statusLength += playerLength;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
SVC_Status
|
|
|
|
|
|
|
|
|
|
Responds with all the info that qplug or qspy can see
|
|
|
|
|
================
|
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
void SVC_Status( netadr_t from )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
Netchan_OutOfBandPrint(NS_SERVER, from, "print\n%s", SV_StatusString());
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
SVC_Ack
|
|
|
|
|
|
|
|
|
|
================
|
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
void SVC_Ack( netadr_t from )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
Msg ("Ping acknowledge from %s\n", NET_AdrToString(from));
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
SVC_Info
|
|
|
|
|
|
|
|
|
|
Responds with short info for broadcast scans
|
|
|
|
|
The second parameter should be the current protocol version number.
|
|
|
|
|
================
|
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
void SVC_Info( netadr_t from )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
char string[64];
|
2008-07-09 22:00:00 +02:00
|
|
|
|
int i, count;
|
|
|
|
|
int version;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
if (maxclients->value == 1)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
return; // ignore in single player
|
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
version = com.atoi(Cmd_Argv(1));
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if (version != PROTOCOL_VERSION)
|
2008-06-09 22:00:00 +02:00
|
|
|
|
com.sprintf (string, "%s: wrong version\n", hostname->string, sizeof(string));
|
2007-06-21 22:00:00 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
count = 0;
|
2007-09-06 22:00:00 +02:00
|
|
|
|
for (i=0 ; i<maxclients->value ; i++)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
if (svs.clients[i].state >= cs_connected)
|
|
|
|
|
count++;
|
|
|
|
|
|
2008-06-09 22:00:00 +02:00
|
|
|
|
com.sprintf (string, "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
Netchan_OutOfBandPrint(NS_SERVER, from, "info\n%s", string);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
SVC_Ping
|
|
|
|
|
|
|
|
|
|
Just responds with an acknowledgement
|
|
|
|
|
================
|
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
void SVC_Ping( netadr_t from )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
Netchan_OutOfBandPrint (NS_SERVER, from, "ack");
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Rcon_Validate (void)
|
|
|
|
|
{
|
|
|
|
|
if (!strlen (rcon_password->string))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (strcmp (Cmd_Argv(1), rcon_password->string) )
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===============
|
|
|
|
|
SVC_RemoteCommand
|
|
|
|
|
|
|
|
|
|
A client issued an rcon command.
|
|
|
|
|
Shift down the remaining args
|
|
|
|
|
Redirect all printfs
|
|
|
|
|
===============
|
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
void SVC_RemoteCommand( netadr_t from, sizebuf_t *msg )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
int i;
|
2007-09-10 22:00:00 +02:00
|
|
|
|
char remaining[1024];
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
i = Rcon_Validate ();
|
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
if (i == 0) MsgDev(D_INFO, "Bad rcon from %s:\n%s\n", NET_AdrToString (from), msg->data + 4);
|
|
|
|
|
else MsgDev(D_INFO, "Rcon from %s:\n%s\n", NET_AdrToString (from), msg->data + 4);
|
2008-07-10 22:00:00 +02:00
|
|
|
|
SVC_BeginRedirect( from, RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-10 22:00:00 +02:00
|
|
|
|
if (!Rcon_Validate()) Msg("Bad rcon_password.\n");
|
2007-06-21 22:00:00 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
remaining[0] = 0;
|
|
|
|
|
|
2007-09-10 22:00:00 +02:00
|
|
|
|
for (i = 2; i < Cmd_Argc(); i++)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
strcat (remaining, Cmd_Argv(i) );
|
|
|
|
|
strcat (remaining, " ");
|
|
|
|
|
}
|
|
|
|
|
Cmd_ExecuteString (remaining);
|
|
|
|
|
}
|
2008-07-10 22:00:00 +02:00
|
|
|
|
SVC_EndRedirect( from );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
SV_ConnectionlessPacket
|
|
|
|
|
|
|
|
|
|
A connectionless packet has four leading 0xff
|
|
|
|
|
characters to distinguish it from a game channel.
|
|
|
|
|
Clients that are in the game can still send
|
|
|
|
|
connectionless packets.
|
|
|
|
|
=================
|
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
char *s;
|
|
|
|
|
char *c;
|
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
MSG_BeginReading( msg );
|
|
|
|
|
MSG_ReadLong( msg );// skip the -1 marker
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
s = MSG_ReadStringLine( msg );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
Cmd_TokenizeString( s );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
c = Cmd_Argv( 0 );
|
|
|
|
|
MsgDev( D_INFO, "SV_ConnectionlessPacket: %s : %s\n", NET_AdrToString(from), c);
|
2007-08-17 22:00:00 +02:00
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
if (!strcmp(c, "ping")) SVC_Ping( from );
|
|
|
|
|
else if (!strcmp(c, "ack")) SVC_Ack( from );
|
|
|
|
|
else if (!strcmp(c,"status")) SVC_Status( from );
|
|
|
|
|
else if (!strcmp(c,"info")) SVC_Info( from );
|
|
|
|
|
else if (!strcmp(c,"getchallenge")) SV_GetChallenge( from );
|
|
|
|
|
else if (!strcmp(c,"connect")) SV_DirectConnect( from );
|
|
|
|
|
else if (!strcmp(c, "rcon")) SVC_RemoteCommand( from, msg );
|
|
|
|
|
else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString (from), s);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===================
|
|
|
|
|
SV_CalcPings
|
|
|
|
|
|
|
|
|
|
Updates the cl->ping variables
|
|
|
|
|
===================
|
|
|
|
|
*/
|
|
|
|
|
void SV_CalcPings (void)
|
|
|
|
|
{
|
|
|
|
|
int i, j;
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
int total, count;
|
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
for (i=0 ; i<maxclients->value ; i++)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
cl = &svs.clients[i];
|
|
|
|
|
if (cl->state != cs_spawned )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
if (cl->lastframe > 0)
|
|
|
|
|
cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl->lastframe + 1;
|
|
|
|
|
else
|
|
|
|
|
cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
total = 0;
|
|
|
|
|
count = 0;
|
|
|
|
|
for (j=0 ; j<LATENCY_COUNTS ; j++)
|
|
|
|
|
{
|
|
|
|
|
if (cl->frame_latency[j] > 0)
|
|
|
|
|
{
|
|
|
|
|
count++;
|
|
|
|
|
total += cl->frame_latency[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-07-04 22:00:00 +02:00
|
|
|
|
if( !count ) cl->ping = 0;
|
|
|
|
|
else cl->ping = total / count;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// let the game dll know about the ping
|
2007-09-16 22:00:00 +02:00
|
|
|
|
cl->edict->priv.sv->client->ping = cl->ping;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
===================
|
|
|
|
|
SV_GiveMsec
|
|
|
|
|
|
|
|
|
|
Every few frames, gives all clients an allotment of milliseconds
|
|
|
|
|
for their command moves. If they exceed it, assume cheating.
|
|
|
|
|
===================
|
|
|
|
|
*/
|
|
|
|
|
void SV_GiveMsec (void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
if (sv.framenum & 15)
|
|
|
|
|
return;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
for (i=0 ; i<maxclients->value ; i++)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
cl = &svs.clients[i];
|
|
|
|
|
if (cl->state == cs_free )
|
|
|
|
|
continue;
|
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
cl->commandMsec = 1800; // 1600 + some slop
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
SV_ReadPackets
|
|
|
|
|
=================
|
|
|
|
|
*/
|
2008-07-09 22:00:00 +02:00
|
|
|
|
void SV_ReadPackets( void )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
|
|
|
|
int i, qport;
|
2008-07-10 22:00:00 +02:00
|
|
|
|
sizebuf_t net_message;
|
|
|
|
|
sizebuf_t *msg = &net_message;
|
|
|
|
|
byte buffer[MAX_MSGLEN];
|
|
|
|
|
netadr_t net_from;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-10 22:00:00 +02:00
|
|
|
|
SZ_Init( msg, buffer, sizeof(buffer));
|
|
|
|
|
while( NET_GetPacket( NS_SERVER, &net_from, msg ))
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
// check for connectionless packet (0xffffffff) first
|
2008-07-09 22:00:00 +02:00
|
|
|
|
if( msg->cursize >= 4 && *(int *)msg->data == -1 )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-10 22:00:00 +02:00
|
|
|
|
SV_ConnectionlessPacket( net_from, msg );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// read the qport out of the message so we can fix up
|
|
|
|
|
// stupid address translating routers
|
2008-07-09 22:00:00 +02:00
|
|
|
|
MSG_BeginReading( msg );
|
|
|
|
|
MSG_ReadLong( msg ); // sequence number
|
2008-07-10 22:00:00 +02:00
|
|
|
|
MSG_ReadLong( msg ); // sequence number
|
2008-07-09 22:00:00 +02:00
|
|
|
|
qport = MSG_ReadShort( msg ) & 0xffff;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// check for packets from connected clients
|
2008-07-09 22:00:00 +02:00
|
|
|
|
for( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
if( cl->state == cs_free ) continue;
|
2008-07-10 22:00:00 +02:00
|
|
|
|
if( !NET_CompareBaseAdr( net_from, cl->netchan.remote_address)) continue;
|
2008-07-09 22:00:00 +02:00
|
|
|
|
if( cl->netchan.qport != qport ) continue;
|
2008-07-10 22:00:00 +02:00
|
|
|
|
if( cl->netchan.remote_address.port != net_from.port )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
MsgDev(D_INFO, "SV_PacketEvent: fixing up a translated port\n");
|
2008-07-10 22:00:00 +02:00
|
|
|
|
cl->netchan.remote_address.port = net_from.port;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2008-07-09 22:00:00 +02:00
|
|
|
|
// make sure it is a valid, in sequence packet
|
|
|
|
|
if( Netchan_Process(&cl->netchan, msg))
|
2007-10-19 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
// this is a valid, sequenced packet, so process it
|
2008-07-09 22:00:00 +02:00
|
|
|
|
if( cl->state != cs_zombie )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2007-10-19 22:00:00 +02:00
|
|
|
|
cl->lastmessage = svs.realtime; // don't timeout
|
2008-07-09 22:00:00 +02:00
|
|
|
|
SV_ExecuteClientMessage( cl, msg );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2008-07-09 22:00:00 +02:00
|
|
|
|
if( i == maxclients->integer ) continue;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2008-07-09 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// if we received a sequenced packet from an address we don't recognize,
|
|
|
|
|
// send an out of band disconnect packet to it
|
|
|
|
|
//FIXME
|
|
|
|
|
//Netchan_OutOfBandPrint( NS_SERVER, from, "disconnect\n" );
|
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
|
|
|
|
|
==================
|
|
|
|
|
*/
|
|
|
|
|
void SV_CheckTimeouts (void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2008-07-09 22:00:00 +02:00
|
|
|
|
sv_client_t *cl;
|
2007-09-07 22:00:00 +02:00
|
|
|
|
float droppoint;
|
|
|
|
|
float zombiepoint;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-03 22:00:00 +02:00
|
|
|
|
droppoint = svs.realtime - 1000 * timeout->value;
|
|
|
|
|
zombiepoint = svs.realtime - 1000 * zombietime->value;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
// message times may be wrong across a changelevel
|
2007-09-09 22:00:00 +02:00
|
|
|
|
if (cl->lastmessage > svs.realtime)
|
|
|
|
|
cl->lastmessage = svs.realtime;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
if (cl->state == cs_zombie
|
2007-09-09 22:00:00 +02:00
|
|
|
|
&& cl->lastmessage < zombiepoint)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
cl->state = cs_free; // can now be reused
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2007-09-09 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
|
|
|
|
{
|
2008-06-09 22:00:00 +02:00
|
|
|
|
SV_BroadcastPrintf (PRINT_CONSOLE, "%s timed out\n", cl->name);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
SV_DropClient (cl);
|
|
|
|
|
cl->state = cs_free; // don't bother with zombie state
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
/*
|
|
|
|
|
=================
|
|
|
|
|
SV_RunGameFrame
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
void SV_RunGameFrame (void)
|
|
|
|
|
{
|
2008-07-04 22:00:00 +02:00
|
|
|
|
int i;
|
|
|
|
|
|
2008-07-03 22:00:00 +02:00
|
|
|
|
if( sv_paused->integer && maxclients->integer == 1 )
|
|
|
|
|
return;
|
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
// we always need to bump framenum, even if we
|
|
|
|
|
// don't run the world, otherwise the delta
|
|
|
|
|
// compression can get confused when a client
|
|
|
|
|
// has the "current" frame
|
|
|
|
|
sv.framenum++;
|
2008-07-04 22:00:00 +02:00
|
|
|
|
sv.frametime = HOST_FRAMETIME * 0.001f;
|
2007-09-17 22:00:00 +02:00
|
|
|
|
sv.time = sv.framenum * sv.frametime;
|
2007-09-06 22:00:00 +02:00
|
|
|
|
|
2008-07-03 22:00:00 +02:00
|
|
|
|
if(!cm_paused->value)
|
2008-07-04 22:00:00 +02:00
|
|
|
|
for( i = 0; i < 6; i++ )
|
|
|
|
|
pe->Frame( sv.frametime * i );//FIXME
|
2008-07-03 22:00:00 +02:00
|
|
|
|
SV_RunFrame ();
|
2007-09-06 22:00:00 +02:00
|
|
|
|
|
2008-07-03 22:00:00 +02:00
|
|
|
|
// never get more than one tic behind
|
|
|
|
|
if( sv.time * 1000 < svs.realtime )
|
|
|
|
|
{
|
|
|
|
|
Msg ("sv highclamp\n");
|
|
|
|
|
svs.realtime = sv.time * 1000;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
==================
|
|
|
|
|
SV_Frame
|
|
|
|
|
|
|
|
|
|
==================
|
|
|
|
|
*/
|
2008-07-03 22:00:00 +02:00
|
|
|
|
void SV_Frame( dword time )
|
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;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-07 22:00:00 +02:00
|
|
|
|
svs.realtime += time;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// keep the random time dependent
|
|
|
|
|
rand ();
|
|
|
|
|
|
2007-09-16 22:00:00 +02:00
|
|
|
|
// setup the VM frame
|
|
|
|
|
SV_VM_Begin();
|
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
// check timeouts
|
|
|
|
|
SV_CheckTimeouts ();
|
2007-09-06 22:00:00 +02:00
|
|
|
|
|
2007-06-21 22:00:00 +02:00
|
|
|
|
// get packets from clients
|
|
|
|
|
SV_ReadPackets ();
|
|
|
|
|
|
|
|
|
|
// move autonomous things around if enough time has passed
|
2008-07-04 22:00:00 +02:00
|
|
|
|
if( svs.realtime < (sv.time * 1000))
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
|
|
|
|
// never let the time get too far off
|
2008-07-04 22:00:00 +02:00
|
|
|
|
if((sv.time * 1000) - svs.realtime > HOST_FRAMETIME )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
{
|
2008-07-03 22:00:00 +02:00
|
|
|
|
Msg ("sv lowclamp\n");
|
2008-07-04 22:00:00 +02:00
|
|
|
|
svs.realtime = (sv.time * 1000 ) - HOST_FRAMETIME;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
2007-09-17 22:00:00 +02:00
|
|
|
|
|
2008-07-06 22:00:00 +02:00
|
|
|
|
//NET_Sleep((sv.time*1000) - svs.realtime);
|
2007-09-17 22:00:00 +02:00
|
|
|
|
SV_VM_End(); //end frame
|
2007-06-21 22:00:00 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update ping based on the last known frame from all clients
|
|
|
|
|
SV_CalcPings ();
|
|
|
|
|
|
|
|
|
|
// give the clients some timeslices
|
|
|
|
|
SV_GiveMsec ();
|
|
|
|
|
|
|
|
|
|
// let everything in the world think and move
|
2007-09-06 22:00:00 +02:00
|
|
|
|
SV_RunGameFrame ();
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// send messages back to the clients that had packets read this frame
|
|
|
|
|
SV_SendClientMessages ();
|
|
|
|
|
|
|
|
|
|
// send a heartbeat to the master if needed
|
|
|
|
|
Master_Heartbeat ();
|
|
|
|
|
|
2007-09-16 22:00:00 +02:00
|
|
|
|
// end the server VM frame
|
|
|
|
|
SV_VM_End();
|
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
|
|
|
|
|
================
|
|
|
|
|
*/
|
2007-09-09 22:00:00 +02:00
|
|
|
|
#define HEARTBEAT_SECONDS 300.0f
|
2007-06-21 22:00:00 +02:00
|
|
|
|
void Master_Heartbeat (void)
|
|
|
|
|
{
|
|
|
|
|
char *string;
|
|
|
|
|
int i;
|
|
|
|
|
|
2008-07-06 22:00:00 +02:00
|
|
|
|
if( host.type == HOST_DEDICATED )
|
|
|
|
|
return; // only dedicated servers send heartbeats
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// pgm post3.19 change, cvar pointer not validated before dereferencing
|
|
|
|
|
if (!public_server || !public_server->value)
|
2007-09-09 22:00:00 +02:00
|
|
|
|
return; // a private dedicated game
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// check for time wraparound
|
2007-09-09 22:00:00 +02:00
|
|
|
|
if (svs.last_heartbeat > svs.realtime) svs.last_heartbeat = svs.realtime;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-03 22:00:00 +02:00
|
|
|
|
if( svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS * 1000 )
|
2007-06-21 22:00:00 +02:00
|
|
|
|
return; // not time to send yet
|
|
|
|
|
|
2007-09-09 22:00:00 +02:00
|
|
|
|
svs.last_heartbeat = svs.realtime;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// send the same string that we would give for a status OOB command
|
|
|
|
|
string = SV_StatusString();
|
|
|
|
|
|
|
|
|
|
// send to group master
|
2007-09-09 22:00:00 +02:00
|
|
|
|
for (i = 0; i < MAX_MASTERS; i++)
|
|
|
|
|
{
|
2007-06-21 22:00:00 +02:00
|
|
|
|
if (master_adr[i].port)
|
|
|
|
|
{
|
2007-07-28 22:00:00 +02:00
|
|
|
|
Msg ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
|
2007-06-21 22:00:00 +02:00
|
|
|
|
Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "heartbeat\n%s", string);
|
|
|
|
|
}
|
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
|
|
|
|
|
=================
|
|
|
|
|
*/
|
|
|
|
|
void Master_Shutdown (void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2008-07-06 22:00:00 +02:00
|
|
|
|
if( host.type == HOST_DEDICATED )
|
|
|
|
|
return; // only dedicated servers send heartbeats
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// pgm post3.19 change, cvar pointer not validated before dereferencing
|
|
|
|
|
if (!public_server || !public_server->value)
|
|
|
|
|
return; // a private dedicated game
|
|
|
|
|
|
|
|
|
|
// send to group master
|
2008-01-13 22:00:00 +01:00
|
|
|
|
for(i = 0; i < MAX_MASTERS; i++)
|
|
|
|
|
{
|
2007-06-21 22:00:00 +02:00
|
|
|
|
if (master_adr[i].port)
|
|
|
|
|
{
|
2008-01-13 22:00:00 +01:00
|
|
|
|
if( i ) Msg ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
Only called at xash.exe startup, not for each game
|
|
|
|
|
===============
|
|
|
|
|
*/
|
|
|
|
|
void SV_Init (void)
|
|
|
|
|
{
|
2007-12-11 22:00:00 +01:00
|
|
|
|
SV_InitOperatorCommands();
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-06-29 22:00:00 +02:00
|
|
|
|
rcon_password = Cvar_Get( "rcon_password", "", 0, "remote connect password" );
|
|
|
|
|
Cvar_Get ("skill", "1", 0, "game skill level" );
|
|
|
|
|
Cvar_Get ("deathmatch", "0", CVAR_LATCH, "displays deathmatch state" );
|
|
|
|
|
Cvar_Get ("coop", "0", CVAR_LATCH, "displays cooperative state" );
|
|
|
|
|
Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO, "setup deathmatch flags" );
|
|
|
|
|
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" );
|
2008-07-03 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
sv_fps = Cvar_Get( "sv_fps", "60", CVAR_ARCHIVE|CVAR_LATCH, "running server at" );
|
2008-06-29 22:00:00 +02:00
|
|
|
|
maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH, "max count of clients for current game" );
|
|
|
|
|
hostname = Cvar_Get ("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_paused = Cvar_Get ("paused", "0", 0, "server pause" );
|
|
|
|
|
sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0, "client enforce time" );
|
|
|
|
|
allow_download = Cvar_Get ("allow_download", "1", CVAR_ARCHIVE, "allow download resources" );
|
|
|
|
|
sv_noreload = Cvar_Get ("sv_noreload", "0", 0, "ignore savepoints for singleplayer" );
|
|
|
|
|
|
|
|
|
|
sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH, "player accellerate in air" );
|
|
|
|
|
sv_maxvelocity = Cvar_Get("sv_maxvelocity", "2000", 0, "max world velocity" );
|
|
|
|
|
sv_gravity = Cvar_Get("sv_gravity", "800", 0, "world gravity" );
|
2007-09-06 22:00:00 +02:00
|
|
|
|
|
2008-06-29 22:00:00 +02:00
|
|
|
|
public_server = Cvar_Get ("public", "0", 0, "change server type from private to public" );
|
|
|
|
|
sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE, "max reconnect attempts" );
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-06 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
host.sv_running = true;
|
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.
|
|
|
|
|
==================
|
|
|
|
|
*/
|
|
|
|
|
void SV_FinalMessage (char *message, bool reconnect)
|
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
int i;
|
|
|
|
|
sv_client_t *cl;
|
|
|
|
|
byte msg_buf[MAX_MSGLEN];
|
|
|
|
|
sizebuf_t msg;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2008-07-09 22:00:00 +02:00
|
|
|
|
SZ_Init( &msg, msg_buf, sizeof(msg_buf));
|
|
|
|
|
MSG_WriteByte(&msg, svc_print);
|
|
|
|
|
MSG_WriteByte(&msg, PRINT_CONSOLE);
|
|
|
|
|
MSG_WriteString(&msg, message);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
if (reconnect)
|
2007-08-01 22:00:00 +02:00
|
|
|
|
{
|
2008-07-09 22:00:00 +02:00
|
|
|
|
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-09 22:00:00 +02:00
|
|
|
|
MSG_WriteByte (&msg, svc_disconnect);
|
2007-08-01 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
|
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
if (cl->state >= cs_connected)
|
2008-07-09 22:00:00 +02:00
|
|
|
|
Netchan_Transmit (&cl->netchan, msg.cursize, msg.data);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
2007-09-06 22:00:00 +02:00
|
|
|
|
for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
|
2007-06-21 22:00:00 +02:00
|
|
|
|
if (cl->state >= cs_connected)
|
2008-07-09 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
|
|
|
|
|
if(host.state == HOST_ERROR) return;
|
|
|
|
|
|
2007-11-21 22:00:00 +01:00
|
|
|
|
MsgDev(D_NOTE, "SV_Shutdown: %s\n", host.finalmsg );
|
|
|
|
|
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();
|
2008-05-20 22:00:00 +02:00
|
|
|
|
SV_FreeServerProgs();
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// free current level
|
|
|
|
|
memset (&sv, 0, sizeof(sv));
|
2007-11-14 22:00:00 +01:00
|
|
|
|
Host_SetServerState (sv.state);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
|
|
|
|
|
// free server static data
|
2008-05-22 22:00:00 +02:00
|
|
|
|
if (svs.clients) Mem_Free (svs.clients);
|
|
|
|
|
if (svs.client_entities) Mem_Free (svs.client_entities);
|
2007-06-21 22:00:00 +02:00
|
|
|
|
memset (&svs, 0, sizeof(svs));
|
2008-07-06 22:00:00 +02:00
|
|
|
|
host.sv_running = false;
|
2007-06-21 22:00:00 +02:00
|
|
|
|
}
|
|
|
|
|
|