Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

sv_init.c (18747B)


      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 /*
     26 ===============
     27 SV_SetConfigstring
     28 
     29 ===============
     30 */
     31 void SV_SetConfigstring (int index, const char *val) {
     32 	int		len, i;
     33 	int		maxChunkSize = MAX_STRING_CHARS - 24;
     34 	client_t	*client;
     35 
     36 	if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
     37 		Com_Error (ERR_DROP, "SV_SetConfigstring: bad index %i\n", index);
     38 	}
     39 
     40 	if ( !val ) {
     41 		val = "";
     42 	}
     43 
     44 	// don't bother broadcasting an update if no change
     45 	if ( !strcmp( val, sv.configstrings[ index ] ) ) {
     46 		return;
     47 	}
     48 
     49 	// change the string in sv
     50 	Z_Free( sv.configstrings[index] );
     51 	sv.configstrings[index] = CopyString( val );
     52 
     53 	// send it to all the clients if we aren't
     54 	// spawning a new server
     55 	if ( sv.state == SS_GAME || sv.restarting ) {
     56 
     57 		// send the data to all relevent clients
     58 		for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) {
     59 			if ( client->state < CS_PRIMED ) {
     60 				continue;
     61 			}
     62 			// do not always send server info to all clients
     63 			if ( index == CS_SERVERINFO && client->gentity && (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
     64 				continue;
     65 			}
     66 
     67 			len = strlen( val );
     68 			if( len >= maxChunkSize ) {
     69 				int		sent = 0;
     70 				int		remaining = len;
     71 				char	*cmd;
     72 				char	buf[MAX_STRING_CHARS];
     73 
     74 				while (remaining > 0 ) {
     75 					if ( sent == 0 ) {
     76 						cmd = "bcs0";
     77 					}
     78 					else if( remaining < maxChunkSize ) {
     79 						cmd = "bcs2";
     80 					}
     81 					else {
     82 						cmd = "bcs1";
     83 					}
     84 					Q_strncpyz( buf, &val[sent], maxChunkSize );
     85 
     86 					SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd, index, buf );
     87 
     88 					sent += (maxChunkSize - 1);
     89 					remaining -= (maxChunkSize - 1);
     90 				}
     91 			} else {
     92 				// standard cs, just send it
     93 				SV_SendServerCommand( client, "cs %i \"%s\"\n", index, val );
     94 			}
     95 		}
     96 	}
     97 }
     98 
     99 
    100 
    101 /*
    102 ===============
    103 SV_GetConfigstring
    104 
    105 ===============
    106 */
    107 void SV_GetConfigstring( int index, char *buffer, int bufferSize ) {
    108 	if ( bufferSize < 1 ) {
    109 		Com_Error( ERR_DROP, "SV_GetConfigstring: bufferSize == %i", bufferSize );
    110 	}
    111 	if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
    112 		Com_Error (ERR_DROP, "SV_GetConfigstring: bad index %i\n", index);
    113 	}
    114 	if ( !sv.configstrings[index] ) {
    115 		buffer[0] = 0;
    116 		return;
    117 	}
    118 
    119 	Q_strncpyz( buffer, sv.configstrings[index], bufferSize );
    120 }
    121 
    122 
    123 /*
    124 ===============
    125 SV_SetUserinfo
    126 
    127 ===============
    128 */
    129 void SV_SetUserinfo( int index, const char *val ) {
    130 	if ( index < 0 || index >= sv_maxclients->integer ) {
    131 		Com_Error (ERR_DROP, "SV_SetUserinfo: bad index %i\n", index);
    132 	}
    133 
    134 	if ( !val ) {
    135 		val = "";
    136 	}
    137 
    138 	Q_strncpyz( svs.clients[index].userinfo, val, sizeof( svs.clients[ index ].userinfo ) );
    139 	Q_strncpyz( svs.clients[index].name, Info_ValueForKey( val, "name" ), sizeof(svs.clients[index].name) );
    140 }
    141 
    142 
    143 
    144 /*
    145 ===============
    146 SV_GetUserinfo
    147 
    148 ===============
    149 */
    150 void SV_GetUserinfo( int index, char *buffer, int bufferSize ) {
    151 	if ( bufferSize < 1 ) {
    152 		Com_Error( ERR_DROP, "SV_GetUserinfo: bufferSize == %i", bufferSize );
    153 	}
    154 	if ( index < 0 || index >= sv_maxclients->integer ) {
    155 		Com_Error (ERR_DROP, "SV_GetUserinfo: bad index %i\n", index);
    156 	}
    157 	Q_strncpyz( buffer, svs.clients[ index ].userinfo, bufferSize );
    158 }
    159 
    160 
    161 /*
    162 ================
    163 SV_CreateBaseline
    164 
    165 Entity baselines are used to compress non-delta messages
    166 to the clients -- only the fields that differ from the
    167 baseline will be transmitted
    168 ================
    169 */
    170 void SV_CreateBaseline( void ) {
    171 	sharedEntity_t *svent;
    172 	int				entnum;	
    173 
    174 	for ( entnum = 1; entnum < sv.num_entities ; entnum++ ) {
    175 		svent = SV_GentityNum(entnum);
    176 		if (!svent->r.linked) {
    177 			continue;
    178 		}
    179 		svent->s.number = entnum;
    180 
    181 		//
    182 		// take current state as baseline
    183 		//
    184 		sv.svEntities[entnum].baseline = svent->s;
    185 	}
    186 }
    187 
    188 
    189 /*
    190 ===============
    191 SV_BoundMaxClients
    192 
    193 ===============
    194 */
    195 void SV_BoundMaxClients( int minimum ) {
    196 	// get the current maxclients value
    197 	Cvar_Get( "sv_maxclients", "8", 0 );
    198 
    199 	sv_maxclients->modified = qfalse;
    200 
    201 	if ( sv_maxclients->integer < minimum ) {
    202 		Cvar_Set( "sv_maxclients", va("%i", minimum) );
    203 	} else if ( sv_maxclients->integer > MAX_CLIENTS ) {
    204 		Cvar_Set( "sv_maxclients", va("%i", MAX_CLIENTS) );
    205 	}
    206 }
    207 
    208 
    209 /*
    210 ===============
    211 SV_Startup
    212 
    213 Called when a host starts a map when it wasn't running
    214 one before.  Successive map or map_restart commands will
    215 NOT cause this to be called, unless the game is exited to
    216 the menu system first.
    217 ===============
    218 */
    219 void SV_Startup( void ) {
    220 	if ( svs.initialized ) {
    221 		Com_Error( ERR_FATAL, "SV_Startup: svs.initialized" );
    222 	}
    223 	SV_BoundMaxClients( 1 );
    224 
    225 	svs.clients = Z_Malloc (sizeof(client_t) * sv_maxclients->integer );
    226 	if ( com_dedicated->integer ) {
    227 		svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * 64;
    228 	} else {
    229 		// we don't need nearly as many when playing locally
    230 		svs.numSnapshotEntities = sv_maxclients->integer * 4 * 64;
    231 	}
    232 	svs.initialized = qtrue;
    233 
    234 	Cvar_Set( "sv_running", "1" );
    235 }
    236 
    237 
    238 /*
    239 ==================
    240 SV_ChangeMaxClients
    241 ==================
    242 */
    243 void SV_ChangeMaxClients( void ) {
    244 	int		oldMaxClients;
    245 	int		i;
    246 	client_t	*oldClients;
    247 	int		count;
    248 
    249 	// get the highest client number in use
    250 	count = 0;
    251 	for ( i = 0 ; i < sv_maxclients->integer ; i++ ) {
    252 		if ( svs.clients[i].state >= CS_CONNECTED ) {
    253 			if (i > count)
    254 				count = i;
    255 		}
    256 	}
    257 	count++;
    258 
    259 	oldMaxClients = sv_maxclients->integer;
    260 	// never go below the highest client number in use
    261 	SV_BoundMaxClients( count );
    262 	// if still the same
    263 	if ( sv_maxclients->integer == oldMaxClients ) {
    264 		return;
    265 	}
    266 
    267 	oldClients = Hunk_AllocateTempMemory( count * sizeof(client_t) );
    268 	// copy the clients to hunk memory
    269 	for ( i = 0 ; i < count ; i++ ) {
    270 		if ( svs.clients[i].state >= CS_CONNECTED ) {
    271 			oldClients[i] = svs.clients[i];
    272 		}
    273 		else {
    274 			Com_Memset(&oldClients[i], 0, sizeof(client_t));
    275 		}
    276 	}
    277 
    278 	// free old clients arrays
    279 	Z_Free( svs.clients );
    280 
    281 	// allocate new clients
    282 	svs.clients = Z_Malloc ( sv_maxclients->integer * sizeof(client_t) );
    283 	Com_Memset( svs.clients, 0, sv_maxclients->integer * sizeof(client_t) );
    284 
    285 	// copy the clients over
    286 	for ( i = 0 ; i < count ; i++ ) {
    287 		if ( oldClients[i].state >= CS_CONNECTED ) {
    288 			svs.clients[i] = oldClients[i];
    289 		}
    290 	}
    291 
    292 	// free the old clients on the hunk
    293 	Hunk_FreeTempMemory( oldClients );
    294 	
    295 	// allocate new snapshot entities
    296 	if ( com_dedicated->integer ) {
    297 		svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * 64;
    298 	} else {
    299 		// we don't need nearly as many when playing locally
    300 		svs.numSnapshotEntities = sv_maxclients->integer * 4 * 64;
    301 	}
    302 }
    303 
    304 /*
    305 ================
    306 SV_ClearServer
    307 ================
    308 */
    309 void SV_ClearServer(void) {
    310 	int i;
    311 
    312 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
    313 		if ( sv.configstrings[i] ) {
    314 			Z_Free( sv.configstrings[i] );
    315 		}
    316 	}
    317 	Com_Memset (&sv, 0, sizeof(sv));
    318 }
    319 
    320 /*
    321 ================
    322 SV_TouchCGame
    323 
    324   touch the cgame.vm so that a pure client can load it if it's in a seperate pk3
    325 ================
    326 */
    327 void SV_TouchCGame(void) {
    328 	fileHandle_t	f;
    329 	char filename[MAX_QPATH];
    330 
    331 	Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", "cgame" );
    332 	FS_FOpenFileRead( filename, &f, qfalse );
    333 	if ( f ) {
    334 		FS_FCloseFile( f );
    335 	}
    336 }
    337 
    338 /*
    339 ================
    340 SV_SpawnServer
    341 
    342 Change the server to a new map, taking all connected
    343 clients along with it.
    344 This is NOT called for map_restart
    345 ================
    346 */
    347 void SV_SpawnServer( char *server, qboolean killBots ) {
    348 	int			i;
    349 	int			checksum;
    350 	qboolean	isBot;
    351 	char		systemInfo[16384];
    352 	const char	*p;
    353 
    354 	// shut down the existing game if it is running
    355 	SV_ShutdownGameProgs();
    356 
    357 	Com_Printf ("------ Server Initialization ------\n");
    358 	Com_Printf ("Server: %s\n",server);
    359 
    360 	// if not running a dedicated server CL_MapLoading will connect the client to the server
    361 	// also print some status stuff
    362 	CL_MapLoading();
    363 
    364 	// make sure all the client stuff is unloaded
    365 	CL_ShutdownAll();
    366 
    367 	// clear the whole hunk because we're (re)loading the server
    368 	Hunk_Clear();
    369 
    370 	// clear collision map data
    371 	CM_ClearMap();
    372 
    373 	// init client structures and svs.numSnapshotEntities 
    374 	if ( !Cvar_VariableValue("sv_running") ) {
    375 		SV_Startup();
    376 	} else {
    377 		// check for maxclients change
    378 		if ( sv_maxclients->modified ) {
    379 			SV_ChangeMaxClients();
    380 		}
    381 	}
    382 
    383 	// clear pak references
    384 	FS_ClearPakReferences(0);
    385 
    386 	// allocate the snapshot entities on the hunk
    387 	svs.snapshotEntities = Hunk_Alloc( sizeof(entityState_t)*svs.numSnapshotEntities, h_high );
    388 	svs.nextSnapshotEntities = 0;
    389 
    390 	// toggle the server bit so clients can detect that a
    391 	// server has changed
    392 	svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;
    393 
    394 	// set nextmap to the same map, but it may be overriden
    395 	// by the game startup or another console command
    396 	Cvar_Set( "nextmap", "map_restart 0");
    397 //	Cvar_Set( "nextmap", va("map %s", server) );
    398 
    399 	// wipe the entire per-level structure
    400 	SV_ClearServer();
    401 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
    402 		sv.configstrings[i] = CopyString("");
    403 	}
    404 
    405 	// make sure we are not paused
    406 	Cvar_Set("cl_paused", "0");
    407 
    408 	// get a new checksum feed and restart the file system
    409 	srand(Com_Milliseconds());
    410 	sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds();
    411 	FS_Restart( sv.checksumFeed );
    412 
    413 	CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum );
    414 
    415 	// set serverinfo visible name
    416 	Cvar_Set( "mapname", server );
    417 
    418 	Cvar_Set( "sv_mapChecksum", va("%i",checksum) );
    419 
    420 	// serverid should be different each time
    421 	sv.serverId = com_frameTime;
    422 	sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe
    423 	sv.checksumFeedServerId = sv.serverId;
    424 	Cvar_Set( "sv_serverid", va("%i", sv.serverId ) );
    425 
    426 	// clear physics interaction links
    427 	SV_ClearWorld ();
    428 	
    429 	// media configstring setting should be done during
    430 	// the loading stage, so connected clients don't have
    431 	// to load during actual gameplay
    432 	sv.state = SS_LOADING;
    433 
    434 	// load and spawn all other entities
    435 	SV_InitGameProgs();
    436 
    437 	// don't allow a map_restart if game is modified
    438 	sv_gametype->modified = qfalse;
    439 
    440 	// run a few frames to allow everything to settle
    441 	for ( i = 0 ;i < 3 ; i++ ) {
    442 		VM_Call( gvm, GAME_RUN_FRAME, svs.time );
    443 		SV_BotFrame( svs.time );
    444 		svs.time += 100;
    445 	}
    446 
    447 	// create a baseline for more efficient communications
    448 	SV_CreateBaseline ();
    449 
    450 	for (i=0 ; i<sv_maxclients->integer ; i++) {
    451 		// send the new gamestate to all connected clients
    452 		if (svs.clients[i].state >= CS_CONNECTED) {
    453 			char	*denied;
    454 
    455 			if ( svs.clients[i].netchan.remoteAddress.type == NA_BOT ) {
    456 				if ( killBots ) {
    457 					SV_DropClient( &svs.clients[i], "" );
    458 					continue;
    459 				}
    460 				isBot = qtrue;
    461 			}
    462 			else {
    463 				isBot = qfalse;
    464 			}
    465 
    466 			// connect the client again
    467 			denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) );	// firstTime = qfalse
    468 			if ( denied ) {
    469 				// this generally shouldn't happen, because the client
    470 				// was connected before the level change
    471 				SV_DropClient( &svs.clients[i], denied );
    472 			} else {
    473 				if( !isBot ) {
    474 					// when we get the next packet from a connected client,
    475 					// the new gamestate will be sent
    476 					svs.clients[i].state = CS_CONNECTED;
    477 				}
    478 				else {
    479 					client_t		*client;
    480 					sharedEntity_t	*ent;
    481 
    482 					client = &svs.clients[i];
    483 					client->state = CS_ACTIVE;
    484 					ent = SV_GentityNum( i );
    485 					ent->s.number = i;
    486 					client->gentity = ent;
    487 
    488 					client->deltaMessage = -1;
    489 					client->nextSnapshotTime = svs.time;	// generate a snapshot immediately
    490 
    491 					VM_Call( gvm, GAME_CLIENT_BEGIN, i );
    492 				}
    493 			}
    494 		}
    495 	}	
    496 
    497 	// run another frame to allow things to look at all the players
    498 	VM_Call( gvm, GAME_RUN_FRAME, svs.time );
    499 	SV_BotFrame( svs.time );
    500 	svs.time += 100;
    501 
    502 	if ( sv_pure->integer ) {
    503 		// the server sends these to the clients so they will only
    504 		// load pk3s also loaded at the server
    505 		p = FS_LoadedPakChecksums();
    506 		Cvar_Set( "sv_paks", p );
    507 		if (strlen(p) == 0) {
    508 			Com_Printf( "WARNING: sv_pure set but no PK3 files loaded\n" );
    509 		}
    510 		p = FS_LoadedPakNames();
    511 		Cvar_Set( "sv_pakNames", p );
    512 
    513 		// if a dedicated pure server we need to touch the cgame because it could be in a
    514 		// seperate pk3 file and the client will need to load the latest cgame.qvm
    515 		if ( com_dedicated->integer ) {
    516 			SV_TouchCGame();
    517 		}
    518 	}
    519 	else {
    520 		Cvar_Set( "sv_paks", "" );
    521 		Cvar_Set( "sv_pakNames", "" );
    522 	}
    523 	// the server sends these to the clients so they can figure
    524 	// out which pk3s should be auto-downloaded
    525 	p = FS_ReferencedPakChecksums();
    526 	Cvar_Set( "sv_referencedPaks", p );
    527 	p = FS_ReferencedPakNames();
    528 	Cvar_Set( "sv_referencedPakNames", p );
    529 
    530 	// save systeminfo and serverinfo strings
    531 	Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) );
    532 	cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
    533 	SV_SetConfigstring( CS_SYSTEMINFO, systemInfo );
    534 
    535 	SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
    536 	cvar_modifiedFlags &= ~CVAR_SERVERINFO;
    537 
    538 	// any media configstring setting now should issue a warning
    539 	// and any configstring changes should be reliably transmitted
    540 	// to all clients
    541 	sv.state = SS_GAME;
    542 
    543 	// send a heartbeat now so the master will get up to date info
    544 	SV_Heartbeat_f();
    545 
    546 	Hunk_SetMark();
    547 
    548 	Com_Printf ("-----------------------------------\n");
    549 }
    550 
    551 /*
    552 ===============
    553 SV_Init
    554 
    555 Only called at main exe startup, not for each game
    556 ===============
    557 */
    558 void SV_BotInitBotLib(void);
    559 
    560 void SV_Init (void) {
    561 	SV_AddOperatorCommands ();
    562 
    563 	// serverinfo vars
    564 	Cvar_Get ("dmflags", "0", CVAR_SERVERINFO);
    565 	Cvar_Get ("fraglimit", "20", CVAR_SERVERINFO);
    566 	Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
    567 	sv_gametype = Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH );
    568 	Cvar_Get ("sv_keywords", "", CVAR_SERVERINFO);
    569 	Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_ROM);
    570 	sv_mapname = Cvar_Get ("mapname", "nomap", CVAR_SERVERINFO | CVAR_ROM);
    571 	sv_privateClients = Cvar_Get ("sv_privateClients", "0", CVAR_SERVERINFO);
    572 	sv_hostname = Cvar_Get ("sv_hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE );
    573 	sv_maxclients = Cvar_Get ("sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH);
    574 
    575 	sv_maxRate = Cvar_Get ("sv_maxRate", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
    576 	sv_minPing = Cvar_Get ("sv_minPing", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
    577 	sv_maxPing = Cvar_Get ("sv_maxPing", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
    578 	sv_floodProtect = Cvar_Get ("sv_floodProtect", "1", CVAR_ARCHIVE | CVAR_SERVERINFO );
    579 
    580 	// systeminfo
    581 	Cvar_Get ("sv_cheats", "1", CVAR_SYSTEMINFO | CVAR_ROM );
    582 	sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM );
    583 #ifndef DLL_ONLY // bk010216 - for DLL-only servers
    584 	sv_pure = Cvar_Get ("sv_pure", "1", CVAR_SYSTEMINFO );
    585 #else
    586 	sv_pure = Cvar_Get ("sv_pure", "0", CVAR_SYSTEMINFO | CVAR_INIT | CVAR_ROM );
    587 #endif
    588 	Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
    589 	Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
    590 	Cvar_Get ("sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM );
    591 	Cvar_Get ("sv_referencedPakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
    592 
    593 	// server vars
    594 	sv_rconPassword = Cvar_Get ("rconPassword", "", CVAR_TEMP );
    595 	sv_privatePassword = Cvar_Get ("sv_privatePassword", "", CVAR_TEMP );
    596 	sv_fps = Cvar_Get ("sv_fps", "20", CVAR_TEMP );
    597 	sv_timeout = Cvar_Get ("sv_timeout", "200", CVAR_TEMP );
    598 	sv_zombietime = Cvar_Get ("sv_zombietime", "2", CVAR_TEMP );
    599 	Cvar_Get ("nextmap", "", CVAR_TEMP );
    600 
    601 	sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO);
    602 	sv_master[0] = Cvar_Get ("sv_master1", MASTER_SERVER_NAME, 0 );
    603 	sv_master[1] = Cvar_Get ("sv_master2", "", CVAR_ARCHIVE );
    604 	sv_master[2] = Cvar_Get ("sv_master3", "", CVAR_ARCHIVE );
    605 	sv_master[3] = Cvar_Get ("sv_master4", "", CVAR_ARCHIVE );
    606 	sv_master[4] = Cvar_Get ("sv_master5", "", CVAR_ARCHIVE );
    607 	sv_reconnectlimit = Cvar_Get ("sv_reconnectlimit", "3", 0);
    608 	sv_showloss = Cvar_Get ("sv_showloss", "0", 0);
    609 	sv_padPackets = Cvar_Get ("sv_padPackets", "0", 0);
    610 	sv_killserver = Cvar_Get ("sv_killserver", "0", 0);
    611 	sv_mapChecksum = Cvar_Get ("sv_mapChecksum", "", CVAR_ROM);
    612 	sv_lanForceRate = Cvar_Get ("sv_lanForceRate", "1", CVAR_ARCHIVE );
    613 	sv_strictAuth = Cvar_Get ("sv_strictAuth", "1", CVAR_ARCHIVE );
    614 
    615 	// initialize bot cvars so they are listed and can be set before loading the botlib
    616 	SV_BotInitCvars();
    617 
    618 	// init the botlib here because we need the pre-compiler in the UI
    619 	SV_BotInitBotLib();
    620 }
    621 
    622 
    623 /*
    624 ==================
    625 SV_FinalMessage
    626 
    627 Used by SV_Shutdown to send a final message to all
    628 connected clients before the server goes down.  The messages are sent immediately,
    629 not just stuck on the outgoing message list, because the server is going
    630 to totally exit after returning from this function.
    631 ==================
    632 */
    633 void SV_FinalMessage( char *message ) {
    634 	int			i, j;
    635 	client_t	*cl;
    636 	
    637 	// send it twice, ignoring rate
    638 	for ( j = 0 ; j < 2 ; j++ ) {
    639 		for (i=0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++) {
    640 			if (cl->state >= CS_CONNECTED) {
    641 				// don't send a disconnect to a local client
    642 				if ( cl->netchan.remoteAddress.type != NA_LOOPBACK ) {
    643 					SV_SendServerCommand( cl, "print \"%s\"", message );
    644 					SV_SendServerCommand( cl, "disconnect" );
    645 				}
    646 				// force a snapshot to be sent
    647 				cl->nextSnapshotTime = -1;
    648 				SV_SendClientSnapshot( cl );
    649 			}
    650 		}
    651 	}
    652 }
    653 
    654 
    655 /*
    656 ================
    657 SV_Shutdown
    658 
    659 Called when each game quits,
    660 before Sys_Quit or Sys_Error
    661 ================
    662 */
    663 void SV_Shutdown( char *finalmsg ) {
    664 	if ( !com_sv_running || !com_sv_running->integer ) {
    665 		return;
    666 	}
    667 
    668 	Com_Printf( "----- Server Shutdown -----\n" );
    669 
    670 	if ( svs.clients && !com_errorEntered ) {
    671 		SV_FinalMessage( finalmsg );
    672 	}
    673 
    674 	SV_RemoveOperatorCommands();
    675 	SV_MasterShutdown();
    676 	SV_ShutdownGameProgs();
    677 
    678 	// free current level
    679 	SV_ClearServer();
    680 
    681 	// free server static data
    682 	if ( svs.clients ) {
    683 		Z_Free( svs.clients );
    684 	}
    685 	Com_Memset( &svs, 0, sizeof( svs ) );
    686 
    687 	Cvar_Set( "sv_running", "0" );
    688 	Cvar_Set("ui_singlePlayerActive", "0");
    689 
    690 	Com_Printf( "---------------------------\n" );
    691 
    692 	// disconnect any local clients
    693 	CL_Disconnect( qfalse );
    694 }
    695