diff --git a/src/server/sv_main.c b/src/server/sv_main.c index 09b7e11e..3c681e14 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -1,1046 +1,1097 @@ /* -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. - -*/ + * 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 + * + * ======================================================================= + */ #include "header/server.h" -netadr_t master_adr[MAX_MASTERS]; // address of group servers +#define HEARTBEAT_SECONDS 300 -client_t *sv_client; // current client +netadr_t master_adr [ MAX_MASTERS ]; /* address of group servers */ -cvar_t *sv_paused; -cvar_t *sv_timedemo; +client_t *sv_client; /* current client */ -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 - -cvar_t *sv_reconnect_limit; // minimum seconds between connect messages - -void Master_Shutdown (void); - - -//============================================================================ +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 */ +cvar_t *sv_reconnect_limit; /* minimum seconds between connect messages */ +void Master_Shutdown ( void ); /* -===================== -SV_DropClient - -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) + * 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 ) { - // add the disconnect - MSG_WriteByte (&drop->netchan.message, svc_disconnect); + /* add the disconnect */ + MSG_WriteByte( &drop->netchan.message, svc_disconnect ); - if (drop->state == cs_spawned) + if ( drop->state == cs_spawned ) { - // call the prog function for removing a client - // this will remove the body, among other things - ge->ClientDisconnect (drop->edict); + /* call the prog function for removing a client + this will remove the body, among other things */ + ge->ClientDisconnect( drop->edict ); } - if (drop->download) + if ( drop->download ) { - FS_FreeFile (drop->download); + FS_FreeFile( drop->download ); drop->download = NULL; } - drop->state = cs_zombie; // become free in a few seconds - drop->name[0] = 0; + drop->state = cs_zombie; /* become free in a few seconds */ + drop->name [ 0 ] = 0; } - - /* -============================================================================== - -CONNECTIONLESS COMMANDS - -============================================================================== -*/ - -/* -=============== -SV_StatusString - -Builds the string that is sent as heartbeats and status replies -=============== -*/ -char *SV_StatusString (void) + * Builds the string that is sent as heartbeats and status replies + */ +char * +SV_StatusString ( void ) { - char player[1024]; - static char status[MAX_MSGLEN - 16]; - int i; - client_t *cl; - int statusLength; - int playerLength; + 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); + strcpy( status, Cvar_Serverinfo() ); + strcat( status, "\n" ); + statusLength = (int) strlen( status ); - for (i=0 ; ivalue ; i++) + for ( i = 0; i < maxclients->value; i++ ) { - cl = &svs.clients[i]; - if (cl->state == cs_connected || cl->state == cs_spawned ) + cl = &svs.clients [ i ]; + + if ( ( cl->state == cs_connected ) || ( cl->state == cs_spawned ) ) { - 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); + 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 ); statusLength += playerLength; } } - return status; + return ( status ); } /* -================ -SVC_Status - -Responds with all the info that qplug or qspy can see -================ -*/ -void SVC_Status (void) + * Responds with all the info that qplug or qspy can see + */ +void +SVC_Status ( void ) { - Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString()); + Netchan_OutOfBandPrint( NS_SERVER, net_from, "print\n%s", SV_StatusString() ); +} + +void +SVC_Ack ( void ) +{ + Com_Printf( "Ping acknowledge from %s\n", NET_AdrToString( net_from ) ); } /* -================ -SVC_Ack - -================ -*/ -void SVC_Ack (void) + * Responds with short info for broadcast scans + * The second parameter should be the current protocol version number. + */ +void +SVC_Info ( void ) { - Com_Printf ("Ping acknowledge from %s\n", NET_AdrToString(net_from)); -} + char string [ 64 ]; + int i, count; + int version; -/* -================ -SVC_Info + if ( maxclients->value == 1 ) + { + return; /* ignore in single player */ + } -Responds with short info for broadcast scans -The second parameter should be the current protocol version number. -================ -*/ -void SVC_Info (void) -{ - char string[64]; - int i, count; - int version; + version = atoi( Cmd_Argv( 1 ) ); - if (maxclients->value == 1) - return; // ignore in single player - - version = atoi (Cmd_Argv(1)); - - if (version != PROTOCOL_VERSION) - Com_sprintf (string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string)); + if ( version != PROTOCOL_VERSION ) + { + Com_sprintf( string, sizeof ( string ), "%s: wrong version\n", hostname->string, sizeof ( string ) ); + } else { count = 0; - for (i=0 ; ivalue ; i++) - if (svs.clients[i].state >= cs_connected) - count++; - Com_sprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value); + for ( i = 0; i < maxclients->value; i++ ) + { + if ( svs.clients [ i ].state >= cs_connected ) + { + count++; + } + } + + Com_sprintf( string, sizeof ( string ), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int) maxclients->value ); } - Netchan_OutOfBandPrint (NS_SERVER, net_from, "info\n%s", string); + Netchan_OutOfBandPrint( NS_SERVER, net_from, "info\n%s", string ); } /* -================ -SVC_Ping - -Just responds with an acknowledgement -================ -*/ -void SVC_Ping (void) + * SVC_Ping + */ +void +SVC_Ping ( void ) { - Netchan_OutOfBandPrint (NS_SERVER, net_from, "ack"); + Netchan_OutOfBandPrint( NS_SERVER, net_from, "ack" ); } - /* -================= -SVC_GetChallenge - -Returns a challenge number that can be used -in a subsequent client_connect command. -We do this to prevent denial of service attacks that -flood the server with invalid connection IPs. With a -challenge, they must give a valid IP address. -================= -*/ -void SVC_GetChallenge (void) + * Returns a challenge number that can be used + * in a subsequent client_connect command. + * We do this to prevent denial of service attacks that + * flood the server with invalid connection IPs. With a + * challenge, they must give a valid IP address. + */ +void +SVC_GetChallenge ( void ) { - int i; - int oldest; - int oldestTime; + int i; + int oldest; + int oldestTime; oldest = 0; oldestTime = 0x7fffffff; - // see if we already have a challenge for this ip - for (i = 0 ; i < MAX_CHALLENGES ; i++) + /* see if we already have a challenge for this ip */ + for ( i = 0; i < MAX_CHALLENGES; i++ ) { - if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) - break; - if (svs.challenges[i].time < oldestTime) + if ( NET_CompareBaseAdr( net_from, svs.challenges [ i ].adr ) ) { - oldestTime = svs.challenges[i].time; + break; + } + + if ( svs.challenges [ i ].time < oldestTime ) + { + oldestTime = svs.challenges [ i ].time; oldest = i; } } - if (i == MAX_CHALLENGES) + if ( i == MAX_CHALLENGES ) { - // overwrite the oldest - svs.challenges[oldest].challenge = rand() & 0x7fff; - svs.challenges[oldest].adr = net_from; - svs.challenges[oldest].time = curtime; + /* overwrite the oldest */ + svs.challenges [ oldest ].challenge = rand() & 0x7fff; + svs.challenges [ oldest ].adr = net_from; + svs.challenges [ oldest ].time = curtime; i = oldest; } - // send it back - Netchan_OutOfBandPrint (NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge); + /* send it back */ + Netchan_OutOfBandPrint( NS_SERVER, net_from, "challenge %i", svs.challenges [ i ].challenge ); } /* -================== -SVC_DirectConnect - -A connection request that did not come from the master -================== -*/ -void SVC_DirectConnect (void) + * A connection request that did not come from the master + */ +void +SVC_DirectConnect ( void ) { - char userinfo[MAX_INFO_STRING]; - netadr_t adr; - int i; - client_t *cl, *newcl; - client_t temp; - edict_t *ent; - int edictnum; - int version; - int qport; - int challenge; + char userinfo [ MAX_INFO_STRING ]; + netadr_t adr; + int i; + client_t *cl, *newcl; + client_t temp; + edict_t *ent; + int edictnum; + int version; + int qport; + int challenge; adr = net_from; - Com_DPrintf ("SVC_DirectConnect ()\n"); + Com_DPrintf( "SVC_DirectConnect ()\n" ); - version = atoi(Cmd_Argv(1)); - if (version != PROTOCOL_VERSION) + version = atoi( Cmd_Argv( 1 ) ); + + if ( version != PROTOCOL_VERSION ) { - Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION); - Com_DPrintf (" rejected connect from version %i\n", version); + Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION ); + Com_DPrintf( " rejected connect from version %i\n", version ); return; } - qport = atoi(Cmd_Argv(2)); + qport = atoi( Cmd_Argv( 2 ) ); - challenge = atoi(Cmd_Argv(3)); + challenge = atoi( Cmd_Argv( 3 ) ); - strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1); - userinfo[sizeof(userinfo) - 1] = 0; + strncpy( userinfo, Cmd_Argv( 4 ), sizeof ( userinfo ) - 1 ); + userinfo [ sizeof ( userinfo ) - 1 ] = 0; - // force the IP key/value pair so the game can filter based on ip - Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from)); + /* force the IP key/value pair so the game can filter based on ip */ + Info_SetValueForKey( userinfo, "ip", NET_AdrToString( net_from ) ); - // attractloop servers are ONLY for local clients - if (sv.attractloop) + /* attractloop servers are ONLY for local clients */ + if ( sv.attractloop ) { - if (!NET_IsLocalAddress (adr)) + if ( !NET_IsLocalAddress( adr ) ) { - Com_Printf ("Remote connect in attract loop. Ignored.\n"); - Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n"); + Com_Printf( "Remote connect in attract loop. Ignored.\n" ); + Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nConnection refused.\n" ); return; } } - // see if the challenge is valid - if (!NET_IsLocalAddress (adr)) + /* see if the challenge is valid */ + if ( !NET_IsLocalAddress( adr ) ) { - for (i=0 ; ivalue ; i++,cl++) + /* if there is already a slot for this ip, reuse it */ + for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ ) { - if (cl->state < cs_connected) - continue; - if (NET_CompareBaseAdr (adr, cl->netchan.remote_address) - && ( cl->netchan.qport == qport - || adr.port == cl->netchan.remote_address.port ) ) + if ( cl->state < cs_connected ) { - if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000)) + continue; + } + + if ( NET_CompareBaseAdr( adr, cl->netchan.remote_address ) && + ( ( cl->netchan.qport == qport ) || + ( adr.port == cl->netchan.remote_address.port ) ) ) + { + if ( !NET_IsLocalAddress( adr ) && ( ( svs.realtime - cl->lastconnect ) < ( (int) sv_reconnect_limit->value * 1000 ) ) ) { - Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr)); + Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AdrToString( adr ) ); return; } - Com_Printf ("%s:reconnect\n", NET_AdrToString (adr)); + + Com_Printf( "%s:reconnect\n", NET_AdrToString( adr ) ); newcl = cl; goto gotnewcl; } } - // find a client slot + /* find a client slot */ newcl = NULL; - for (i=0,cl=svs.clients ; ivalue ; i++,cl++) + + for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ ) { - if (cl->state == cs_free) + if ( cl->state == cs_free ) { newcl = cl; break; } } - if (!newcl) + + if ( !newcl ) { - Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n"); - Com_DPrintf ("Rejected a connection.\n"); + Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nServer is full.\n" ); + Com_DPrintf( "Rejected a connection.\n" ); return; } -gotnewcl: - // build a new connection - // accept the new client - // this is the only place a client_t is ever initialized +gotnewcl: + /* build a new connection + accept the new client + this is the only place a client_t is ever initialized */ *newcl = temp; sv_client = newcl; - edictnum = (newcl-svs.clients)+1; - ent = EDICT_NUM(edictnum); + edictnum = ( newcl - svs.clients ) + 1; + ent = EDICT_NUM( edictnum ); newcl->edict = ent; - newcl->challenge = challenge; // save challenge for checksumming + newcl->challenge = challenge; /* save challenge for checksumming */ - // get the game a chance to reject this connection or modify the userinfo - if (!(ge->ClientConnect (ent, userinfo))) + /* get the game a chance to reject this connection or modify the userinfo */ + if ( !( ge->ClientConnect( ent, userinfo ) ) ) { - if (*Info_ValueForKey (userinfo, "rejmsg")) - Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n", - Info_ValueForKey (userinfo, "rejmsg")); + if ( *Info_ValueForKey( userinfo, "rejmsg" ) ) + { + Netchan_OutOfBandPrint( NS_SERVER, adr, "print\n%s\nConnection refused.\n", + Info_ValueForKey( userinfo, "rejmsg" ) ); + } else - Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" ); - Com_DPrintf ("Game rejected a connection.\n"); + { + Netchan_OutOfBandPrint( NS_SERVER, adr, "print\nConnection refused.\n" ); + } + + Com_DPrintf( "Game rejected a connection.\n" ); return; } - // parse some info from the info strings - strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1); - SV_UserinfoChanged (newcl); + /* parse some info from the info strings */ + strncpy( newcl->userinfo, userinfo, sizeof ( newcl->userinfo ) - 1 ); + SV_UserinfoChanged( newcl ); - // send the connect packet to the client - Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect"); + /* send the connect packet to the client */ + Netchan_OutOfBandPrint( NS_SERVER, adr, "client_connect" ); - Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport); + Netchan_Setup( NS_SERVER, &newcl->netchan, adr, qport ); newcl->state = cs_connected; - - SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) ); + + SZ_Init( &newcl->datagram, newcl->datagram_buf, sizeof ( newcl->datagram_buf ) ); newcl->datagram.allowoverflow = true; - newcl->lastmessage = svs.realtime; // don't timeout + newcl->lastmessage = svs.realtime; /* don't timeout */ newcl->lastconnect = svs.realtime; } -int Rcon_Validate (void) +int +Rcon_Validate ( void ) { - if (!strlen (rcon_password->string)) - return 0; + if ( !strlen( rcon_password->string ) ) + { + return ( 0 ); + } - if (strcmp (Cmd_Argv(1), rcon_password->string) ) - return 0; + if ( strcmp( Cmd_Argv( 1 ), rcon_password->string ) ) + { + return ( 0 ); + } - return 1; + return ( 1 ); } /* -=============== -SVC_RemoteCommand - -A client issued an rcon command. -Shift down the remaining args -Redirect all printfs -=============== -*/ -void SVC_RemoteCommand (void) + * A client issued an rcon command. + * Shift down the remaining args + * Redirect all printfs + */ +void +SVC_RemoteCommand ( void ) { - int i; - char remaining[1024]; + int i; + char remaining [ 1024 ]; - i = Rcon_Validate (); + i = Rcon_Validate(); - if (i == 0) - Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4); - else - Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4); - - Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); - - if (!Rcon_Validate ()) + if ( i == 0 ) { - Com_Printf ("Bad rcon_password.\n"); + Com_Printf( "Bad rcon from %s:\n%s\n", NET_AdrToString( net_from ), net_message.data + 4 ); } else { - remaining[0] = 0; + Com_Printf( "Rcon from %s:\n%s\n", NET_AdrToString( net_from ), net_message.data + 4 ); + } - for (i=2 ; iping variables -=================== -*/ -void SV_CalcPings (void) -{ - int i, j; - client_t *cl; - int total, count; - - for (i=0 ; ivalue ; i++) + if ( !strcmp( c, "ping" ) ) { - cl = &svs.clients[i]; - if (cl->state != cs_spawned ) + SVC_Ping(); + } + else if ( !strcmp( c, "ack" ) ) + { + SVC_Ack(); + } + else if ( !strcmp( c, "status" ) ) + { + SVC_Status(); + } + else if ( !strcmp( c, "info" ) ) + { + SVC_Info(); + } + else if ( !strcmp( c, "getchallenge" ) ) + { + SVC_GetChallenge(); + } + else if ( !strcmp( c, "connect" ) ) + { + SVC_DirectConnect(); + } + else if ( !strcmp( c, "rcon" ) ) + { + SVC_RemoteCommand(); + } + else + { + Com_Printf( "bad connectionless packet from %s:\n%s\n", + NET_AdrToString( net_from ), s ); + } +} + +/* ============================================================================ */ + +/* + * Updates the cl->ping variables + */ +void +SV_CalcPings ( void ) +{ + int i, j; + client_t *cl; + int total, count; + + for ( i = 0; i < maxclients->value; i++ ) + { + cl = &svs.clients [ i ]; + + if ( cl->state != cs_spawned ) + { continue; + } total = 0; count = 0; - for (j=0 ; jframe_latency[j] > 0) + if ( cl->frame_latency [ j ] > 0 ) { count++; - total += cl->frame_latency[j]; + total += cl->frame_latency [ j ]; } } - if (!count) - cl->ping = 0; - else - cl->ping = total / count; - // let the game dll know about the ping + if ( !count ) + { + cl->ping = 0; + } + else + { + cl->ping = total / count; + } + + /* let the game dll know about the ping */ cl->edict->client->ping = cl->ping; } } - /* -=================== -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) + * 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; - client_t *cl; + int i; + client_t *cl; - if (sv.framenum & 15) + if ( sv.framenum & 15 ) + { return; - - for (i=0 ; ivalue ; i++) - { - cl = &svs.clients[i]; - if (cl->state == cs_free ) - continue; - - cl->commandMsec = 1800; // 1600 + some slop } -} - -/* -================= -SV_ReadPackets -================= -*/ -void SV_ReadPackets (void) -{ - int i; - client_t *cl; - int qport; - - while (NET_GetPacket (NS_SERVER, &net_from, &net_message)) + for ( i = 0; i < maxclients->value; i++ ) { - // check for connectionless packet (0xffffffff) first - if (*(int *)net_message.data == -1) + cl = &svs.clients [ i ]; + + if ( cl->state == cs_free ) { - SV_ConnectionlessPacket (); continue; } - // 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 = MSG_ReadShort (&net_message) & 0xffff; + cl->commandMsec = 1800; /* 1600 + some slop */ + } +} - // check for packets from connected clients - for (i=0, cl=svs.clients ; ivalue ; i++,cl++) +void +SV_ReadPackets ( void ) +{ + int i; + client_t *cl; + int qport; + + while ( NET_GetPacket( NS_SERVER, &net_from, &net_message ) ) + { + /* check for connectionless packet (0xffffffff) first */ + if ( *(int *) net_message.data == -1 ) { - if (cl->state == cs_free) - 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) + SV_ConnectionlessPacket(); + continue; + } + + /* 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 = MSG_ReadShort( &net_message ) & 0xffff; + + /* check for packets from connected clients */ + for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ ) + { + if ( cl->state == cs_free ) { - Com_Printf ("SV_ReadPackets: fixing up a translated port\n"); + 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 ) + { + Com_Printf( "SV_ReadPackets: fixing up a translated port\n" ); cl->netchan.remote_address.port = net_from.port; } - if (Netchan_Process(&cl->netchan, &net_message)) - { // this is a valid, sequenced packet, so process it - if (cl->state != cs_zombie) + if ( Netchan_Process( &cl->netchan, &net_message ) ) + { + /* this is a valid, sequenced packet, so process it */ + if ( cl->state != cs_zombie ) { - cl->lastmessage = svs.realtime; // don't timeout - if (!(sv.demofile && sv.state == ss_demo)) - SV_ExecuteClientMessage (cl); + cl->lastmessage = svs.realtime; /* don't timeout */ + + if ( !( sv.demofile && ( sv.state == ss_demo ) ) ) + { + SV_ExecuteClientMessage( cl ); + } } } + break; } - - if (i != maxclients->value) + + if ( i != maxclients->value ) + { continue; + } } } /* -================== -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. - -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) + * 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 ) { - int i; - client_t *cl; - int droppoint; - int zombiepoint; + int i; + client_t *cl; + int droppoint; + int zombiepoint; - droppoint = svs.realtime - 1000*timeout->value; - zombiepoint = svs.realtime - 1000*zombietime->value; + droppoint = svs.realtime - 1000 * timeout->value; + zombiepoint = svs.realtime - 1000 * zombietime->value; - for (i=0,cl=svs.clients ; ivalue ; i++,cl++) + for ( i = 0, cl = svs.clients; i < maxclients->value; i++, cl++ ) { - // message times may be wrong across a changelevel - if (cl->lastmessage > svs.realtime) + /* message times may be wrong across a changelevel */ + if ( cl->lastmessage > svs.realtime ) + { cl->lastmessage = svs.realtime; + } - if (cl->state == cs_zombie - && cl->lastmessage < zombiepoint) + if ( ( cl->state == cs_zombie ) && + ( cl->lastmessage < zombiepoint ) ) { - cl->state = cs_free; // can now be reused + cl->state = cs_free; /* can now be reused */ continue; } - if ( (cl->state == cs_connected || cl->state == cs_spawned) - && cl->lastmessage < droppoint) + + if ( ( ( cl->state == cs_connected ) || ( cl->state == cs_spawned ) ) && + ( cl->lastmessage < droppoint ) ) { - SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); - SV_DropClient (cl); - cl->state = cs_free; // don't bother with zombie state + SV_BroadcastPrintf( PRINT_HIGH, "%s timed out\n", cl->name ); + SV_DropClient( cl ); + cl->state = cs_free; /* don't bother with zombie state */ } } } /* -================ -SV_PrepWorldFrame - -This has to be done before the world logic, because -player processing happens outside RunWorldFrame -================ -*/ -void SV_PrepWorldFrame (void) + * This has to be done before the world logic, because + * player processing happens outside RunWorldFrame + */ +void +SV_PrepWorldFrame ( void ) { - edict_t *ent; - int i; + edict_t *ent; + int i; - for (i=0 ; inum_edicts ; i++, ent++) + for ( i = 0; i < ge->num_edicts; i++, ent++ ) { - ent = EDICT_NUM(i); - // events only last for a single message + ent = EDICT_NUM( i ); + + /* events only last for a single message */ ent->s.event = 0; } - } - -/* -================= -SV_RunGameFrame -================= -*/ -void SV_RunGameFrame (void) +void +SV_RunGameFrame ( void ) { #ifndef DEDICATED_ONLY - if (host_speeds->value) - time_before_game = Sys_Milliseconds (); + + if ( host_speeds->value ) + { + time_before_game = Sys_Milliseconds(); + } + #endif - // 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 + /* 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++; - sv.time = sv.framenum*100; + sv.time = sv.framenum * 100; - // don't run if paused - if (!sv_paused->value || maxclients->value > 1) + /* don't run if paused */ + if ( !sv_paused->value || ( maxclients->value > 1 ) ) { - ge->RunFrame (); + ge->RunFrame(); - // never get more than one tic behind - if (sv.time < svs.realtime) + /* never get more than one tic behind */ + if ( sv.time < svs.realtime ) { - if (sv_showclamp->value) - Com_Printf ("sv highclamp\n"); + if ( sv_showclamp->value ) + { + Com_Printf( "sv highclamp\n" ); + } + svs.realtime = sv.time; } } -#ifndef DEDICATED_ONLY - if (host_speeds->value) - time_after_game = Sys_Milliseconds (); +#ifndef DEDICATED_ONLY + + if ( host_speeds->value ) + { + time_after_game = Sys_Milliseconds(); + } + #endif } -/* -================== -SV_Frame - -================== -*/ -void SV_Frame (int msec) +void +SV_Frame ( int msec ) { -#ifndef DEDICATED_ONLY +#ifndef DEDICATED_ONLY time_before_game = time_after_game = 0; #endif - // if server is not active, do nothing - if (!svs.initialized) - return; - - svs.realtime += msec; - - // keep the random time dependent - rand (); - - // check timeouts - SV_CheckTimeouts (); - - // get packets from clients - SV_ReadPackets (); - - // move autonomous things around if enough time has passed - if (!sv_timedemo->value && svs.realtime < sv.time) + /* if server is not active, do nothing */ + if ( !svs.initialized ) { - // never let the time get too far off - if (sv.time - svs.realtime > 100) - { - if (sv_showclamp->value) - Com_Printf ("sv lowclamp\n"); - svs.realtime = sv.time - 100; - } - NET_Sleep(sv.time - svs.realtime); return; } - // update ping based on the last known frame from all clients - SV_CalcPings (); + svs.realtime += msec; - // give the clients some timeslices - SV_GiveMsec (); + /* keep the random time dependent */ + rand(); - // let everything in the world think and move - SV_RunGameFrame (); + /* check timeouts */ + SV_CheckTimeouts(); - // send messages back to the clients that had packets read this frame - SV_SendClientMessages (); + /* get packets from clients */ + SV_ReadPackets(); - // save the entire world state if recording a serverdemo - SV_RecordDemoMessage (); + /* move autonomous things around if enough time has passed */ + if ( !sv_timedemo->value && ( svs.realtime < sv.time ) ) + { + /* never let the time get too far off */ + if ( sv.time - svs.realtime > 100 ) + { + if ( sv_showclamp->value ) + { + Com_Printf( "sv lowclamp\n" ); + } - // send a heartbeat to the master if needed - Master_Heartbeat (); + svs.realtime = sv.time - 100; + } - // clear teleport flags, etc for next frame - SV_PrepWorldFrame (); + NET_Sleep( sv.time - svs.realtime ); + 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 */ + SV_RunGameFrame(); + + /* send messages back to the clients that had packets read this frame */ + SV_SendClientMessages(); + + /* save the entire world state if recording a serverdemo */ + SV_RecordDemoMessage(); + + /* send a heartbeat to the master if needed */ + Master_Heartbeat(); + + /* clear teleport flags, etc for next frame */ + SV_PrepWorldFrame(); } -//============================================================================ - /* -================ -Master_Heartbeat - -Send a message to the master every few minutes to -let it know we are alive, and log information -================ -*/ -#define HEARTBEAT_SECONDS 300 -void Master_Heartbeat (void) + * Send a message to the master every few minutes to + * let it know we are alive, and log information + */ +void +Master_Heartbeat ( void ) { - char *string; - int i; + char *string; + int i; - // pgm post3.19 change, cvar pointer not validated before dereferencing - if (!dedicated || !dedicated->value) - return; // only dedicated servers send heartbeats + if ( !dedicated || !dedicated->value ) + { + return; /* only dedicated servers send heartbeats */ + } - // pgm post3.19 change, cvar pointer not validated before dereferencing - if (!public_server || !public_server->value) - return; // a private dedicated game + if ( !public_server || !public_server->value ) + { + return; /* a private dedicated game */ + } - // check for time wraparound - if (svs.last_heartbeat > svs.realtime) + /* check for time wraparound */ + if ( svs.last_heartbeat > svs.realtime ) + { svs.last_heartbeat = svs.realtime; + } - if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000) - return; // not time to send yet + if ( svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS * 1000 ) + { + return; /* not time to send yet */ + } svs.last_heartbeat = svs.realtime; - // send the same string that we would give for a status OOB command + /* send the same string that we would give for a status OOB command */ string = SV_StatusString(); - // send to group master - for (i=0 ; ivalue) - return; // only dedicated servers send heartbeats - - // 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 - for (i=0 ; i 0) - Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i])); - Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "shutdown"); - } -} - -//============================================================================ - - -/* -================= -SV_UserinfoChanged - -Pull specific info from a newly changed userinfo string -into a more C freindly form. -================= -*/ -void SV_UserinfoChanged (client_t *cl) -{ - 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 ; iname) ; i++) - cl->name[i] &= 127; - - // rate command - val = Info_ValueForKey (cl->userinfo, "rate"); - if (strlen(val)) + /* send to group master */ + for ( i = 0; i < MAX_MASTERS; i++ ) { - i = atoi(val); + if ( master_adr [ i ].port ) + { + Com_Printf( "Sending heartbeat to %s\n", NET_AdrToString( master_adr [ i ] ) ); + Netchan_OutOfBandPrint( NS_SERVER, master_adr [ i ], "heartbeat\n%s", string ); + } + } +} + +/* + * Informs all masters that this server is going down + */ +void +Master_Shutdown ( void ) +{ + int i; + + if ( !dedicated || !dedicated->value ) + { + return; /* only dedicated servers send heartbeats */ + } + + if ( !public_server || !public_server->value ) + { + return; /* a private dedicated game */ + } + + /* send to group master */ + for ( i = 0; i < MAX_MASTERS; i++ ) + { + if ( master_adr [ i ].port ) + { + if ( i > 0 ) + { + Com_Printf( "Sending heartbeat to %s\n", NET_AdrToString( master_adr [ i ] ) ); + } + + Netchan_OutOfBandPrint( NS_SERVER, master_adr [ i ], "shutdown" ); + } + } +} + +/* + * Pull specific info from a newly changed userinfo string + * into a more C freindly form. + */ +void +SV_UserinfoChanged ( client_t *cl ) +{ + 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++ ) + { + cl->name [ i ] &= 127; + } + + /* rate command */ + val = Info_ValueForKey( cl->userinfo, "rate" ); + + if ( strlen( val ) ) + { + i = atoi( val ); cl->rate = i; - if (cl->rate < 100) + + if ( cl->rate < 100 ) + { cl->rate = 100; - if (cl->rate > 15000) + } + + if ( cl->rate > 15000 ) + { cl->rate = 15000; + } } else - cl->rate = 5000; - - // msg command - val = Info_ValueForKey (cl->userinfo, "msg"); - if (strlen(val)) { - cl->messagelevel = atoi(val); + cl->rate = 5000; } -} + /* msg command */ + val = Info_ValueForKey( cl->userinfo, "msg" ); - -//============================================================================ - -/* -=============== -SV_Init - -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)); + if ( strlen( val ) ) + { + cl->messagelevel = atoi( val ); + } } /* -================== -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, qboolean reconnect) + * Only called at quake2.exe startup, not for each game + */ +void +SV_Init ( void ) { - int i; - client_t *cl; - - SZ_Clear (&net_message); - MSG_WriteByte (&net_message, svc_print); - MSG_WriteByte (&net_message, PRINT_HIGH); - MSG_WriteString (&net_message, message); + SV_InitOperatorCommands(); - if (reconnect) - MSG_WriteByte (&net_message, svc_reconnect); + 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 ) ); +} + +/* + * 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; + + SZ_Clear( &net_message ); + MSG_WriteByte( &net_message, svc_print ); + MSG_WriteByte( &net_message, PRINT_HIGH ); + MSG_WriteString( &net_message, message ); + + if ( reconnect ) + { + MSG_WriteByte( &net_message, svc_reconnect ); + } else - MSG_WriteByte (&net_message, svc_disconnect); + { + MSG_WriteByte( &net_message, svc_disconnect ); + } - // send it twice - // stagger the packets to crutch operating system limited buffers + /* 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 ; ivalue ; i++, cl++) - if (cl->state >= cs_connected) - Netchan_Transmit (&cl->netchan, net_message.cursize - , net_message.data); - - for (i=0, cl = svs.clients ; ivalue ; 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 ); + } + } } - - /* -================ -SV_Shutdown - -Called when each game quits, -before Sys_Quit or Sys_Error -================ -*/ -void SV_Shutdown (char *finalmsg, qboolean reconnect) + * Called when each game quits, + * before Sys_Quit or Sys_Error + */ +void +SV_Shutdown ( char *finalmsg, qboolean reconnect ) { - if (svs.clients) - SV_FinalMessage (finalmsg, reconnect); + if ( svs.clients ) + { + SV_FinalMessage( finalmsg, reconnect ); + } - Master_Shutdown (); - SV_ShutdownGameProgs (); + Master_Shutdown(); + SV_ShutdownGameProgs(); - // free current level - if (sv.demofile) - FS_FCloseFile ((size_t)sv.demofile); - memset (&sv, 0, sizeof(sv)); - Com_SetServerState (sv.state); + /* free current level */ + if ( sv.demofile ) + { + FS_FCloseFile( (size_t) sv.demofile ); + } - // free server static data - if (svs.clients) - Z_Free (svs.clients); - if (svs.client_entities) - Z_Free (svs.client_entities); - if (svs.demofile) - FS_FCloseFile ((size_t)svs.demofile); - memset (&svs, 0, sizeof(svs)); + memset( &sv, 0, sizeof ( sv ) ); + Com_SetServerState( sv.state ); + + /* free server static data */ + if ( svs.clients ) + { + Z_Free( svs.clients ); + } + + if ( svs.client_entities ) + { + Z_Free( svs.client_entities ); + } + + if ( svs.demofile ) + { + FS_FCloseFile( (size_t) svs.demofile ); + } + + memset( &svs, 0, sizeof ( svs ) ); }