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