Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

sv_main.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 
00023 #include "server.h"
00024 
00025 serverStatic_t  svs;                // persistant server info
00026 server_t        sv;                 // local server
00027 vm_t            *gvm = NULL;                // game virtual machine // bk001212 init
00028 
00029 cvar_t  *sv_fps;                // time rate for running non-clients
00030 cvar_t  *sv_timeout;            // seconds without any message
00031 cvar_t  *sv_zombietime;         // seconds to sink messages after disconnect
00032 cvar_t  *sv_rconPassword;       // password for remote server commands
00033 cvar_t  *sv_privatePassword;    // password for the privateClient slots
00034 cvar_t  *sv_allowDownload;
00035 cvar_t  *sv_maxclients;
00036 
00037 cvar_t  *sv_privateClients;     // number of clients reserved for password
00038 cvar_t  *sv_hostname;
00039 cvar_t  *sv_master[MAX_MASTER_SERVERS];     // master server ip address
00040 cvar_t  *sv_reconnectlimit;     // minimum seconds between connect messages
00041 cvar_t  *sv_showloss;           // report when usercmds are lost
00042 cvar_t  *sv_padPackets;         // add nop bytes to messages
00043 cvar_t  *sv_killserver;         // menu system can set to 1 to shut server down
00044 cvar_t  *sv_mapname;
00045 cvar_t  *sv_mapChecksum;
00046 cvar_t  *sv_serverid;
00047 cvar_t  *sv_maxRate;
00048 cvar_t  *sv_minPing;
00049 cvar_t  *sv_maxPing;
00050 cvar_t  *sv_gametype;
00051 cvar_t  *sv_pure;
00052 cvar_t  *sv_floodProtect;
00053 cvar_t  *sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491)
00054 cvar_t  *sv_strictAuth;
00055 
00056 /*
00057 =============================================================================
00058 
00059 EVENT MESSAGES
00060 
00061 =============================================================================
00062 */
00063 
00064 /*
00065 ===============
00066 SV_ExpandNewlines
00067 
00068 Converts newlines to "\n" so a line prints nicer
00069 ===============
00070 */
00071 char    *SV_ExpandNewlines( char *in ) {
00072     static  char    string[1024];
00073     int     l;
00074 
00075     l = 0;
00076     while ( *in && l < sizeof(string) - 3 ) {
00077         if ( *in == '\n' ) {
00078             string[l++] = '\\';
00079             string[l++] = 'n';
00080         } else {
00081             string[l++] = *in;
00082         }
00083         in++;
00084     }
00085     string[l] = 0;
00086 
00087     return string;
00088 }
00089 
00090 /*
00091 ======================
00092 SV_ReplacePendingServerCommands
00093 
00094   This is ugly
00095 ======================
00096 */
00097 int SV_ReplacePendingServerCommands( client_t *client, const char *cmd ) {
00098     int i, index, csnum1, csnum2;
00099 
00100     for ( i = client->reliableSent+1; i <= client->reliableSequence; i++ ) {
00101         index = i & ( MAX_RELIABLE_COMMANDS - 1 );
00102         //
00103         if ( !Q_strncmp(cmd, client->reliableCommands[ index ], strlen("cs")) ) {
00104             sscanf(cmd, "cs %i", &csnum1);
00105             sscanf(client->reliableCommands[ index ], "cs %i", &csnum2);
00106             if ( csnum1 == csnum2 ) {
00107                 Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
00108                 /*
00109                 if ( client->netchan.remoteAddress.type != NA_BOT ) {
00110                     Com_Printf( "WARNING: client %i removed double pending config string %i: %s\n", client-svs.clients, csnum1, cmd );
00111                 }
00112                 */
00113                 return qtrue;
00114             }
00115         }
00116     }
00117     return qfalse;
00118 }
00119 
00120 /*
00121 ======================
00122 SV_AddServerCommand
00123 
00124 The given command will be transmitted to the client, and is guaranteed to
00125 not have future snapshot_t executed before it is executed
00126 ======================
00127 */
00128 void SV_AddServerCommand( client_t *client, const char *cmd ) {
00129     int     index, i;
00130 
00131     // this is very ugly but it's also a waste to for instance send multiple config string updates
00132     // for the same config string index in one snapshot
00133 //  if ( SV_ReplacePendingServerCommands( client, cmd ) ) {
00134 //      return;
00135 //  }
00136 
00137     client->reliableSequence++;
00138     // if we would be losing an old command that hasn't been acknowledged,
00139     // we must drop the connection
00140     // we check == instead of >= so a broadcast print added by SV_DropClient()
00141     // doesn't cause a recursive drop client
00142     if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) {
00143         Com_Printf( "===== pending server commands =====\n" );
00144         for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) {
00145             Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
00146         }
00147         Com_Printf( "cmd %5d: %s\n", i, cmd );
00148         SV_DropClient( client, "Server command overflow" );
00149         return;
00150     }
00151     index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
00152     Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
00153 }
00154 
00155 
00156 /*
00157 =================
00158 SV_SendServerCommand
00159 
00160 Sends a reliable command string to be interpreted by 
00161 the client game module: "cp", "print", "chat", etc
00162 A NULL client will broadcast to all clients
00163 =================
00164 */
00165 void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) {
00166     va_list     argptr;
00167     byte        message[MAX_MSGLEN];
00168     client_t    *client;
00169     int         j;
00170     
00171     va_start (argptr,fmt);
00172     Q_vsnprintf ((char *)message, sizeof(message), fmt,argptr);
00173     va_end (argptr);
00174 
00175     if ( cl != NULL ) {
00176         SV_AddServerCommand( cl, (char *)message );
00177         return;
00178     }
00179 
00180     // hack to echo broadcast prints to console
00181     if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) {
00182         Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) );
00183     }
00184 
00185     // send the data to all relevent clients
00186     for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) {
00187         if ( client->state < CS_PRIMED ) {
00188             continue;
00189         }
00190         SV_AddServerCommand( client, (char *)message );
00191     }
00192 }
00193 
00194 
00195 /*
00196 ==============================================================================
00197 
00198 MASTER SERVER FUNCTIONS
00199 
00200 ==============================================================================
00201 */
00202 
00203 /*
00204 ================
00205 SV_MasterHeartbeat
00206 
00207 Send a message to the masters every few minutes to
00208 let it know we are alive, and log information.
00209 We will also have a heartbeat sent when a server
00210 changes from empty to non-empty, and full to non-full,
00211 but not on every player enter or exit.
00212 ================
00213 */
00214 #define HEARTBEAT_MSEC  300*1000
00215 #define HEARTBEAT_GAME  "QuakeArena-1"
00216 void SV_MasterHeartbeat( void ) {
00217     static netadr_t adr[MAX_MASTER_SERVERS];
00218     int         i;
00219 
00220     // "dedicated 1" is for lan play, "dedicated 2" is for inet public play
00221     if ( !com_dedicated || com_dedicated->integer != 2 ) {
00222         return;     // only dedicated servers send heartbeats
00223     }
00224 
00225     // if not time yet, don't send anything
00226     if ( svs.time < svs.nextHeartbeatTime ) {
00227         return;
00228     }
00229     svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC;
00230 
00231 
00232     // send to group masters
00233     for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) {
00234         if ( !sv_master[i]->string[0] ) {
00235             continue;
00236         }
00237 
00238         // see if we haven't already resolved the name
00239         // resolving usually causes hitches on win95, so only
00240         // do it when needed
00241         if ( sv_master[i]->modified ) {
00242             sv_master[i]->modified = qfalse;
00243     
00244             Com_Printf( "Resolving %s\n", sv_master[i]->string );
00245             if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) {
00246                 // if the address failed to resolve, clear it
00247                 // so we don't take repeated dns hits
00248                 Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string );
00249                 Cvar_Set( sv_master[i]->name, "" );
00250                 sv_master[i]->modified = qfalse;
00251                 continue;
00252             }
00253             if ( !strstr( ":", sv_master[i]->string ) ) {
00254                 adr[i].port = BigShort( PORT_MASTER );
00255             }
00256             Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string,
00257                 adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3],
00258                 BigShort( adr[i].port ) );
00259         }
00260 
00261 
00262         Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string );
00263         // this command should be changed if the server info / status format
00264         // ever incompatably changes
00265         NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME );
00266     }
00267 }
00268 
00269 /*
00270 =================
00271 SV_MasterShutdown
00272 
00273 Informs all masters that this server is going down
00274 =================
00275 */
00276 void SV_MasterShutdown( void ) {
00277     // send a hearbeat right now
00278     svs.nextHeartbeatTime = -9999;
00279     SV_MasterHeartbeat();
00280 
00281     // send it again to minimize chance of drops
00282     svs.nextHeartbeatTime = -9999;
00283     SV_MasterHeartbeat();
00284 
00285     // when the master tries to poll the server, it won't respond, so
00286     // it will be removed from the list
00287 }
00288 
00289 
00290 /*
00291 ==============================================================================
00292 
00293 CONNECTIONLESS COMMANDS
00294 
00295 ==============================================================================
00296 */
00297 
00298 /*
00299 ================
00300 SVC_Status
00301 
00302 Responds with all the info that qplug or qspy can see about the server
00303 and all connected players.  Used for getting detailed information after
00304 the simple info query.
00305 ================
00306 */
00307 void SVC_Status( netadr_t from ) {
00308     char    player[1024];
00309     char    status[MAX_MSGLEN];
00310     int     i;
00311     client_t    *cl;
00312     playerState_t   *ps;
00313     int     statusLength;
00314     int     playerLength;
00315     char    infostring[MAX_INFO_STRING];
00316 
00317     // ignore if we are in single player
00318     if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) {
00319         return;
00320     }
00321 
00322     strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
00323 
00324     // echo back the parameter to status. so master servers can use it as a challenge
00325     // to prevent timed spoofed reply packets that add ghost servers
00326     Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
00327 
00328     // add "demo" to the sv_keywords if restricted
00329     if ( Cvar_VariableValue( "fs_restrict" ) ) {
00330         char    keywords[MAX_INFO_STRING];
00331 
00332         Com_sprintf( keywords, sizeof( keywords ), "demo %s",
00333             Info_ValueForKey( infostring, "sv_keywords" ) );
00334         Info_SetValueForKey( infostring, "sv_keywords", keywords );
00335     }
00336 
00337     status[0] = 0;
00338     statusLength = 0;
00339 
00340     for (i=0 ; i < sv_maxclients->integer ; i++) {
00341         cl = &svs.clients[i];
00342         if ( cl->state >= CS_CONNECTED ) {
00343             ps = SV_GameClientNum( i );
00344             Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", 
00345                 ps->persistant[PERS_SCORE], cl->ping, cl->name);
00346             playerLength = strlen(player);
00347             if (statusLength + playerLength >= sizeof(status) ) {
00348                 break;      // can't hold any more
00349             }
00350             strcpy (status + statusLength, player);
00351             statusLength += playerLength;
00352         }
00353     }
00354 
00355     NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status );
00356 }
00357 
00358 /*
00359 ================
00360 SVC_Info
00361 
00362 Responds with a short info message that should be enough to determine
00363 if a user is interested in a server to do a full status
00364 ================
00365 */
00366 void SVC_Info( netadr_t from ) {
00367     int     i, count;
00368     char    *gamedir;
00369     char    infostring[MAX_INFO_STRING];
00370 
00371     // ignore if we are in single player
00372     if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
00373         return;
00374     }
00375 
00376     // don't count privateclients
00377     count = 0;
00378     for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) {
00379         if ( svs.clients[i].state >= CS_CONNECTED ) {
00380             count++;
00381         }
00382     }
00383 
00384     infostring[0] = 0;
00385 
00386     // echo back the parameter to status. so servers can use it as a challenge
00387     // to prevent timed spoofed reply packets that add ghost servers
00388     Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
00389 
00390     Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) );
00391     Info_SetValueForKey( infostring, "hostname", sv_hostname->string );
00392     Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
00393     Info_SetValueForKey( infostring, "clients", va("%i", count) );
00394     Info_SetValueForKey( infostring, "sv_maxclients", 
00395         va("%i", sv_maxclients->integer - sv_privateClients->integer ) );
00396     Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) );
00397     Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
00398 
00399     if( sv_minPing->integer ) {
00400         Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) );
00401     }
00402     if( sv_maxPing->integer ) {
00403         Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) );
00404     }
00405     gamedir = Cvar_VariableString( "fs_game" );
00406     if( *gamedir ) {
00407         Info_SetValueForKey( infostring, "game", gamedir );
00408     }
00409 
00410     NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring );
00411 }
00412 
00413 /*
00414 ================
00415 SVC_FlushRedirect
00416 
00417 ================
00418 */
00419 void SV_FlushRedirect( char *outputbuf ) {
00420     NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf );
00421 }
00422 
00423 /*
00424 ===============
00425 SVC_RemoteCommand
00426 
00427 An rcon packet arrived from the network.
00428 Shift down the remaining args
00429 Redirect all printfs
00430 ===============
00431 */
00432 void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
00433     qboolean    valid;
00434     unsigned int time;
00435     char        remaining[1024];
00436     // TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc.
00437     // (OOB messages are the bottleneck here)
00438 #define SV_OUTPUTBUF_LENGTH (1024 - 16)
00439     char        sv_outputbuf[SV_OUTPUTBUF_LENGTH];
00440     static unsigned int lasttime = 0;
00441     char *cmd_aux;
00442 
00443     // TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
00444     time = Com_Milliseconds();
00445     if (time<(lasttime+500)) {
00446         return;
00447     }
00448     lasttime = time;
00449 
00450     if ( !strlen( sv_rconPassword->string ) ||
00451         strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
00452         valid = qfalse;
00453         Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
00454     } else {
00455         valid = qtrue;
00456         Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
00457     }
00458 
00459     // start redirecting all print outputs to the packet
00460     svs.redirectAddress = from;
00461     Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
00462 
00463     if ( !strlen( sv_rconPassword->string ) ) {
00464         Com_Printf ("No rconpassword set on the server.\n");
00465     } else if ( !valid ) {
00466         Com_Printf ("Bad rconpassword.\n");
00467     } else {
00468         remaining[0] = 0;
00469         
00470         // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
00471         // get the command directly, "rcon <pass> <command>" to avoid quoting issues
00472         // extract the command by walking
00473         // since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing
00474         cmd_aux = Cmd_Cmd();
00475         cmd_aux+=4;
00476         while(cmd_aux[0]==' ')
00477             cmd_aux++;
00478         while(cmd_aux[0] && cmd_aux[0]!=' ') // password
00479             cmd_aux++;
00480         while(cmd_aux[0]==' ')
00481             cmd_aux++;
00482         
00483         Q_strcat( remaining, sizeof(remaining), cmd_aux);
00484         
00485         Cmd_ExecuteString (remaining);
00486 
00487     }
00488 
00489     Com_EndRedirect ();
00490 }
00491 
00492 /*
00493 =================
00494 SV_ConnectionlessPacket
00495 
00496 A connectionless packet has four leading 0xff
00497 characters to distinguish it from a game channel.
00498 Clients that are in the game can still send
00499 connectionless packets.
00500 =================
00501 */
00502 void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
00503     char    *s;
00504     char    *c;
00505 
00506     MSG_BeginReadingOOB( msg );
00507     MSG_ReadLong( msg );        // skip the -1 marker
00508 
00509     if (!Q_strncmp("connect", &msg->data[4], 7)) {
00510         Huff_Decompress(msg, 12);
00511     }
00512 
00513     s = MSG_ReadStringLine( msg );
00514     Cmd_TokenizeString( s );
00515 
00516     c = Cmd_Argv(0);
00517     Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c);
00518 
00519     if (!Q_stricmp(c, "getstatus")) {
00520         SVC_Status( from  );
00521   } else if (!Q_stricmp(c, "getinfo")) {
00522         SVC_Info( from );
00523     } else if (!Q_stricmp(c, "getchallenge")) {
00524         SV_GetChallenge( from );
00525     } else if (!Q_stricmp(c, "connect")) {
00526         SV_DirectConnect( from );
00527     } else if (!Q_stricmp(c, "ipAuthorize")) {
00528         SV_AuthorizeIpPacket( from );
00529     } else if (!Q_stricmp(c, "rcon")) {
00530         SVC_RemoteCommand( from, msg );
00531     } else if (!Q_stricmp(c, "disconnect")) {
00532         // if a client starts up a local server, we may see some spurious
00533         // server disconnect messages when their new server sees our final
00534         // sequenced messages to the old client
00535     } else {
00536         Com_DPrintf ("bad connectionless packet from %s:\n%s\n"
00537         , NET_AdrToString (from), s);
00538     }
00539 }
00540 
00541 //============================================================================
00542 
00543 /*
00544 =================
00545 SV_ReadPackets
00546 =================
00547 */
00548 void SV_PacketEvent( netadr_t from, msg_t *msg ) {
00549     int         i;
00550     client_t    *cl;
00551     int         qport;
00552 
00553     // check for connectionless packet (0xffffffff) first
00554     if ( msg->cursize >= 4 && *(int *)msg->data == -1) {
00555         SV_ConnectionlessPacket( from, msg );
00556         return;
00557     }
00558 
00559     // read the qport out of the message so we can fix up
00560     // stupid address translating routers
00561     MSG_BeginReadingOOB( msg );
00562     MSG_ReadLong( msg );                // sequence number
00563     qport = MSG_ReadShort( msg ) & 0xffff;
00564 
00565     // find which client the message is from
00566     for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
00567         if (cl->state == CS_FREE) {
00568             continue;
00569         }
00570         if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) {
00571             continue;
00572         }
00573         // it is possible to have multiple clients from a single IP
00574         // address, so they are differentiated by the qport variable
00575         if (cl->netchan.qport != qport) {
00576             continue;
00577         }
00578 
00579         // the IP port can't be used to differentiate them, because
00580         // some address translating routers periodically change UDP
00581         // port assignments
00582         if (cl->netchan.remoteAddress.port != from.port) {
00583             Com_Printf( "SV_PacketEvent: fixing up a translated port\n" );
00584             cl->netchan.remoteAddress.port = from.port;
00585         }
00586 
00587         // make sure it is a valid, in sequence packet
00588         if (SV_Netchan_Process(cl, msg)) {
00589             // zombie clients still need to do the Netchan_Process
00590             // to make sure they don't need to retransmit the final
00591             // reliable message, but they don't do any other processing
00592             if (cl->state != CS_ZOMBIE) {
00593                 cl->lastPacketTime = svs.time;  // don't timeout
00594                 SV_ExecuteClientMessage( cl, msg );
00595             }
00596         }
00597         return;
00598     }
00599     
00600     // if we received a sequenced packet from an address we don't recognize,
00601     // send an out of band disconnect packet to it
00602     NET_OutOfBandPrint( NS_SERVER, from, "disconnect" );
00603 }
00604 
00605 
00606 /*
00607 ===================
00608 SV_CalcPings
00609 
00610 Updates the cl->ping variables
00611 ===================
00612 */
00613 void SV_CalcPings( void ) {
00614     int         i, j;
00615     client_t    *cl;
00616     int         total, count;
00617     int         delta;
00618     playerState_t   *ps;
00619 
00620     for (i=0 ; i < sv_maxclients->integer ; i++) {
00621         cl = &svs.clients[i];
00622         if ( cl->state != CS_ACTIVE ) {
00623             cl->ping = 999;
00624             continue;
00625         }
00626         if ( !cl->gentity ) {
00627             cl->ping = 999;
00628             continue;
00629         }
00630         if ( cl->gentity->r.svFlags & SVF_BOT ) {
00631             cl->ping = 0;
00632             continue;
00633         }
00634 
00635         total = 0;
00636         count = 0;
00637         for ( j = 0 ; j < PACKET_BACKUP ; j++ ) {
00638             if ( cl->frames[j].messageAcked <= 0 ) {
00639                 continue;
00640             }
00641             delta = cl->frames[j].messageAcked - cl->frames[j].messageSent;
00642             count++;
00643             total += delta;
00644         }
00645         if (!count) {
00646             cl->ping = 999;
00647         } else {
00648             cl->ping = total/count;
00649             if ( cl->ping > 999 ) {
00650                 cl->ping = 999;
00651             }
00652         }
00653 
00654         // let the game dll know about the ping
00655         ps = SV_GameClientNum( i );
00656         ps->ping = cl->ping;
00657     }
00658 }
00659 
00660 /*
00661 ==================
00662 SV_CheckTimeouts
00663 
00664 If a packet has not been received from a client for timeout->integer 
00665 seconds, drop the conneciton.  Server time is used instead of
00666 realtime to avoid dropping the local client while debugging.
00667 
00668 When a client is normally dropped, the client_t goes into a zombie state
00669 for a few seconds to make sure any final reliable message gets resent
00670 if necessary
00671 ==================
00672 */
00673 void SV_CheckTimeouts( void ) {
00674     int     i;
00675     client_t    *cl;
00676     int         droppoint;
00677     int         zombiepoint;
00678 
00679     droppoint = svs.time - 1000 * sv_timeout->integer;
00680     zombiepoint = svs.time - 1000 * sv_zombietime->integer;
00681 
00682     for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
00683         // message times may be wrong across a changelevel
00684         if (cl->lastPacketTime > svs.time) {
00685             cl->lastPacketTime = svs.time;
00686         }
00687 
00688         if (cl->state == CS_ZOMBIE
00689         && cl->lastPacketTime < zombiepoint) {
00690             // using the client id cause the cl->name is empty at this point
00691             Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i );
00692             cl->state = CS_FREE;    // can now be reused
00693             continue;
00694         }
00695         if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint) {
00696             // wait several frames so a debugger session doesn't
00697             // cause a timeout
00698             if ( ++cl->timeoutCount > 5 ) {
00699                 SV_DropClient (cl, "timed out"); 
00700                 cl->state = CS_FREE;    // don't bother with zombie state
00701             }
00702         } else {
00703             cl->timeoutCount = 0;
00704         }
00705     }
00706 }
00707 
00708 
00709 /*
00710 ==================
00711 SV_CheckPaused
00712 ==================
00713 */
00714 qboolean SV_CheckPaused( void ) {
00715     int     count;
00716     client_t    *cl;
00717     int     i;
00718 
00719     if ( !cl_paused->integer ) {
00720         return qfalse;
00721     }
00722 
00723     // only pause if there is just a single client connected
00724     count = 0;
00725     for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
00726         if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) {
00727             count++;
00728         }
00729     }
00730 
00731     if ( count > 1 ) {
00732         // don't pause
00733         if (sv_paused->integer)
00734             Cvar_Set("sv_paused", "0");
00735         return qfalse;
00736     }
00737 
00738     if (!sv_paused->integer)
00739         Cvar_Set("sv_paused", "1");
00740     return qtrue;
00741 }
00742 
00743 /*
00744 ==================
00745 SV_Frame
00746 
00747 Player movement occurs as a result of packet events, which
00748 happen before SV_Frame is called
00749 ==================
00750 */
00751 void SV_Frame( int msec ) {
00752     int     frameMsec;
00753     int     startTime;
00754 
00755     // the menu kills the server with this cvar
00756     if ( sv_killserver->integer ) {
00757         SV_Shutdown ("Server was killed.\n");
00758         Cvar_Set( "sv_killserver", "0" );
00759         return;
00760     }
00761 
00762     if ( !com_sv_running->integer ) {
00763         return;
00764     }
00765 
00766     // allow pause if only the local client is connected
00767     if ( SV_CheckPaused() ) {
00768         return;
00769     }
00770 
00771     // if it isn't time for the next frame, do nothing
00772     if ( sv_fps->integer < 1 ) {
00773         Cvar_Set( "sv_fps", "10" );
00774     }
00775     frameMsec = 1000 / sv_fps->integer ;
00776 
00777     sv.timeResidual += msec;
00778 
00779     if (!com_dedicated->integer) SV_BotFrame( svs.time + sv.timeResidual );
00780 
00781     if ( com_dedicated->integer && sv.timeResidual < frameMsec ) {
00782         // NET_Sleep will give the OS time slices until either get a packet
00783         // or time enough for a server frame has gone by
00784         NET_Sleep(frameMsec - sv.timeResidual);
00785         return;
00786     }
00787 
00788     // if time is about to hit the 32nd bit, kick all clients
00789     // and clear sv.time, rather
00790     // than checking for negative time wraparound everywhere.
00791     // 2giga-milliseconds = 23 days, so it won't be too often
00792     if ( svs.time > 0x70000000 ) {
00793         SV_Shutdown( "Restarting server due to time wrapping" );
00794         Cbuf_AddText( "vstr nextmap\n" );
00795         return;
00796     }
00797     // this can happen considerably earlier when lots of clients play and the map doesn't change
00798     if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) {
00799         SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" );
00800         Cbuf_AddText( "vstr nextmap\n" );
00801         return;
00802     }
00803 
00804     if( sv.restartTime && svs.time >= sv.restartTime ) {
00805         sv.restartTime = 0;
00806         Cbuf_AddText( "map_restart 0\n" );
00807         return;
00808     }
00809 
00810     // update infostrings if anything has been changed
00811     if ( cvar_modifiedFlags & CVAR_SERVERINFO ) {
00812         SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
00813         cvar_modifiedFlags &= ~CVAR_SERVERINFO;
00814     }
00815     if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) {
00816         SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );
00817         cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
00818     }
00819 
00820     if ( com_speeds->integer ) {
00821         startTime = Sys_Milliseconds ();
00822     } else {
00823         startTime = 0;  // quite a compiler warning
00824     }
00825 
00826     // update ping based on the all received frames
00827     SV_CalcPings();
00828 
00829     if (com_dedicated->integer) SV_BotFrame( svs.time );
00830 
00831     // run the game simulation in chunks
00832     while ( sv.timeResidual >= frameMsec ) {
00833         sv.timeResidual -= frameMsec;
00834         svs.time += frameMsec;
00835 
00836         // let everything in the world think and move
00837         VM_Call( gvm, GAME_RUN_FRAME, svs.time );
00838     }
00839 
00840     if ( com_speeds->integer ) {
00841         time_game = Sys_Milliseconds () - startTime;
00842     }
00843 
00844     // check timeouts
00845     SV_CheckTimeouts();
00846 
00847     // send messages back to the clients
00848     SV_SendClientMessages();
00849 
00850     // send a heartbeat to the master if needed
00851     SV_MasterHeartbeat();
00852 }
00853 
00854 //============================================================================
00855 

Generated on Thu Aug 25 12:37:57 2005 for Quake III Arena by  doxygen 1.3.9.1