Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

sv_main.c (24080B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 
     21 #include "server.h"
     22 
     23 netadr_t	master_adr[MAX_MASTERS];	// address of group servers
     24 
     25 client_t	*sv_client;			// current client
     26 
     27 cvar_t	*sv_paused;
     28 cvar_t	*sv_timedemo;
     29 
     30 cvar_t	*sv_enforcetime;
     31 
     32 cvar_t	*timeout;				// seconds without any message
     33 cvar_t	*zombietime;			// seconds to sink messages after disconnect
     34 
     35 cvar_t	*rcon_password;			// password for remote server commands
     36 
     37 cvar_t	*allow_download;
     38 cvar_t *allow_download_players;
     39 cvar_t *allow_download_models;
     40 cvar_t *allow_download_sounds;
     41 cvar_t *allow_download_maps;
     42 
     43 cvar_t *sv_airaccelerate;
     44 
     45 cvar_t	*sv_noreload;			// don't reload level state when reentering
     46 
     47 cvar_t	*maxclients;			// FIXME: rename sv_maxclients
     48 cvar_t	*sv_showclamp;
     49 
     50 cvar_t	*hostname;
     51 cvar_t	*public_server;			// should heartbeats be sent
     52 
     53 cvar_t	*sv_reconnect_limit;	// minimum seconds between connect messages
     54 
     55 void Master_Shutdown (void);
     56 
     57 
     58 //============================================================================
     59 
     60 
     61 /*
     62 =====================
     63 SV_DropClient
     64 
     65 Called when the player is totally leaving the server, either willingly
     66 or unwillingly.  This is NOT called if the entire server is quiting
     67 or crashing.
     68 =====================
     69 */
     70 void SV_DropClient (client_t *drop)
     71 {
     72 	// add the disconnect
     73 	MSG_WriteByte (&drop->netchan.message, svc_disconnect);
     74 
     75 	if (drop->state == cs_spawned)
     76 	{
     77 		// call the prog function for removing a client
     78 		// this will remove the body, among other things
     79 		ge->ClientDisconnect (drop->edict);
     80 	}
     81 
     82 	if (drop->download)
     83 	{
     84 		FS_FreeFile (drop->download);
     85 		drop->download = NULL;
     86 	}
     87 
     88 	drop->state = cs_zombie;		// become free in a few seconds
     89 	drop->name[0] = 0;
     90 }
     91 
     92 
     93 
     94 /*
     95 ==============================================================================
     96 
     97 CONNECTIONLESS COMMANDS
     98 
     99 ==============================================================================
    100 */
    101 
    102 /*
    103 ===============
    104 SV_StatusString
    105 
    106 Builds the string that is sent as heartbeats and status replies
    107 ===============
    108 */
    109 char	*SV_StatusString (void)
    110 {
    111 	char	player[1024];
    112 	static char	status[MAX_MSGLEN - 16];
    113 	int		i;
    114 	client_t	*cl;
    115 	int		statusLength;
    116 	int		playerLength;
    117 
    118 	strcpy (status, Cvar_Serverinfo());
    119 	strcat (status, "\n");
    120 	statusLength = strlen(status);
    121 
    122 	for (i=0 ; i<maxclients->value ; i++)
    123 	{
    124 		cl = &svs.clients[i];
    125 		if (cl->state == cs_connected || cl->state == cs_spawned )
    126 		{
    127 			Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", 
    128 				cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, cl->name);
    129 			playerLength = strlen(player);
    130 			if (statusLength + playerLength >= sizeof(status) )
    131 				break;		// can't hold any more
    132 			strcpy (status + statusLength, player);
    133 			statusLength += playerLength;
    134 		}
    135 	}
    136 
    137 	return status;
    138 }
    139 
    140 /*
    141 ================
    142 SVC_Status
    143 
    144 Responds with all the info that qplug or qspy can see
    145 ================
    146 */
    147 void SVC_Status (void)
    148 {
    149 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString());
    150 #if 0
    151 	Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
    152 	Com_Printf (SV_StatusString());
    153 	Com_EndRedirect ();
    154 #endif
    155 }
    156 
    157 /*
    158 ================
    159 SVC_Ack
    160 
    161 ================
    162 */
    163 void SVC_Ack (void)
    164 {
    165 	Com_Printf ("Ping acknowledge from %s\n", NET_AdrToString(net_from));
    166 }
    167 
    168 /*
    169 ================
    170 SVC_Info
    171 
    172 Responds with short info for broadcast scans
    173 The second parameter should be the current protocol version number.
    174 ================
    175 */
    176 void SVC_Info (void)
    177 {
    178 	char	string[64];
    179 	int		i, count;
    180 	int		version;
    181 
    182 	if (maxclients->value == 1)
    183 		return;		// ignore in single player
    184 
    185 	version = atoi (Cmd_Argv(1));
    186 
    187 	if (version != PROTOCOL_VERSION)
    188 		Com_sprintf (string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string));
    189 	else
    190 	{
    191 		count = 0;
    192 		for (i=0 ; i<maxclients->value ; i++)
    193 			if (svs.clients[i].state >= cs_connected)
    194 				count++;
    195 
    196 		Com_sprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value);
    197 	}
    198 
    199 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "info\n%s", string);
    200 }
    201 
    202 /*
    203 ================
    204 SVC_Ping
    205 
    206 Just responds with an acknowledgement
    207 ================
    208 */
    209 void SVC_Ping (void)
    210 {
    211 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "ack");
    212 }
    213 
    214 
    215 /*
    216 =================
    217 SVC_GetChallenge
    218 
    219 Returns a challenge number that can be used
    220 in a subsequent client_connect command.
    221 We do this to prevent denial of service attacks that
    222 flood the server with invalid connection IPs.  With a
    223 challenge, they must give a valid IP address.
    224 =================
    225 */
    226 void SVC_GetChallenge (void)
    227 {
    228 	int		i;
    229 	int		oldest;
    230 	int		oldestTime;
    231 
    232 	oldest = 0;
    233 	oldestTime = 0x7fffffff;
    234 
    235 	// see if we already have a challenge for this ip
    236 	for (i = 0 ; i < MAX_CHALLENGES ; i++)
    237 	{
    238 		if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
    239 			break;
    240 		if (svs.challenges[i].time < oldestTime)
    241 		{
    242 			oldestTime = svs.challenges[i].time;
    243 			oldest = i;
    244 		}
    245 	}
    246 
    247 	if (i == MAX_CHALLENGES)
    248 	{
    249 		// overwrite the oldest
    250 		svs.challenges[oldest].challenge = rand() & 0x7fff;
    251 		svs.challenges[oldest].adr = net_from;
    252 		svs.challenges[oldest].time = curtime;
    253 		i = oldest;
    254 	}
    255 
    256 	// send it back
    257 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge);
    258 }
    259 
    260 /*
    261 ==================
    262 SVC_DirectConnect
    263 
    264 A connection request that did not come from the master
    265 ==================
    266 */
    267 void SVC_DirectConnect (void)
    268 {
    269 	char		userinfo[MAX_INFO_STRING];
    270 	netadr_t	adr;
    271 	int			i;
    272 	client_t	*cl, *newcl;
    273 	client_t	temp;
    274 	edict_t		*ent;
    275 	int			edictnum;
    276 	int			version;
    277 	int			qport;
    278 	int			challenge;
    279 
    280 	adr = net_from;
    281 
    282 	Com_DPrintf ("SVC_DirectConnect ()\n");
    283 
    284 	version = atoi(Cmd_Argv(1));
    285 	if (version != PROTOCOL_VERSION)
    286 	{
    287 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION);
    288 		Com_DPrintf ("    rejected connect from version %i\n", version);
    289 		return;
    290 	}
    291 
    292 	qport = atoi(Cmd_Argv(2));
    293 
    294 	challenge = atoi(Cmd_Argv(3));
    295 
    296 	strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1);
    297 	userinfo[sizeof(userinfo) - 1] = 0;
    298 
    299 	// force the IP key/value pair so the game can filter based on ip
    300 	Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from));
    301 
    302 	// attractloop servers are ONLY for local clients
    303 	if (sv.attractloop)
    304 	{
    305 		if (!NET_IsLocalAddress (adr))
    306 		{
    307 			Com_Printf ("Remote connect in attract loop.  Ignored.\n");
    308 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n");
    309 			return;
    310 		}
    311 	}
    312 
    313 	// see if the challenge is valid
    314 	if (!NET_IsLocalAddress (adr))
    315 	{
    316 		for (i=0 ; i<MAX_CHALLENGES ; i++)
    317 		{
    318 			if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
    319 			{
    320 				if (challenge == svs.challenges[i].challenge)
    321 					break;		// good
    322 				Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n");
    323 				return;
    324 			}
    325 		}
    326 		if (i == MAX_CHALLENGES)
    327 		{
    328 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n");
    329 			return;
    330 		}
    331 	}
    332 
    333 	newcl = &temp;
    334 	memset (newcl, 0, sizeof(client_t));
    335 
    336 	// if there is already a slot for this ip, reuse it
    337 	for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
    338 	{
    339 		if (cl->state == cs_free)
    340 			continue;
    341 		if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
    342 			&& ( cl->netchan.qport == qport 
    343 			|| adr.port == cl->netchan.remote_address.port ) )
    344 		{
    345 			if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000))
    346 			{
    347 				Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr));
    348 				return;
    349 			}
    350 			Com_Printf ("%s:reconnect\n", NET_AdrToString (adr));
    351 			newcl = cl;
    352 			goto gotnewcl;
    353 		}
    354 	}
    355 
    356 	// find a client slot
    357 	newcl = NULL;
    358 	for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
    359 	{
    360 		if (cl->state == cs_free)
    361 		{
    362 			newcl = cl;
    363 			break;
    364 		}
    365 	}
    366 	if (!newcl)
    367 	{
    368 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n");
    369 		Com_DPrintf ("Rejected a connection.\n");
    370 		return;
    371 	}
    372 
    373 gotnewcl:	
    374 	// build a new connection
    375 	// accept the new client
    376 	// this is the only place a client_t is ever initialized
    377 	*newcl = temp;
    378 	sv_client = newcl;
    379 	edictnum = (newcl-svs.clients)+1;
    380 	ent = EDICT_NUM(edictnum);
    381 	newcl->edict = ent;
    382 	newcl->challenge = challenge; // save challenge for checksumming
    383 
    384 	// get the game a chance to reject this connection or modify the userinfo
    385 	if (!(ge->ClientConnect (ent, userinfo)))
    386 	{
    387 		if (*Info_ValueForKey (userinfo, "rejmsg")) 
    388 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n",  
    389 				Info_ValueForKey (userinfo, "rejmsg"));
    390 		else
    391 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" );
    392 		Com_DPrintf ("Game rejected a connection.\n");
    393 		return;
    394 	}
    395 
    396 	// parse some info from the info strings
    397 	strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1);
    398 	SV_UserinfoChanged (newcl);
    399 
    400 	// send the connect packet to the client
    401 	Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect");
    402 
    403 	Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport);
    404 
    405 	newcl->state = cs_connected;
    406 	
    407 	SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) );
    408 	newcl->datagram.allowoverflow = true;
    409 	newcl->lastmessage = svs.realtime;	// don't timeout
    410 	newcl->lastconnect = svs.realtime;
    411 }
    412 
    413 int Rcon_Validate (void)
    414 {
    415 	if (!strlen (rcon_password->string))
    416 		return 0;
    417 
    418 	if (strcmp (Cmd_Argv(1), rcon_password->string) )
    419 		return 0;
    420 
    421 	return 1;
    422 }
    423 
    424 /*
    425 ===============
    426 SVC_RemoteCommand
    427 
    428 A client issued an rcon command.
    429 Shift down the remaining args
    430 Redirect all printfs
    431 ===============
    432 */
    433 void SVC_RemoteCommand (void)
    434 {
    435 	int		i;
    436 	char	remaining[1024];
    437 
    438 	i = Rcon_Validate ();
    439 
    440 	if (i == 0)
    441 		Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
    442 	else
    443 		Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
    444 
    445 	Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
    446 
    447 	if (!Rcon_Validate ())
    448 	{
    449 		Com_Printf ("Bad rcon_password.\n");
    450 	}
    451 	else
    452 	{
    453 		remaining[0] = 0;
    454 
    455 		for (i=2 ; i<Cmd_Argc() ; i++)
    456 		{
    457 			strcat (remaining, Cmd_Argv(i) );
    458 			strcat (remaining, " ");
    459 		}
    460 
    461 		Cmd_ExecuteString (remaining);
    462 	}
    463 
    464 	Com_EndRedirect ();
    465 }
    466 
    467 /*
    468 =================
    469 SV_ConnectionlessPacket
    470 
    471 A connectionless packet has four leading 0xff
    472 characters to distinguish it from a game channel.
    473 Clients that are in the game can still send
    474 connectionless packets.
    475 =================
    476 */
    477 void SV_ConnectionlessPacket (void)
    478 {
    479 	char	*s;
    480 	char	*c;
    481 
    482 	MSG_BeginReading (&net_message);
    483 	MSG_ReadLong (&net_message);		// skip the -1 marker
    484 
    485 	s = MSG_ReadStringLine (&net_message);
    486 
    487 	Cmd_TokenizeString (s, false);
    488 
    489 	c = Cmd_Argv(0);
    490 	Com_DPrintf ("Packet %s : %s\n", NET_AdrToString(net_from), c);
    491 
    492 	if (!strcmp(c, "ping"))
    493 		SVC_Ping ();
    494 	else if (!strcmp(c, "ack"))
    495 		SVC_Ack ();
    496 	else if (!strcmp(c,"status"))
    497 		SVC_Status ();
    498 	else if (!strcmp(c,"info"))
    499 		SVC_Info ();
    500 	else if (!strcmp(c,"getchallenge"))
    501 		SVC_GetChallenge ();
    502 	else if (!strcmp(c,"connect"))
    503 		SVC_DirectConnect ();
    504 	else if (!strcmp(c, "rcon"))
    505 		SVC_RemoteCommand ();
    506 	else
    507 		Com_Printf ("bad connectionless packet from %s:\n%s\n"
    508 		, NET_AdrToString (net_from), s);
    509 }
    510 
    511 
    512 //============================================================================
    513 
    514 /*
    515 ===================
    516 SV_CalcPings
    517 
    518 Updates the cl->ping variables
    519 ===================
    520 */
    521 void SV_CalcPings (void)
    522 {
    523 	int			i, j;
    524 	client_t	*cl;
    525 	int			total, count;
    526 
    527 	for (i=0 ; i<maxclients->value ; i++)
    528 	{
    529 		cl = &svs.clients[i];
    530 		if (cl->state != cs_spawned )
    531 			continue;
    532 
    533 #if 0
    534 		if (cl->lastframe > 0)
    535 			cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl->lastframe + 1;
    536 		else
    537 			cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0;
    538 #endif
    539 
    540 		total = 0;
    541 		count = 0;
    542 		for (j=0 ; j<LATENCY_COUNTS ; j++)
    543 		{
    544 			if (cl->frame_latency[j] > 0)
    545 			{
    546 				count++;
    547 				total += cl->frame_latency[j];
    548 			}
    549 		}
    550 		if (!count)
    551 			cl->ping = 0;
    552 		else
    553 #if 0
    554 			cl->ping = total*100/count - 100;
    555 #else
    556 			cl->ping = total / count;
    557 #endif
    558 
    559 		// let the game dll know about the ping
    560 		cl->edict->client->ping = cl->ping;
    561 	}
    562 }
    563 
    564 
    565 /*
    566 ===================
    567 SV_GiveMsec
    568 
    569 Every few frames, gives all clients an allotment of milliseconds
    570 for their command moves.  If they exceed it, assume cheating.
    571 ===================
    572 */
    573 void SV_GiveMsec (void)
    574 {
    575 	int			i;
    576 	client_t	*cl;
    577 
    578 	if (sv.framenum & 15)
    579 		return;
    580 
    581 	for (i=0 ; i<maxclients->value ; i++)
    582 	{
    583 		cl = &svs.clients[i];
    584 		if (cl->state == cs_free )
    585 			continue;
    586 		
    587 		cl->commandMsec = 1800;		// 1600 + some slop
    588 	}
    589 }
    590 
    591 
    592 /*
    593 =================
    594 SV_ReadPackets
    595 =================
    596 */
    597 void SV_ReadPackets (void)
    598 {
    599 	int			i;
    600 	client_t	*cl;
    601 	int			qport;
    602 
    603 	while (NET_GetPacket (NS_SERVER, &net_from, &net_message))
    604 	{
    605 		// check for connectionless packet (0xffffffff) first
    606 		if (*(int *)net_message.data == -1)
    607 		{
    608 			SV_ConnectionlessPacket ();
    609 			continue;
    610 		}
    611 
    612 		// read the qport out of the message so we can fix up
    613 		// stupid address translating routers
    614 		MSG_BeginReading (&net_message);
    615 		MSG_ReadLong (&net_message);		// sequence number
    616 		MSG_ReadLong (&net_message);		// sequence number
    617 		qport = MSG_ReadShort (&net_message) & 0xffff;
    618 
    619 		// check for packets from connected clients
    620 		for (i=0, cl=svs.clients ; i<maxclients->value ; i++,cl++)
    621 		{
    622 			if (cl->state == cs_free)
    623 				continue;
    624 			if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
    625 				continue;
    626 			if (cl->netchan.qport != qport)
    627 				continue;
    628 			if (cl->netchan.remote_address.port != net_from.port)
    629 			{
    630 				Com_Printf ("SV_ReadPackets: fixing up a translated port\n");
    631 				cl->netchan.remote_address.port = net_from.port;
    632 			}
    633 
    634 			if (Netchan_Process(&cl->netchan, &net_message))
    635 			{	// this is a valid, sequenced packet, so process it
    636 				if (cl->state != cs_zombie)
    637 				{
    638 					cl->lastmessage = svs.realtime;	// don't timeout
    639 					SV_ExecuteClientMessage (cl);
    640 				}
    641 			}
    642 			break;
    643 		}
    644 		
    645 		if (i != maxclients->value)
    646 			continue;
    647 	}
    648 }
    649 
    650 /*
    651 ==================
    652 SV_CheckTimeouts
    653 
    654 If a packet has not been received from a client for timeout->value
    655 seconds, drop the conneciton.  Server frames are used instead of
    656 realtime to avoid dropping the local client while debugging.
    657 
    658 When a client is normally dropped, the client_t goes into a zombie state
    659 for a few seconds to make sure any final reliable message gets resent
    660 if necessary
    661 ==================
    662 */
    663 void SV_CheckTimeouts (void)
    664 {
    665 	int		i;
    666 	client_t	*cl;
    667 	int			droppoint;
    668 	int			zombiepoint;
    669 
    670 	droppoint = svs.realtime - 1000*timeout->value;
    671 	zombiepoint = svs.realtime - 1000*zombietime->value;
    672 
    673 	for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
    674 	{
    675 		// message times may be wrong across a changelevel
    676 		if (cl->lastmessage > svs.realtime)
    677 			cl->lastmessage = svs.realtime;
    678 
    679 		if (cl->state == cs_zombie
    680 		&& cl->lastmessage < zombiepoint)
    681 		{
    682 			cl->state = cs_free;	// can now be reused
    683 			continue;
    684 		}
    685 		if ( (cl->state == cs_connected || cl->state == cs_spawned) 
    686 			&& cl->lastmessage < droppoint)
    687 		{
    688 			SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
    689 			SV_DropClient (cl); 
    690 			cl->state = cs_free;	// don't bother with zombie state
    691 		}
    692 	}
    693 }
    694 
    695 /*
    696 ================
    697 SV_PrepWorldFrame
    698 
    699 This has to be done before the world logic, because
    700 player processing happens outside RunWorldFrame
    701 ================
    702 */
    703 void SV_PrepWorldFrame (void)
    704 {
    705 	edict_t	*ent;
    706 	int		i;
    707 
    708 	for (i=0 ; i<ge->num_edicts ; i++, ent++)
    709 	{
    710 		ent = EDICT_NUM(i);
    711 		// events only last for a single message
    712 		ent->s.event = 0;
    713 	}
    714 
    715 }
    716 
    717 
    718 /*
    719 =================
    720 SV_RunGameFrame
    721 =================
    722 */
    723 void SV_RunGameFrame (void)
    724 {
    725 	if (host_speeds->value)
    726 		time_before_game = Sys_Milliseconds ();
    727 
    728 	// we always need to bump framenum, even if we
    729 	// don't run the world, otherwise the delta
    730 	// compression can get confused when a client
    731 	// has the "current" frame
    732 	sv.framenum++;
    733 	sv.time = sv.framenum*100;
    734 
    735 	// don't run if paused
    736 	if (!sv_paused->value || maxclients->value > 1)
    737 	{
    738 		ge->RunFrame ();
    739 
    740 		// never get more than one tic behind
    741 		if (sv.time < svs.realtime)
    742 		{
    743 			if (sv_showclamp->value)
    744 				Com_Printf ("sv highclamp\n");
    745 			svs.realtime = sv.time;
    746 		}
    747 	}
    748 
    749 	if (host_speeds->value)
    750 		time_after_game = Sys_Milliseconds ();
    751 
    752 }
    753 
    754 /*
    755 ==================
    756 SV_Frame
    757 
    758 ==================
    759 */
    760 void SV_Frame (int msec)
    761 {
    762 	time_before_game = time_after_game = 0;
    763 
    764 	// if server is not active, do nothing
    765 	if (!svs.initialized)
    766 		return;
    767 
    768     svs.realtime += msec;
    769 
    770 	// keep the random time dependent
    771 	rand ();
    772 
    773 	// check timeouts
    774 	SV_CheckTimeouts ();
    775 
    776 	// get packets from clients
    777 	SV_ReadPackets ();
    778 
    779 	// move autonomous things around if enough time has passed
    780 	if (!sv_timedemo->value && svs.realtime < sv.time)
    781 	{
    782 		// never let the time get too far off
    783 		if (sv.time - svs.realtime > 100)
    784 		{
    785 			if (sv_showclamp->value)
    786 				Com_Printf ("sv lowclamp\n");
    787 			svs.realtime = sv.time - 100;
    788 		}
    789 		NET_Sleep(sv.time - svs.realtime);
    790 		return;
    791 	}
    792 
    793 	// update ping based on the last known frame from all clients
    794 	SV_CalcPings ();
    795 
    796 	// give the clients some timeslices
    797 	SV_GiveMsec ();
    798 
    799 	// let everything in the world think and move
    800 	SV_RunGameFrame ();
    801 
    802 	// send messages back to the clients that had packets read this frame
    803 	SV_SendClientMessages ();
    804 
    805 	// save the entire world state if recording a serverdemo
    806 	SV_RecordDemoMessage ();
    807 
    808 	// send a heartbeat to the master if needed
    809 	Master_Heartbeat ();
    810 
    811 	// clear teleport flags, etc for next frame
    812 	SV_PrepWorldFrame ();
    813 
    814 }
    815 
    816 //============================================================================
    817 
    818 /*
    819 ================
    820 Master_Heartbeat
    821 
    822 Send a message to the master every few minutes to
    823 let it know we are alive, and log information
    824 ================
    825 */
    826 #define	HEARTBEAT_SECONDS	300
    827 void Master_Heartbeat (void)
    828 {
    829 	char		*string;
    830 	int			i;
    831 
    832 	
    833 	if (!dedicated->value)
    834 		return;		// only dedicated servers send heartbeats
    835 
    836 	if (!public_server->value)
    837 		return;		// a private dedicated game
    838 
    839 	// check for time wraparound
    840 	if (svs.last_heartbeat > svs.realtime)
    841 		svs.last_heartbeat = svs.realtime;
    842 
    843 	if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000)
    844 		return;		// not time to send yet
    845 
    846 	svs.last_heartbeat = svs.realtime;
    847 
    848 	// send the same string that we would give for a status OOB command
    849 	string = SV_StatusString();
    850 
    851 	// send to group master
    852 	for (i=0 ; i<MAX_MASTERS ; i++)
    853 		if (master_adr[i].port)
    854 		{
    855 			Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
    856 			Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "heartbeat\n%s", string);
    857 		}
    858 }
    859 
    860 /*
    861 =================
    862 Master_Shutdown
    863 
    864 Informs all masters that this server is going down
    865 =================
    866 */
    867 void Master_Shutdown (void)
    868 {
    869 	int			i;
    870 
    871 	if (!dedicated->value)
    872 		return;		// only dedicated servers send heartbeats
    873 
    874 	if (!public_server->value)
    875 		return;		// a private dedicated game
    876 
    877 	// send to group master
    878 	for (i=0 ; i<MAX_MASTERS ; i++)
    879 		if (master_adr[i].port)
    880 		{
    881 			if (i > 0)
    882 				Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
    883 			Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "shutdown");
    884 		}
    885 }
    886 
    887 //============================================================================
    888 
    889 
    890 /*
    891 =================
    892 SV_UserinfoChanged
    893 
    894 Pull specific info from a newly changed userinfo string
    895 into a more C freindly form.
    896 =================
    897 */
    898 void SV_UserinfoChanged (client_t *cl)
    899 {
    900 	char	*val;
    901 	int		i;
    902 
    903 	// call prog code to allow overrides
    904 	ge->ClientUserinfoChanged (cl->edict, cl->userinfo);
    905 	
    906 	// name for C code
    907 	strncpy (cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name)-1);
    908 	// mask off high bit
    909 	for (i=0 ; i<sizeof(cl->name) ; i++)
    910 		cl->name[i] &= 127;
    911 
    912 	// rate command
    913 	val = Info_ValueForKey (cl->userinfo, "rate");
    914 	if (strlen(val))
    915 	{
    916 		i = atoi(val);
    917 		cl->rate = i;
    918 		if (cl->rate < 100)
    919 			cl->rate = 100;
    920 		if (cl->rate > 15000)
    921 			cl->rate = 15000;
    922 	}
    923 	else
    924 		cl->rate = 5000;
    925 
    926 	// msg command
    927 	val = Info_ValueForKey (cl->userinfo, "msg");
    928 	if (strlen(val))
    929 	{
    930 		cl->messagelevel = atoi(val);
    931 	}
    932 
    933 }
    934 
    935 
    936 //============================================================================
    937 
    938 /*
    939 ===============
    940 SV_Init
    941 
    942 Only called at quake2.exe startup, not for each game
    943 ===============
    944 */
    945 void SV_Init (void)
    946 {
    947 	SV_InitOperatorCommands	();
    948 
    949 	rcon_password = Cvar_Get ("rcon_password", "", 0);
    950 	Cvar_Get ("skill", "1", 0);
    951 	Cvar_Get ("deathmatch", "0", CVAR_LATCH);
    952 	Cvar_Get ("coop", "0", CVAR_LATCH);
    953 	Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO);
    954 	Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO);
    955 	Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
    956 	Cvar_Get ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
    957 	Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET);;
    958 	maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
    959 	hostname = Cvar_Get ("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE);
    960 	timeout = Cvar_Get ("timeout", "125", 0);
    961 	zombietime = Cvar_Get ("zombietime", "2", 0);
    962 	sv_showclamp = Cvar_Get ("showclamp", "0", 0);
    963 	sv_paused = Cvar_Get ("paused", "0", 0);
    964 	sv_timedemo = Cvar_Get ("timedemo", "0", 0);
    965 	sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0);
    966 	allow_download = Cvar_Get ("allow_download", "0", CVAR_ARCHIVE);
    967 	allow_download_players  = Cvar_Get ("allow_download_players", "0", CVAR_ARCHIVE);
    968 	allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE);
    969 	allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE);
    970 	allow_download_maps	  = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE);
    971 
    972 	sv_noreload = Cvar_Get ("sv_noreload", "0", 0);
    973 
    974 	sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH);
    975 
    976 	public_server = Cvar_Get ("public", "0", 0);
    977 
    978 	sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE);
    979 
    980 	SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer));
    981 }
    982 
    983 /*
    984 ==================
    985 SV_FinalMessage
    986 
    987 Used by SV_Shutdown to send a final message to all
    988 connected clients before the server goes down.  The messages are sent immediately,
    989 not just stuck on the outgoing message list, because the server is going
    990 to totally exit after returning from this function.
    991 ==================
    992 */
    993 void SV_FinalMessage (char *message, qboolean reconnect)
    994 {
    995 	int			i;
    996 	client_t	*cl;
    997 	
    998 	SZ_Clear (&net_message);
    999 	MSG_WriteByte (&net_message, svc_print);
   1000 	MSG_WriteByte (&net_message, PRINT_HIGH);
   1001 	MSG_WriteString (&net_message, message);
   1002 
   1003 	if (reconnect)
   1004 		MSG_WriteByte (&net_message, svc_reconnect);
   1005 	else
   1006 		MSG_WriteByte (&net_message, svc_disconnect);
   1007 
   1008 	// send it twice
   1009 	// stagger the packets to crutch operating system limited buffers
   1010 
   1011 	for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
   1012 		if (cl->state >= cs_connected)
   1013 			Netchan_Transmit (&cl->netchan, net_message.cursize
   1014 			, net_message.data);
   1015 
   1016 	for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
   1017 		if (cl->state >= cs_connected)
   1018 			Netchan_Transmit (&cl->netchan, net_message.cursize
   1019 			, net_message.data);
   1020 }
   1021 
   1022 
   1023 
   1024 /*
   1025 ================
   1026 SV_Shutdown
   1027 
   1028 Called when each game quits,
   1029 before Sys_Quit or Sys_Error
   1030 ================
   1031 */
   1032 void SV_Shutdown (char *finalmsg, qboolean reconnect)
   1033 {
   1034 	if (svs.clients)
   1035 		SV_FinalMessage (finalmsg, reconnect);
   1036 
   1037 	Master_Shutdown ();
   1038 	SV_ShutdownGameProgs ();
   1039 
   1040 	// free current level
   1041 	if (sv.demofile)
   1042 		fclose (sv.demofile);
   1043 	memset (&sv, 0, sizeof(sv));
   1044 	Com_SetServerState (sv.state);
   1045 
   1046 	// free server static data
   1047 	if (svs.clients)
   1048 		Z_Free (svs.clients);
   1049 	if (svs.client_entities)
   1050 		Z_Free (svs.client_entities);
   1051 	if (svs.demofile)
   1052 		fclose (svs.demofile);
   1053 	memset (&svs, 0, sizeof(svs));
   1054 }
   1055