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