Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

sv_main.c (23741B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 
     23 #include "server.h"
     24 
     25 serverStatic_t	svs;				// persistant server info
     26 server_t		sv;					// local server
     27 vm_t			*gvm = NULL;				// game virtual machine // bk001212 init
     28 
     29 cvar_t	*sv_fps;				// time rate for running non-clients
     30 cvar_t	*sv_timeout;			// seconds without any message
     31 cvar_t	*sv_zombietime;			// seconds to sink messages after disconnect
     32 cvar_t	*sv_rconPassword;		// password for remote server commands
     33 cvar_t	*sv_privatePassword;	// password for the privateClient slots
     34 cvar_t	*sv_allowDownload;
     35 cvar_t	*sv_maxclients;
     36 
     37 cvar_t	*sv_privateClients;		// number of clients reserved for password
     38 cvar_t	*sv_hostname;
     39 cvar_t	*sv_master[MAX_MASTER_SERVERS];		// master server ip address
     40 cvar_t	*sv_reconnectlimit;		// minimum seconds between connect messages
     41 cvar_t	*sv_showloss;			// report when usercmds are lost
     42 cvar_t	*sv_padPackets;			// add nop bytes to messages
     43 cvar_t	*sv_killserver;			// menu system can set to 1 to shut server down
     44 cvar_t	*sv_mapname;
     45 cvar_t	*sv_mapChecksum;
     46 cvar_t	*sv_serverid;
     47 cvar_t	*sv_maxRate;
     48 cvar_t	*sv_minPing;
     49 cvar_t	*sv_maxPing;
     50 cvar_t	*sv_gametype;
     51 cvar_t	*sv_pure;
     52 cvar_t	*sv_floodProtect;
     53 cvar_t	*sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491)
     54 cvar_t	*sv_strictAuth;
     55 
     56 /*
     57 =============================================================================
     58 
     59 EVENT MESSAGES
     60 
     61 =============================================================================
     62 */
     63 
     64 /*
     65 ===============
     66 SV_ExpandNewlines
     67 
     68 Converts newlines to "\n" so a line prints nicer
     69 ===============
     70 */
     71 char	*SV_ExpandNewlines( char *in ) {
     72 	static	char	string[1024];
     73 	int		l;
     74 
     75 	l = 0;
     76 	while ( *in && l < sizeof(string) - 3 ) {
     77 		if ( *in == '\n' ) {
     78 			string[l++] = '\\';
     79 			string[l++] = 'n';
     80 		} else {
     81 			string[l++] = *in;
     82 		}
     83 		in++;
     84 	}
     85 	string[l] = 0;
     86 
     87 	return string;
     88 }
     89 
     90 /*
     91 ======================
     92 SV_ReplacePendingServerCommands
     93 
     94   This is ugly
     95 ======================
     96 */
     97 int SV_ReplacePendingServerCommands( client_t *client, const char *cmd ) {
     98 	int i, index, csnum1, csnum2;
     99 
    100 	for ( i = client->reliableSent+1; i <= client->reliableSequence; i++ ) {
    101 		index = i & ( MAX_RELIABLE_COMMANDS - 1 );
    102 		//
    103 		if ( !Q_strncmp(cmd, client->reliableCommands[ index ], strlen("cs")) ) {
    104 			sscanf(cmd, "cs %i", &csnum1);
    105 			sscanf(client->reliableCommands[ index ], "cs %i", &csnum2);
    106 			if ( csnum1 == csnum2 ) {
    107 				Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
    108 				/*
    109 				if ( client->netchan.remoteAddress.type != NA_BOT ) {
    110 					Com_Printf( "WARNING: client %i removed double pending config string %i: %s\n", client-svs.clients, csnum1, cmd );
    111 				}
    112 				*/
    113 				return qtrue;
    114 			}
    115 		}
    116 	}
    117 	return qfalse;
    118 }
    119 
    120 /*
    121 ======================
    122 SV_AddServerCommand
    123 
    124 The given command will be transmitted to the client, and is guaranteed to
    125 not have future snapshot_t executed before it is executed
    126 ======================
    127 */
    128 void SV_AddServerCommand( client_t *client, const char *cmd ) {
    129 	int		index, i;
    130 
    131 	// this is very ugly but it's also a waste to for instance send multiple config string updates
    132 	// for the same config string index in one snapshot
    133 //	if ( SV_ReplacePendingServerCommands( client, cmd ) ) {
    134 //		return;
    135 //	}
    136 
    137 	client->reliableSequence++;
    138 	// if we would be losing an old command that hasn't been acknowledged,
    139 	// we must drop the connection
    140 	// we check == instead of >= so a broadcast print added by SV_DropClient()
    141 	// doesn't cause a recursive drop client
    142 	if ( client->reliableSequence - client->reliableAcknowledge == MAX_RELIABLE_COMMANDS + 1 ) {
    143 		Com_Printf( "===== pending server commands =====\n" );
    144 		for ( i = client->reliableAcknowledge + 1 ; i <= client->reliableSequence ; i++ ) {
    145 			Com_Printf( "cmd %5d: %s\n", i, client->reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
    146 		}
    147 		Com_Printf( "cmd %5d: %s\n", i, cmd );
    148 		SV_DropClient( client, "Server command overflow" );
    149 		return;
    150 	}
    151 	index = client->reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
    152 	Q_strncpyz( client->reliableCommands[ index ], cmd, sizeof( client->reliableCommands[ index ] ) );
    153 }
    154 
    155 
    156 /*
    157 =================
    158 SV_SendServerCommand
    159 
    160 Sends a reliable command string to be interpreted by 
    161 the client game module: "cp", "print", "chat", etc
    162 A NULL client will broadcast to all clients
    163 =================
    164 */
    165 void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) {
    166 	va_list		argptr;
    167 	byte		message[MAX_MSGLEN];
    168 	client_t	*client;
    169 	int			j;
    170 	
    171 	va_start (argptr,fmt);
    172 	Q_vsnprintf ((char *)message, sizeof(message), fmt,argptr);
    173 	va_end (argptr);
    174 
    175 	if ( cl != NULL ) {
    176 		SV_AddServerCommand( cl, (char *)message );
    177 		return;
    178 	}
    179 
    180 	// hack to echo broadcast prints to console
    181 	if ( com_dedicated->integer && !strncmp( (char *)message, "print", 5) ) {
    182 		Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) );
    183 	}
    184 
    185 	// send the data to all relevent clients
    186 	for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) {
    187 		if ( client->state < CS_PRIMED ) {
    188 			continue;
    189 		}
    190 		SV_AddServerCommand( client, (char *)message );
    191 	}
    192 }
    193 
    194 
    195 /*
    196 ==============================================================================
    197 
    198 MASTER SERVER FUNCTIONS
    199 
    200 ==============================================================================
    201 */
    202 
    203 /*
    204 ================
    205 SV_MasterHeartbeat
    206 
    207 Send a message to the masters every few minutes to
    208 let it know we are alive, and log information.
    209 We will also have a heartbeat sent when a server
    210 changes from empty to non-empty, and full to non-full,
    211 but not on every player enter or exit.
    212 ================
    213 */
    214 #define	HEARTBEAT_MSEC	300*1000
    215 #define	HEARTBEAT_GAME	"QuakeArena-1"
    216 void SV_MasterHeartbeat( void ) {
    217 	static netadr_t	adr[MAX_MASTER_SERVERS];
    218 	int			i;
    219 
    220 	// "dedicated 1" is for lan play, "dedicated 2" is for inet public play
    221 	if ( !com_dedicated || com_dedicated->integer != 2 ) {
    222 		return;		// only dedicated servers send heartbeats
    223 	}
    224 
    225 	// if not time yet, don't send anything
    226 	if ( svs.time < svs.nextHeartbeatTime ) {
    227 		return;
    228 	}
    229 	svs.nextHeartbeatTime = svs.time + HEARTBEAT_MSEC;
    230 
    231 
    232 	// send to group masters
    233 	for ( i = 0 ; i < MAX_MASTER_SERVERS ; i++ ) {
    234 		if ( !sv_master[i]->string[0] ) {
    235 			continue;
    236 		}
    237 
    238 		// see if we haven't already resolved the name
    239 		// resolving usually causes hitches on win95, so only
    240 		// do it when needed
    241 		if ( sv_master[i]->modified ) {
    242 			sv_master[i]->modified = qfalse;
    243 	
    244 			Com_Printf( "Resolving %s\n", sv_master[i]->string );
    245 			if ( !NET_StringToAdr( sv_master[i]->string, &adr[i] ) ) {
    246 				// if the address failed to resolve, clear it
    247 				// so we don't take repeated dns hits
    248 				Com_Printf( "Couldn't resolve address: %s\n", sv_master[i]->string );
    249 				Cvar_Set( sv_master[i]->name, "" );
    250 				sv_master[i]->modified = qfalse;
    251 				continue;
    252 			}
    253 			if ( !strstr( ":", sv_master[i]->string ) ) {
    254 				adr[i].port = BigShort( PORT_MASTER );
    255 			}
    256 			Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", sv_master[i]->string,
    257 				adr[i].ip[0], adr[i].ip[1], adr[i].ip[2], adr[i].ip[3],
    258 				BigShort( adr[i].port ) );
    259 		}
    260 
    261 
    262 		Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string );
    263 		// this command should be changed if the server info / status format
    264 		// ever incompatably changes
    265 		NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME );
    266 	}
    267 }
    268 
    269 /*
    270 =================
    271 SV_MasterShutdown
    272 
    273 Informs all masters that this server is going down
    274 =================
    275 */
    276 void SV_MasterShutdown( void ) {
    277 	// send a hearbeat right now
    278 	svs.nextHeartbeatTime = -9999;
    279 	SV_MasterHeartbeat();
    280 
    281 	// send it again to minimize chance of drops
    282 	svs.nextHeartbeatTime = -9999;
    283 	SV_MasterHeartbeat();
    284 
    285 	// when the master tries to poll the server, it won't respond, so
    286 	// it will be removed from the list
    287 }
    288 
    289 
    290 /*
    291 ==============================================================================
    292 
    293 CONNECTIONLESS COMMANDS
    294 
    295 ==============================================================================
    296 */
    297 
    298 /*
    299 ================
    300 SVC_Status
    301 
    302 Responds with all the info that qplug or qspy can see about the server
    303 and all connected players.  Used for getting detailed information after
    304 the simple info query.
    305 ================
    306 */
    307 void SVC_Status( netadr_t from ) {
    308 	char	player[1024];
    309 	char	status[MAX_MSGLEN];
    310 	int		i;
    311 	client_t	*cl;
    312 	playerState_t	*ps;
    313 	int		statusLength;
    314 	int		playerLength;
    315 	char	infostring[MAX_INFO_STRING];
    316 
    317 	// ignore if we are in single player
    318 	if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) {
    319 		return;
    320 	}
    321 
    322 	strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
    323 
    324 	// echo back the parameter to status. so master servers can use it as a challenge
    325 	// to prevent timed spoofed reply packets that add ghost servers
    326 	Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
    327 
    328 	// add "demo" to the sv_keywords if restricted
    329 	if ( Cvar_VariableValue( "fs_restrict" ) ) {
    330 		char	keywords[MAX_INFO_STRING];
    331 
    332 		Com_sprintf( keywords, sizeof( keywords ), "demo %s",
    333 			Info_ValueForKey( infostring, "sv_keywords" ) );
    334 		Info_SetValueForKey( infostring, "sv_keywords", keywords );
    335 	}
    336 
    337 	status[0] = 0;
    338 	statusLength = 0;
    339 
    340 	for (i=0 ; i < sv_maxclients->integer ; i++) {
    341 		cl = &svs.clients[i];
    342 		if ( cl->state >= CS_CONNECTED ) {
    343 			ps = SV_GameClientNum( i );
    344 			Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", 
    345 				ps->persistant[PERS_SCORE], cl->ping, cl->name);
    346 			playerLength = strlen(player);
    347 			if (statusLength + playerLength >= sizeof(status) ) {
    348 				break;		// can't hold any more
    349 			}
    350 			strcpy (status + statusLength, player);
    351 			statusLength += playerLength;
    352 		}
    353 	}
    354 
    355 	NET_OutOfBandPrint( NS_SERVER, from, "statusResponse\n%s\n%s", infostring, status );
    356 }
    357 
    358 /*
    359 ================
    360 SVC_Info
    361 
    362 Responds with a short info message that should be enough to determine
    363 if a user is interested in a server to do a full status
    364 ================
    365 */
    366 void SVC_Info( netadr_t from ) {
    367 	int		i, count;
    368 	char	*gamedir;
    369 	char	infostring[MAX_INFO_STRING];
    370 
    371 	// ignore if we are in single player
    372 	if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
    373 		return;
    374 	}
    375 
    376 	// don't count privateclients
    377 	count = 0;
    378 	for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) {
    379 		if ( svs.clients[i].state >= CS_CONNECTED ) {
    380 			count++;
    381 		}
    382 	}
    383 
    384 	infostring[0] = 0;
    385 
    386 	// echo back the parameter to status. so servers can use it as a challenge
    387 	// to prevent timed spoofed reply packets that add ghost servers
    388 	Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
    389 
    390 	Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) );
    391 	Info_SetValueForKey( infostring, "hostname", sv_hostname->string );
    392 	Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
    393 	Info_SetValueForKey( infostring, "clients", va("%i", count) );
    394 	Info_SetValueForKey( infostring, "sv_maxclients", 
    395 		va("%i", sv_maxclients->integer - sv_privateClients->integer ) );
    396 	Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) );
    397 	Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
    398 
    399 	if( sv_minPing->integer ) {
    400 		Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) );
    401 	}
    402 	if( sv_maxPing->integer ) {
    403 		Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) );
    404 	}
    405 	gamedir = Cvar_VariableString( "fs_game" );
    406 	if( *gamedir ) {
    407 		Info_SetValueForKey( infostring, "game", gamedir );
    408 	}
    409 
    410 	NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring );
    411 }
    412 
    413 /*
    414 ================
    415 SVC_FlushRedirect
    416 
    417 ================
    418 */
    419 void SV_FlushRedirect( char *outputbuf ) {
    420 	NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf );
    421 }
    422 
    423 /*
    424 ===============
    425 SVC_RemoteCommand
    426 
    427 An rcon packet arrived from the network.
    428 Shift down the remaining args
    429 Redirect all printfs
    430 ===============
    431 */
    432 void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
    433 	qboolean	valid;
    434 	unsigned int time;
    435 	char		remaining[1024];
    436 	// TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc.
    437 	// (OOB messages are the bottleneck here)
    438 #define SV_OUTPUTBUF_LENGTH (1024 - 16)
    439 	char		sv_outputbuf[SV_OUTPUTBUF_LENGTH];
    440 	static unsigned int lasttime = 0;
    441 	char *cmd_aux;
    442 
    443 	// TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
    444 	time = Com_Milliseconds();
    445 	if (time<(lasttime+500)) {
    446 		return;
    447 	}
    448 	lasttime = time;
    449 
    450 	if ( !strlen( sv_rconPassword->string ) ||
    451 		strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
    452 		valid = qfalse;
    453 		Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
    454 	} else {
    455 		valid = qtrue;
    456 		Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
    457 	}
    458 
    459 	// start redirecting all print outputs to the packet
    460 	svs.redirectAddress = from;
    461 	Com_BeginRedirect (sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
    462 
    463 	if ( !strlen( sv_rconPassword->string ) ) {
    464 		Com_Printf ("No rconpassword set on the server.\n");
    465 	} else if ( !valid ) {
    466 		Com_Printf ("Bad rconpassword.\n");
    467 	} else {
    468 		remaining[0] = 0;
    469 		
    470 		// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
    471 		// get the command directly, "rcon <pass> <command>" to avoid quoting issues
    472 		// extract the command by walking
    473 		// since the cmd formatting can fuckup (amount of spaces), using a dumb step by step parsing
    474 		cmd_aux = Cmd_Cmd();
    475 		cmd_aux+=4;
    476 		while(cmd_aux[0]==' ')
    477 			cmd_aux++;
    478 		while(cmd_aux[0] && cmd_aux[0]!=' ') // password
    479 			cmd_aux++;
    480 		while(cmd_aux[0]==' ')
    481 			cmd_aux++;
    482 		
    483 		Q_strcat( remaining, sizeof(remaining), cmd_aux);
    484 		
    485 		Cmd_ExecuteString (remaining);
    486 
    487 	}
    488 
    489 	Com_EndRedirect ();
    490 }
    491 
    492 /*
    493 =================
    494 SV_ConnectionlessPacket
    495 
    496 A connectionless packet has four leading 0xff
    497 characters to distinguish it from a game channel.
    498 Clients that are in the game can still send
    499 connectionless packets.
    500 =================
    501 */
    502 void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
    503 	char	*s;
    504 	char	*c;
    505 
    506 	MSG_BeginReadingOOB( msg );
    507 	MSG_ReadLong( msg );		// skip the -1 marker
    508 
    509 	if (!Q_strncmp("connect", &msg->data[4], 7)) {
    510 		Huff_Decompress(msg, 12);
    511 	}
    512 
    513 	s = MSG_ReadStringLine( msg );
    514 	Cmd_TokenizeString( s );
    515 
    516 	c = Cmd_Argv(0);
    517 	Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c);
    518 
    519 	if (!Q_stricmp(c, "getstatus")) {
    520 		SVC_Status( from  );
    521   } else if (!Q_stricmp(c, "getinfo")) {
    522 		SVC_Info( from );
    523 	} else if (!Q_stricmp(c, "getchallenge")) {
    524 		SV_GetChallenge( from );
    525 	} else if (!Q_stricmp(c, "connect")) {
    526 		SV_DirectConnect( from );
    527 	} else if (!Q_stricmp(c, "ipAuthorize")) {
    528 		SV_AuthorizeIpPacket( from );
    529 	} else if (!Q_stricmp(c, "rcon")) {
    530 		SVC_RemoteCommand( from, msg );
    531 	} else if (!Q_stricmp(c, "disconnect")) {
    532 		// if a client starts up a local server, we may see some spurious
    533 		// server disconnect messages when their new server sees our final
    534 		// sequenced messages to the old client
    535 	} else {
    536 		Com_DPrintf ("bad connectionless packet from %s:\n%s\n"
    537 		, NET_AdrToString (from), s);
    538 	}
    539 }
    540 
    541 //============================================================================
    542 
    543 /*
    544 =================
    545 SV_ReadPackets
    546 =================
    547 */
    548 void SV_PacketEvent( netadr_t from, msg_t *msg ) {
    549 	int			i;
    550 	client_t	*cl;
    551 	int			qport;
    552 
    553 	// check for connectionless packet (0xffffffff) first
    554 	if ( msg->cursize >= 4 && *(int *)msg->data == -1) {
    555 		SV_ConnectionlessPacket( from, msg );
    556 		return;
    557 	}
    558 
    559 	// read the qport out of the message so we can fix up
    560 	// stupid address translating routers
    561 	MSG_BeginReadingOOB( msg );
    562 	MSG_ReadLong( msg );				// sequence number
    563 	qport = MSG_ReadShort( msg ) & 0xffff;
    564 
    565 	// find which client the message is from
    566 	for (i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
    567 		if (cl->state == CS_FREE) {
    568 			continue;
    569 		}
    570 		if ( !NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) ) {
    571 			continue;
    572 		}
    573 		// it is possible to have multiple clients from a single IP
    574 		// address, so they are differentiated by the qport variable
    575 		if (cl->netchan.qport != qport) {
    576 			continue;
    577 		}
    578 
    579 		// the IP port can't be used to differentiate them, because
    580 		// some address translating routers periodically change UDP
    581 		// port assignments
    582 		if (cl->netchan.remoteAddress.port != from.port) {
    583 			Com_Printf( "SV_PacketEvent: fixing up a translated port\n" );
    584 			cl->netchan.remoteAddress.port = from.port;
    585 		}
    586 
    587 		// make sure it is a valid, in sequence packet
    588 		if (SV_Netchan_Process(cl, msg)) {
    589 			// zombie clients still need to do the Netchan_Process
    590 			// to make sure they don't need to retransmit the final
    591 			// reliable message, but they don't do any other processing
    592 			if (cl->state != CS_ZOMBIE) {
    593 				cl->lastPacketTime = svs.time;	// don't timeout
    594 				SV_ExecuteClientMessage( cl, msg );
    595 			}
    596 		}
    597 		return;
    598 	}
    599 	
    600 	// if we received a sequenced packet from an address we don't recognize,
    601 	// send an out of band disconnect packet to it
    602 	NET_OutOfBandPrint( NS_SERVER, from, "disconnect" );
    603 }
    604 
    605 
    606 /*
    607 ===================
    608 SV_CalcPings
    609 
    610 Updates the cl->ping variables
    611 ===================
    612 */
    613 void SV_CalcPings( void ) {
    614 	int			i, j;
    615 	client_t	*cl;
    616 	int			total, count;
    617 	int			delta;
    618 	playerState_t	*ps;
    619 
    620 	for (i=0 ; i < sv_maxclients->integer ; i++) {
    621 		cl = &svs.clients[i];
    622 		if ( cl->state != CS_ACTIVE ) {
    623 			cl->ping = 999;
    624 			continue;
    625 		}
    626 		if ( !cl->gentity ) {
    627 			cl->ping = 999;
    628 			continue;
    629 		}
    630 		if ( cl->gentity->r.svFlags & SVF_BOT ) {
    631 			cl->ping = 0;
    632 			continue;
    633 		}
    634 
    635 		total = 0;
    636 		count = 0;
    637 		for ( j = 0 ; j < PACKET_BACKUP ; j++ ) {
    638 			if ( cl->frames[j].messageAcked <= 0 ) {
    639 				continue;
    640 			}
    641 			delta = cl->frames[j].messageAcked - cl->frames[j].messageSent;
    642 			count++;
    643 			total += delta;
    644 		}
    645 		if (!count) {
    646 			cl->ping = 999;
    647 		} else {
    648 			cl->ping = total/count;
    649 			if ( cl->ping > 999 ) {
    650 				cl->ping = 999;
    651 			}
    652 		}
    653 
    654 		// let the game dll know about the ping
    655 		ps = SV_GameClientNum( i );
    656 		ps->ping = cl->ping;
    657 	}
    658 }
    659 
    660 /*
    661 ==================
    662 SV_CheckTimeouts
    663 
    664 If a packet has not been received from a client for timeout->integer 
    665 seconds, drop the conneciton.  Server time is used instead of
    666 realtime to avoid dropping the local client while debugging.
    667 
    668 When a client is normally dropped, the client_t goes into a zombie state
    669 for a few seconds to make sure any final reliable message gets resent
    670 if necessary
    671 ==================
    672 */
    673 void SV_CheckTimeouts( void ) {
    674 	int		i;
    675 	client_t	*cl;
    676 	int			droppoint;
    677 	int			zombiepoint;
    678 
    679 	droppoint = svs.time - 1000 * sv_timeout->integer;
    680 	zombiepoint = svs.time - 1000 * sv_zombietime->integer;
    681 
    682 	for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
    683 		// message times may be wrong across a changelevel
    684 		if (cl->lastPacketTime > svs.time) {
    685 			cl->lastPacketTime = svs.time;
    686 		}
    687 
    688 		if (cl->state == CS_ZOMBIE
    689 		&& cl->lastPacketTime < zombiepoint) {
    690 			// using the client id cause the cl->name is empty at this point
    691 			Com_DPrintf( "Going from CS_ZOMBIE to CS_FREE for client %d\n", i );
    692 			cl->state = CS_FREE;	// can now be reused
    693 			continue;
    694 		}
    695 		if ( cl->state >= CS_CONNECTED && cl->lastPacketTime < droppoint) {
    696 			// wait several frames so a debugger session doesn't
    697 			// cause a timeout
    698 			if ( ++cl->timeoutCount > 5 ) {
    699 				SV_DropClient (cl, "timed out"); 
    700 				cl->state = CS_FREE;	// don't bother with zombie state
    701 			}
    702 		} else {
    703 			cl->timeoutCount = 0;
    704 		}
    705 	}
    706 }
    707 
    708 
    709 /*
    710 ==================
    711 SV_CheckPaused
    712 ==================
    713 */
    714 qboolean SV_CheckPaused( void ) {
    715 	int		count;
    716 	client_t	*cl;
    717 	int		i;
    718 
    719 	if ( !cl_paused->integer ) {
    720 		return qfalse;
    721 	}
    722 
    723 	// only pause if there is just a single client connected
    724 	count = 0;
    725 	for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) {
    726 		if ( cl->state >= CS_CONNECTED && cl->netchan.remoteAddress.type != NA_BOT ) {
    727 			count++;
    728 		}
    729 	}
    730 
    731 	if ( count > 1 ) {
    732 		// don't pause
    733 		if (sv_paused->integer)
    734 			Cvar_Set("sv_paused", "0");
    735 		return qfalse;
    736 	}
    737 
    738 	if (!sv_paused->integer)
    739 		Cvar_Set("sv_paused", "1");
    740 	return qtrue;
    741 }
    742 
    743 /*
    744 ==================
    745 SV_Frame
    746 
    747 Player movement occurs as a result of packet events, which
    748 happen before SV_Frame is called
    749 ==================
    750 */
    751 void SV_Frame( int msec ) {
    752 	int		frameMsec;
    753 	int		startTime;
    754 
    755 	// the menu kills the server with this cvar
    756 	if ( sv_killserver->integer ) {
    757 		SV_Shutdown ("Server was killed.\n");
    758 		Cvar_Set( "sv_killserver", "0" );
    759 		return;
    760 	}
    761 
    762 	if ( !com_sv_running->integer ) {
    763 		return;
    764 	}
    765 
    766 	// allow pause if only the local client is connected
    767 	if ( SV_CheckPaused() ) {
    768 		return;
    769 	}
    770 
    771 	// if it isn't time for the next frame, do nothing
    772 	if ( sv_fps->integer < 1 ) {
    773 		Cvar_Set( "sv_fps", "10" );
    774 	}
    775 	frameMsec = 1000 / sv_fps->integer ;
    776 
    777 	sv.timeResidual += msec;
    778 
    779 	if (!com_dedicated->integer) SV_BotFrame( svs.time + sv.timeResidual );
    780 
    781 	if ( com_dedicated->integer && sv.timeResidual < frameMsec ) {
    782 		// NET_Sleep will give the OS time slices until either get a packet
    783 		// or time enough for a server frame has gone by
    784 		NET_Sleep(frameMsec - sv.timeResidual);
    785 		return;
    786 	}
    787 
    788 	// if time is about to hit the 32nd bit, kick all clients
    789 	// and clear sv.time, rather
    790 	// than checking for negative time wraparound everywhere.
    791 	// 2giga-milliseconds = 23 days, so it won't be too often
    792 	if ( svs.time > 0x70000000 ) {
    793 		SV_Shutdown( "Restarting server due to time wrapping" );
    794 		Cbuf_AddText( "vstr nextmap\n" );
    795 		return;
    796 	}
    797 	// this can happen considerably earlier when lots of clients play and the map doesn't change
    798 	if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) {
    799 		SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" );
    800 		Cbuf_AddText( "vstr nextmap\n" );
    801 		return;
    802 	}
    803 
    804 	if( sv.restartTime && svs.time >= sv.restartTime ) {
    805 		sv.restartTime = 0;
    806 		Cbuf_AddText( "map_restart 0\n" );
    807 		return;
    808 	}
    809 
    810 	// update infostrings if anything has been changed
    811 	if ( cvar_modifiedFlags & CVAR_SERVERINFO ) {
    812 		SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
    813 		cvar_modifiedFlags &= ~CVAR_SERVERINFO;
    814 	}
    815 	if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) {
    816 		SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );
    817 		cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
    818 	}
    819 
    820 	if ( com_speeds->integer ) {
    821 		startTime = Sys_Milliseconds ();
    822 	} else {
    823 		startTime = 0;	// quite a compiler warning
    824 	}
    825 
    826 	// update ping based on the all received frames
    827 	SV_CalcPings();
    828 
    829 	if (com_dedicated->integer) SV_BotFrame( svs.time );
    830 
    831 	// run the game simulation in chunks
    832 	while ( sv.timeResidual >= frameMsec ) {
    833 		sv.timeResidual -= frameMsec;
    834 		svs.time += frameMsec;
    835 
    836 		// let everything in the world think and move
    837 		VM_Call( gvm, GAME_RUN_FRAME, svs.time );
    838 	}
    839 
    840 	if ( com_speeds->integer ) {
    841 		time_game = Sys_Milliseconds () - startTime;
    842 	}
    843 
    844 	// check timeouts
    845 	SV_CheckTimeouts();
    846 
    847 	// send messages back to the clients
    848 	SV_SendClientMessages();
    849 
    850 	// send a heartbeat to the master if needed
    851 	SV_MasterHeartbeat();
    852 }
    853 
    854 //============================================================================
    855