1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 /*
30 * name: sv_init.c
31 *
32 * desc:
33 *
34 */
35
36 #include "server.h"
37
38 /*
39 ===============
40 SV_SendConfigstring
41
42 Creates and sends the server command necessary to update the CS index for the
43 given client
44 ===============
45 */
SV_SendConfigstring(client_t * client,int index)46 static void SV_SendConfigstring(client_t *client, int index)
47 {
48 int maxChunkSize = MAX_STRING_CHARS - 24;
49 int len;
50
51 len = strlen(sv.configstrings[index]);
52
53 if( len >= maxChunkSize ) {
54 int sent = 0;
55 int remaining = len;
56 char *cmd;
57 char buf[MAX_STRING_CHARS];
58
59 while (remaining > 0 ) {
60 if ( sent == 0 ) {
61 cmd = "bcs0";
62 }
63 else if( remaining < maxChunkSize ) {
64 cmd = "bcs2";
65 }
66 else {
67 cmd = "bcs1";
68 }
69 Q_strncpyz( buf, &sv.configstrings[index][sent],
70 maxChunkSize );
71
72 SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd,
73 index, buf );
74
75 sent += (maxChunkSize - 1);
76 remaining -= (maxChunkSize - 1);
77 }
78 } else {
79 // standard cs, just send it
80 SV_SendServerCommand( client, "cs %i \"%s\"\n", index,
81 sv.configstrings[index] );
82 }
83 }
84
85 /*
86 ===============
87 SV_UpdateConfigstrings
88
89 Called when a client goes from CS_PRIMED to CS_ACTIVE. Updates all
90 Configstring indexes that have changed while the client was in CS_PRIMED
91 ===============
92 */
SV_UpdateConfigstrings(client_t * client)93 void SV_UpdateConfigstrings(client_t *client)
94 {
95 int index;
96
97 for( index = 0; index < MAX_CONFIGSTRINGS; index++ ) {
98 // if the CS hasn't changed since we went to CS_PRIMED, ignore
99 if(!client->csUpdated[index])
100 continue;
101
102 // do not always send server info to all clients
103 if ( index == CS_SERVERINFO && client->gentity &&
104 (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
105 continue;
106 }
107 SV_SendConfigstring(client, index);
108 client->csUpdated[index] = qfalse;
109 }
110 }
111
112 /*
113 ===============
114 SV_SetConfigstring
115
116 ===============
117 */
SV_SetConfigstring(int index,const char * val)118 void SV_SetConfigstring( int index, const char *val ) {
119 int i;
120 client_t *client;
121
122 if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
123 Com_Error (ERR_DROP, "SV_SetConfigstring: bad index %i", index);
124 }
125
126 if ( !val ) {
127 val = "";
128 }
129
130 // don't bother broadcasting an update if no change
131 if ( !strcmp( val, sv.configstrings[ index ] ) ) {
132 return;
133 }
134
135 // change the string in sv
136 Z_Free( sv.configstrings[index] );
137 sv.configstrings[index] = CopyString( val );
138
139 // send it to all the clients if we aren't
140 // spawning a new server
141 if ( sv.state == SS_GAME || sv.restarting ) {
142 // SV_SendServerCommand( NULL, "cs %i \"%s\"\n", index, val );
143
144 // send the data to all relevent clients
145 for ( i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++ ) {
146 if ( client->state < CS_ACTIVE ) {
147 if ( client->state == CS_PRIMED )
148 client->csUpdated[ index ] = qtrue;
149 continue;
150 }
151 // do not always send server info to all clients
152 if ( index == CS_SERVERINFO && client->gentity && ( client->gentity->r.svFlags & SVF_NOSERVERINFO ) ) {
153 continue;
154 }
155
156 // RF, don't send to bot/AI
157 if ( sv_gametype->integer == GT_SINGLE_PLAYER && client->gentity && ( client->gentity->r.svFlags & SVF_CASTAI ) ) {
158 continue;
159 }
160
161 // SV_SendServerCommand( client, "cs %i \"%s\"\n", index, val );
162
163 SV_SendConfigstring(client, index);
164 }
165 }
166 }
167
168
169
170 /*
171 ===============
172 SV_GetConfigstring
173
174 ===============
175 */
SV_GetConfigstring(int index,char * buffer,int bufferSize)176 void SV_GetConfigstring( int index, char *buffer, int bufferSize ) {
177 if ( bufferSize < 1 ) {
178 Com_Error( ERR_DROP, "SV_GetConfigstring: bufferSize == %i", bufferSize );
179 }
180 if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
181 Com_Error( ERR_DROP, "SV_GetConfigstring: bad index %i", index );
182 }
183 if ( !sv.configstrings[index] ) {
184 buffer[0] = 0;
185 return;
186 }
187
188 Q_strncpyz( buffer, sv.configstrings[index], bufferSize );
189 }
190
191
192 /*
193 ===============
194 SV_SetUserinfo
195
196 ===============
197 */
SV_SetUserinfo(int index,const char * val)198 void SV_SetUserinfo( int index, const char *val ) {
199 if ( index < 0 || index >= sv_maxclients->integer ) {
200 Com_Error( ERR_DROP, "SV_SetUserinfo: bad index %i", index );
201 }
202
203 if ( !val ) {
204 val = "";
205 }
206
207 Q_strncpyz( svs.clients[index].userinfo, val, sizeof( svs.clients[ index ].userinfo ) );
208 Q_strncpyz( svs.clients[index].name, Info_ValueForKey( val, "name" ), sizeof( svs.clients[index].name ) );
209 }
210
211
212
213 /*
214 ===============
215 SV_GetUserinfo
216
217 ===============
218 */
SV_GetUserinfo(int index,char * buffer,int bufferSize)219 void SV_GetUserinfo( int index, char *buffer, int bufferSize ) {
220 if ( bufferSize < 1 ) {
221 Com_Error( ERR_DROP, "SV_GetUserinfo: bufferSize == %i", bufferSize );
222 }
223 if ( index < 0 || index >= sv_maxclients->integer ) {
224 Com_Error( ERR_DROP, "SV_GetUserinfo: bad index %i", index );
225 }
226 Q_strncpyz( buffer, svs.clients[ index ].userinfo, bufferSize );
227 }
228
229
230 /*
231 ================
232 SV_CreateBaseline
233
234 Entity baselines are used to compress non-delta messages
235 to the clients -- only the fields that differ from the
236 baseline will be transmitted
237 ================
238 */
SV_CreateBaseline(void)239 static void SV_CreateBaseline( void ) {
240 sharedEntity_t *svent;
241 int entnum;
242
243 for ( entnum = 1; entnum < sv.num_entities ; entnum++ ) {
244 svent = SV_GentityNum( entnum );
245 if ( !svent->r.linked ) {
246 continue;
247 }
248 svent->s.number = entnum;
249
250 //
251 // take current state as baseline
252 //
253 sv.svEntities[entnum].baseline = svent->s;
254 }
255 }
256
257
258 /*
259 ===============
260 SV_BoundMaxClients
261
262 ===============
263 */
SV_BoundMaxClients(int minimum)264 static void SV_BoundMaxClients( int minimum ) {
265 // get the current maxclients value
266 Cvar_Get( "sv_maxclients", "20", 0 ); // NERVE - SMF - changed to 20 from 8
267
268 sv_maxclients->modified = qfalse;
269
270 if ( sv_maxclients->integer < minimum ) {
271 Cvar_Set( "sv_maxclients", va( "%i", minimum ) );
272 } else if ( sv_maxclients->integer > MAX_CLIENTS ) {
273 Cvar_Set( "sv_maxclients", va( "%i", MAX_CLIENTS ) );
274 }
275 }
276
277
278 /*
279 ===============
280 SV_Startup
281
282 Called when a host starts a map when it wasn't running
283 one before. Successive map or map_restart commands will
284 NOT cause this to be called, unless the game is exited to
285 the menu system first.
286 ===============
287 */
SV_Startup(void)288 static void SV_Startup( void ) {
289 if ( svs.initialized ) {
290 Com_Error( ERR_FATAL, "SV_Startup: svs.initialized" );
291 }
292 SV_BoundMaxClients( 1 );
293
294 // RF, avoid trying to allocate large chunk on a fragmented zone
295 svs.clients = calloc( sizeof( client_t ) * sv_maxclients->integer, 1 );
296 if ( !svs.clients ) {
297 Com_Error( ERR_FATAL, "SV_Startup: unable to allocate svs.clients" );
298 }
299 //svs.clients = Z_Malloc (sizeof(client_t) * sv_maxclients->integer );
300
301 if ( com_dedicated->integer ) {
302 svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * MAX_SNAPSHOT_ENTITIES;
303 } else {
304 // we don't need nearly as many when playing locally
305 svs.numSnapshotEntities = sv_maxclients->integer * 4 * MAX_SNAPSHOT_ENTITIES;
306 }
307 svs.initialized = qtrue;
308
309 // Don't respect sv_killserver unless a server is actually running
310 if ( sv_killserver->integer ) {
311 Cvar_Set( "sv_killserver", "0" );
312 }
313
314 Cvar_Set( "sv_running", "1" );
315
316 // Join the ipv6 multicast group now that a map is running so clients can scan for us on the local network.
317 NET_JoinMulticast6();
318 }
319
320
321 /*
322 ==================
323 SV_ChangeMaxClients
324 ==================
325 */
SV_ChangeMaxClients(void)326 void SV_ChangeMaxClients( void ) {
327 int oldMaxClients;
328 int i;
329 client_t *oldClients;
330 int count;
331
332 // get the highest client number in use
333 count = 0;
334 for ( i = 0 ; i < sv_maxclients->integer ; i++ ) {
335 if ( svs.clients[i].state >= CS_CONNECTED ) {
336 if ( i > count ) {
337 count = i;
338 }
339 }
340 }
341 count++;
342
343 oldMaxClients = sv_maxclients->integer;
344 // never go below the highest client number in use
345 SV_BoundMaxClients( count );
346 // if still the same
347 if ( sv_maxclients->integer == oldMaxClients ) {
348 return;
349 }
350
351 oldClients = Hunk_AllocateTempMemory( count * sizeof( client_t ) );
352 // copy the clients to hunk memory
353 for ( i = 0 ; i < count ; i++ ) {
354 if ( svs.clients[i].state >= CS_CONNECTED ) {
355 oldClients[i] = svs.clients[i];
356 } else {
357 Com_Memset( &oldClients[i], 0, sizeof( client_t ) );
358 }
359 }
360
361 // free old clients arrays
362 //Z_Free( svs.clients );
363 free( svs.clients ); // RF, avoid trying to allocate large chunk on a fragmented zone
364
365 // allocate new clients
366 // RF, avoid trying to allocate large chunk on a fragmented zone
367 svs.clients = calloc( sizeof( client_t ) * sv_maxclients->integer, 1 );
368 if ( !svs.clients ) {
369 Com_Error( ERR_FATAL, "SV_Startup: unable to allocate svs.clients" );
370 }
371 //svs.clients = Z_Malloc ( sv_maxclients->integer * sizeof(client_t) );
372
373 Com_Memset( svs.clients, 0, sv_maxclients->integer * sizeof( client_t ) );
374
375 // copy the clients over
376 for ( i = 0 ; i < count ; i++ ) {
377 if ( oldClients[i].state >= CS_CONNECTED ) {
378 svs.clients[i] = oldClients[i];
379 }
380 }
381
382 // free the old clients on the hunk
383 Hunk_FreeTempMemory( oldClients );
384
385 // allocate new snapshot entities
386 if ( com_dedicated->integer ) {
387 svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * MAX_SNAPSHOT_ENTITIES;
388 } else {
389 // we don't need nearly as many when playing locally
390 svs.numSnapshotEntities = sv_maxclients->integer * 4 * MAX_SNAPSHOT_ENTITIES;
391 }
392 }
393
394
395 /*
396 ====================
397 SV_SetExpectedHunkUsage
398
399 Sets com_expectedhunkusage, so the client knows how to draw the percentage bar
400 ====================
401 */
SV_SetExpectedHunkUsage(char * mapname)402 void SV_SetExpectedHunkUsage( char *mapname ) {
403 int handle;
404 char *memlistfile = "hunkusage.dat";
405 char *buf;
406 char *buftrav;
407 char *token;
408 int len;
409
410 len = FS_FOpenFileByMode( memlistfile, &handle, FS_READ );
411 if ( len >= 0 ) { // the file exists, so read it in, strip out the current entry for this map, and save it out, so we can append the new value
412
413 buf = (char *)Z_Malloc( len + 1 );
414 memset( buf, 0, len + 1 );
415
416 FS_Read( (void *)buf, len, handle );
417 FS_FCloseFile( handle );
418
419 // now parse the file, filtering out the current map
420 buftrav = buf;
421 while ( ( token = COM_Parse( &buftrav ) ) && token[0] ) {
422 if ( !Q_strcasecmp( token, mapname ) ) {
423 // found a match
424 token = COM_Parse( &buftrav ); // read the size
425 if ( token && token[0] ) {
426 // this is the usage
427 Cvar_Set( "com_expectedhunkusage", token );
428 Z_Free( buf );
429 return;
430 }
431 }
432 }
433
434 Z_Free( buf );
435 }
436 // just set it to a negative number,so the cgame knows not to draw the percent bar
437 Cvar_Set( "com_expectedhunkusage", "-1" );
438 }
439
440 /*
441 ================
442 SV_ClearServer
443 ================
444 */
SV_ClearServer(void)445 static void SV_ClearServer(void) {
446 int i;
447
448 for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
449 if ( sv.configstrings[i] ) {
450 Z_Free( sv.configstrings[i] );
451 }
452 }
453 Com_Memset (&sv, 0, sizeof(sv));
454 }
455
456 /*
457 ================
458 SV_TouchFile
459 ================
460 */
SV_TouchFile(const char * filename)461 static void SV_TouchFile( const char *filename ) {
462 fileHandle_t f;
463
464 FS_FOpenFileRead_Filtered( filename, &f, qfalse, FS_EXCLUDE_DIR );
465 if ( f ) {
466 FS_FCloseFile( f );
467 }
468 }
469
470
471 /*
472 ================
473 SV_TouchCGameDLL
474 touch the cgame DLL so that a pure client (with DLL sv_pure support) can load do the correct checks
475 ================
476
477 void SV_TouchCGameDLL( void ) {
478 fileHandle_t f;
479 char *filename;
480
481 // Only touch the legacy dll since we have qvm support
482 filename = "cgame_mp_x86.dll";
483 FS_FOpenFileRead_Filtered( filename, &f, qfalse, FS_EXCLUDE_DIR );
484 if ( f ) {
485 FS_FCloseFile( f );
486 }
487 }
488 */
489
490 /*
491 ================
492 SV_SpawnServer
493
494 Change the server to a new map, taking all connected
495 clients along with it.
496 This is NOT called for map_restart
497 ================
498 */
SV_SpawnServer(char * server,qboolean killBots)499 void SV_SpawnServer( char *server, qboolean killBots ) {
500 int i;
501 int checksum;
502 qboolean isBot;
503 char systemInfo[16384];
504 const char *p;
505
506 // shut down the existing game if it is running
507 SV_ShutdownGameProgs();
508
509 Com_Printf( "------ Server Initialization ------\n" );
510 Com_Printf( "Server: %s\n",server );
511
512 // if not running a dedicated server CL_MapLoading will connect the client to the server
513 // also print some status stuff
514 CL_MapLoading();
515
516 // make sure all the client stuff is unloaded
517 CL_ShutdownAll(qfalse);
518
519 // clear the whole hunk because we're (re)loading the server
520 Hunk_Clear();
521
522 // clear collision map data
523 CM_ClearMap();
524
525 // init client structures and svs.numSnapshotEntities
526 if ( !Cvar_VariableValue( "sv_running" ) ) {
527 SV_Startup();
528 } else {
529 // check for maxclients change
530 if ( sv_maxclients->modified ) {
531 SV_ChangeMaxClients();
532 }
533 }
534
535 // clear pak references
536 FS_ClearPakReferences( 0 );
537
538 // allocate the snapshot entities on the hunk
539 svs.snapshotEntities = Hunk_Alloc( sizeof( entityState_t ) * svs.numSnapshotEntities, h_high );
540 svs.nextSnapshotEntities = 0;
541
542 // toggle the server bit so clients can detect that a
543 // server has changed
544 svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;
545
546 // set nextmap to the same map, but it may be overriden
547 // by the game startup or another console command
548 Cvar_Set( "nextmap", "map_restart 0" );
549 // Cvar_Set( "nextmap", va("map %s", server) );
550
551 for (i=0 ; i<sv_maxclients->integer ; i++) {
552 // save when the server started for each client already connected
553 if (svs.clients[i].state >= CS_CONNECTED) {
554 svs.clients[i].oldServerTime = sv.time;
555 }
556 }
557
558 // wipe the entire per-level structure
559 SV_ClearServer();
560
561 // MrE: main zone should be pretty much emtpy at this point
562 // except for file system data and cached renderer data
563 Z_LogHeap();
564
565 // allocate empty config strings
566 for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
567 sv.configstrings[i] = CopyString( "" );
568 }
569
570 // Ridah
571 // DHM - Nerve :: We want to use the completion bar in multiplayer as well
572 if ( sv_gametype->integer == GT_SINGLE_PLAYER || sv_gametype->integer >= GT_WOLF ) {
573 SV_SetExpectedHunkUsage( va( "maps/%s.bsp", server ) );
574 } else {
575 // just set it to a negative number,so the cgame knows not to draw the percent bar
576 Cvar_Set( "com_expectedhunkusage", "-1" );
577 }
578
579 // make sure we are not paused
580 Cvar_Set( "cl_paused", "0" );
581
582 // get a new checksum feed and restart the file system
583 sv.checksumFeed = ( ( (unsigned int)rand() << 16 ) ^ (unsigned int)rand() ) ^ Com_Milliseconds();
584
585 FS_Restart( sv.checksumFeed );
586
587 // Load map config if present
588 Cbuf_ExecuteText(EXEC_NOW, va( "exec mapcfgs/%s.cfg\n", server ) );
589
590 CM_LoadMap( va( "maps/%s.bsp", server ), qfalse, &checksum );
591
592 // set serverinfo visible name
593 Cvar_Set( "mapname", server );
594
595 Cvar_Set( "sv_mapChecksum", va( "%i",checksum ) );
596
597 // serverid should be different each time
598 sv.serverId = com_frameTime;
599 sv.restartedServerId = sv.serverId;
600 sv.checksumFeedServerId = sv.serverId;
601 Cvar_Set( "sv_serverid", va( "%i", sv.serverId ) );
602
603 // clear physics interaction links
604 SV_ClearWorld();
605
606 // media configstring setting should be done during
607 // the loading stage, so connected clients don't have
608 // to load during actual gameplay
609 sv.state = SS_LOADING;
610
611 Cvar_Set( "sv_serverRestarting", "1" );
612
613 // load and spawn all other entities
614 SV_InitGameProgs();
615
616 // don't allow a map_restart if game is modified
617 sv_gametype->modified = qfalse;
618
619 // run a few frames to allow everything to settle
620 for (i = 0;i < 3; i++)
621 {
622 VM_Call (gvm, GAME_RUN_FRAME, sv.time);
623 SV_BotFrame (sv.time);
624 sv.time += 100;
625 svs.time += 100;
626 }
627
628 // create a baseline for more efficient communications
629 SV_CreateBaseline();
630
631 for ( i = 0 ; i < sv_maxclients->integer ; i++ ) {
632 // send the new gamestate to all connected clients
633 if ( svs.clients[i].state >= CS_CONNECTED ) {
634 char *denied;
635
636 if ( svs.clients[i].netchan.remoteAddress.type == NA_BOT ) {
637 if ( killBots || Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) {
638 SV_DropClient( &svs.clients[i], "" );
639 continue;
640 }
641 isBot = qtrue;
642 } else {
643 isBot = qfalse;
644 }
645
646 // connect the client again
647 denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) ); // firstTime = qfalse
648 if ( denied ) {
649 // this generally shouldn't happen, because the client
650 // was connected before the level change
651 SV_DropClient( &svs.clients[i], denied );
652 } else {
653 if ( !isBot ) {
654 // when we get the next packet from a connected client,
655 // the new gamestate will be sent
656 svs.clients[i].state = CS_CONNECTED;
657 } else {
658 client_t *client;
659 sharedEntity_t *ent;
660
661 client = &svs.clients[i];
662 client->state = CS_ACTIVE;
663 ent = SV_GentityNum( i );
664 ent->s.number = i;
665 client->gentity = ent;
666
667 client->deltaMessage = -1;
668 client->lastSnapshotTime = 0; // generate a snapshot immediately
669
670 VM_Call( gvm, GAME_CLIENT_BEGIN, i );
671 }
672 }
673 }
674 }
675
676 // run another frame to allow things to look at all the players
677 VM_Call (gvm, GAME_RUN_FRAME, sv.time);
678 SV_BotFrame (sv.time);
679 sv.time += 100;
680 svs.time += 100;
681
682 if ( sv_pure->integer ) {
683 // the server sends these to the clients so they will only
684 // load pk3s also loaded at the server
685 p = FS_LoadedPakChecksums();
686 Cvar_Set( "sv_paks", p );
687 if ( strlen( p ) == 0 ) {
688 Com_Printf( "WARNING: sv_pure set but no PK3 files loaded\n" );
689 }
690 p = FS_LoadedPakNames();
691 Cvar_Set( "sv_pakNames", p );
692
693 // we want the server to reference the mp_bin pk3 that the client is expected to load from
694 // we need to touch the cgame and ui because they could be in
695 // seperate pk3 files and the client will need to download the pk3
696 // files with the latest cgame and ui to pass the pure check
697
698 // Only touch the legacy dlls since we have qvm support
699 SV_TouchFile( "cgame_mp_x86.dll" );
700 SV_TouchFile( "ui_mp_x86.dll" );
701 } else {
702 Cvar_Set( "sv_paks", "" );
703 Cvar_Set( "sv_pakNames", "" );
704 }
705 // the server sends these to the clients so they can figure
706 // out which pk3s should be auto-downloaded
707 // NOTE: we consider the referencedPaks as 'required for operation'
708 p = FS_ReferencedPakChecksums();
709 Cvar_Set( "sv_referencedPaks", p );
710 p = FS_ReferencedPakNames();
711 Cvar_Set( "sv_referencedPakNames", p );
712
713 // save systeminfo and serverinfo strings
714 Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) );
715 cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
716 SV_SetConfigstring( CS_SYSTEMINFO, systemInfo );
717
718 SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
719 cvar_modifiedFlags &= ~CVAR_SERVERINFO;
720
721 // NERVE - SMF
722 SV_SetConfigstring( CS_WOLFINFO, Cvar_InfoString( CVAR_WOLFINFO ) );
723 cvar_modifiedFlags &= ~CVAR_WOLFINFO;
724
725 // any media configstring setting now should issue a warning
726 // and any configstring changes should be reliably transmitted
727 // to all clients
728 sv.state = SS_GAME;
729
730 // send a heartbeat now so the master will get up to date info
731 SV_Heartbeat_f();
732
733 Hunk_SetMark();
734
735 #ifndef DEDICATED
736 if ( com_dedicated->integer ) {
737 // restart renderer in order to show console for dedicated servers
738 // launched through the regular binary
739 CL_StartHunkUsers( qtrue );
740 }
741 #endif
742
743 Cvar_Set( "sv_serverRestarting", "0" );
744
745 Com_Printf( "-----------------------------------\n" );
746 }
747
748 // DHM - Nerve :: Update Server
749 #ifdef UPDATE_SERVER
750 /*
751 ====================
752 SV_ParseVersionMapping
753
754 Reads versionmap.cfg which sets up a mapping of client version to installer to download
755 ====================
756 */
SV_ParseVersionMapping(void)757 void SV_ParseVersionMapping( void ) {
758 int handle;
759 char *filename = "versionmap.cfg";
760 char *buf;
761 char *buftrav;
762 char *token;
763 int len;
764
765 len = FS_SV_FOpenFileRead( filename, &handle );
766 if ( len >= 0 ) { // the file exists
767
768 buf = (char *)Z_Malloc( len + 1 );
769 memset( buf, 0, len + 1 );
770
771 FS_Read( (void *)buf, len, handle );
772 FS_FCloseFile( handle );
773
774 // now parse the file, setting the version table info
775 buftrav = buf;
776
777 token = COM_Parse( &buftrav );
778 if ( strcmp( token, "RTCW-VersionMap" ) ) {
779 Z_Free( buf );
780 Com_Error( ERR_FATAL, "invalid versionmap.cfg" );
781 return;
782 }
783
784 Com_Printf( "\n------------Update Server-------------\n\nParsing version map..." );
785
786 while ( ( token = COM_Parse( &buftrav ) ) && token[0] ) {
787 // read the version number
788 strcpy( versionMap[ numVersions ].version, token );
789
790 // read the platform
791 token = COM_Parse( &buftrav );
792 if ( token && token[0] ) {
793 strcpy( versionMap[ numVersions ].platform, token );
794 } else {
795 Z_Free( buf );
796 Com_Error( ERR_FATAL, "error parsing versionmap.cfg, after %s", versionMap[ numVersions ].version );
797 return;
798 }
799
800 // read the installer name
801 token = COM_Parse( &buftrav );
802 if ( token && token[0] ) {
803 strcpy( versionMap[ numVersions ].installer, token );
804 } else {
805 Z_Free( buf );
806 Com_Error( ERR_FATAL, "error parsing versionmap.cfg, after %s", versionMap[ numVersions ].platform );
807 return;
808 }
809
810 numVersions++;
811 if ( numVersions >= MAX_UPDATE_VERSIONS ) {
812 Z_Free( buf );
813 Com_Error( ERR_FATAL, "Exceeded maximum number of mappings(%d)", MAX_UPDATE_VERSIONS );
814 return;
815 }
816
817 }
818
819 Com_Printf( " found %d mapping%c\n--------------------------------------\n\n", numVersions, numVersions > 1 ? 's' : ' ' );
820
821 Z_Free( buf );
822 } else {
823 Com_Error( ERR_FATAL, "Couldn't open versionmap.cfg" );
824 }
825 }
826 #endif
827
828 /*
829 ===============
830 SV_Init
831
832 Only called at main exe startup, not for each game
833 ===============
834 */
SV_Init(void)835 void SV_Init( void ) {
836 int index;
837
838 SV_AddOperatorCommands();
839
840 // serverinfo vars
841 Cvar_Get( "dmflags", "0", /*CVAR_SERVERINFO*/ 0 );
842 Cvar_Get( "fraglimit", "0", /*CVAR_SERVERINFO*/ 0 );
843 Cvar_Get( "timelimit", "0", CVAR_SERVERINFO );
844 // DHM - Nerve :: default to GT_WOLF
845 sv_gametype = Cvar_Get( "g_gametype", "5", CVAR_SERVERINFO | CVAR_LATCH );
846
847 // Rafael gameskill
848 sv_gameskill = Cvar_Get( "g_gameskill", "3", CVAR_SERVERINFO | CVAR_LATCH );
849 // done
850
851 Cvar_Get( "sv_keywords", "", CVAR_SERVERINFO );
852 sv_mapname = Cvar_Get( "mapname", "nomap", CVAR_SERVERINFO | CVAR_ROM );
853 sv_privateClients = Cvar_Get( "sv_privateClients", "0", CVAR_SERVERINFO );
854 sv_hostname = Cvar_Get( "sv_hostname", "WolfHost", CVAR_SERVERINFO | CVAR_ARCHIVE );
855 sv_maxclients = Cvar_Get( "sv_maxclients", "20", CVAR_SERVERINFO | CVAR_LATCH ); // NERVE - SMF - changed to 20 from 8
856
857 sv_minRate = Cvar_Get ("sv_minRate", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
858 sv_maxRate = Cvar_Get( "sv_maxRate", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
859 sv_dlRate = Cvar_Get("sv_dlRate", "100", CVAR_ARCHIVE | CVAR_SERVERINFO);
860 sv_minPing = Cvar_Get( "sv_minPing", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
861 sv_maxPing = Cvar_Get( "sv_maxPing", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
862 sv_floodProtect = Cvar_Get( "sv_floodProtect", "1", CVAR_ARCHIVE | CVAR_SERVERINFO );
863 sv_allowAnonymous = Cvar_Get( "sv_allowAnonymous", "0", CVAR_SERVERINFO );
864 sv_friendlyFire = Cvar_Get( "g_friendlyFire", "1", CVAR_SERVERINFO | CVAR_ARCHIVE ); // NERVE - SMF
865 sv_maxlives = Cvar_Get( "g_maxlives", "0", CVAR_ARCHIVE | CVAR_LATCH | CVAR_SERVERINFO ); // NERVE - SMF
866 sv_tourney = Cvar_Get( "g_noTeamSwitching", "0", CVAR_ARCHIVE ); // NERVE - SMF
867
868 // systeminfo
869 Cvar_Get( "sv_cheats", "1", CVAR_SYSTEMINFO | CVAR_ROM );
870 sv_serverid = Cvar_Get( "sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM );
871 sv_pure = Cvar_Get( "sv_pure", "1", CVAR_SYSTEMINFO );
872 #ifdef USE_VOIP
873 sv_voip = Cvar_Get("sv_voip", "1", CVAR_LATCH);
874 Cvar_CheckRange(sv_voip, 0, 1, qtrue);
875 sv_voipProtocol = Cvar_Get("sv_voipProtocol", sv_voip->integer ? "opus" : "", CVAR_SYSTEMINFO | CVAR_ROM );
876 #endif
877 Cvar_Get( "sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
878 Cvar_Get( "sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
879 Cvar_Get( "sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM );
880 Cvar_Get( "sv_referencedPakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
881
882 #if defined ANTIWALLHACK
883 awh_active = Cvar_Get("awh_active", "0", CVAR_SYSTEMINFO);
884 awh_bbox_horz = Cvar_Get("awh_bbox_horz", "30", CVAR_SYSTEMINFO);
885 awh_bbox_vert = Cvar_Get("awh_bbox_vert", "60", CVAR_SYSTEMINFO);
886
887 AWH_Init();
888 #endif
889
890 // server vars
891 sv_rconPassword = Cvar_Get( "rconPassword", "", CVAR_TEMP );
892 sv_privatePassword = Cvar_Get( "sv_privatePassword", "", CVAR_TEMP );
893 #ifndef UPDATE_SERVER
894 sv_fps = Cvar_Get( "sv_fps", "20", CVAR_TEMP );
895 #else
896 sv_fps = Cvar_Get( "sv_fps", "60", CVAR_TEMP ); // this allows faster downloads
897 #endif
898 sv_timeout = Cvar_Get( "sv_timeout", "240", CVAR_TEMP );
899 sv_zombietime = Cvar_Get( "sv_zombietime", "2", CVAR_TEMP );
900 Cvar_Get( "nextmap", "", CVAR_TEMP );
901
902 sv_allowDownload = Cvar_Get( "sv_allowDownload", "1", CVAR_ARCHIVE );
903 Cvar_Get ("sv_dlURL", "", CVAR_SERVERINFO | CVAR_ARCHIVE);
904
905 sv_master[0] = Cvar_Get("sv_master1", MASTER_SERVER_NAME, 0);
906 sv_master[1] = Cvar_Get("sv_master2", "master.iortcw.org", 0);
907 for(index = 2; index < MAX_MASTER_SERVERS; index++)
908 sv_master[index] = Cvar_Get(va("sv_master%d", index + 1), "", CVAR_ARCHIVE);
909
910 sv_reconnectlimit = Cvar_Get( "sv_reconnectlimit", "3", 0 );
911 sv_showloss = Cvar_Get( "sv_showloss", "0", 0 );
912 sv_padPackets = Cvar_Get( "sv_padPackets", "0", 0 );
913 sv_killserver = Cvar_Get( "sv_killserver", "0", 0 );
914 sv_mapChecksum = Cvar_Get( "sv_mapChecksum", "", CVAR_ROM );
915 sv_lanForceRate = Cvar_Get( "sv_lanForceRate", "1", CVAR_ARCHIVE );
916
917 sv_banFile = Cvar_Get("sv_banFile", "serverbans.dat", CVAR_ARCHIVE);
918
919 sv_onlyVisibleClients = Cvar_Get( "sv_onlyVisibleClients", "0", 0 ); // DHM - Nerve
920
921 sv_forceNameUniq = Cvar_Get( "sv_forceNameUniq", "0", CVAR_ARCHIVE );
922
923 sv_showAverageBPS = Cvar_Get( "sv_showAverageBPS", "0", 0 ); // NERVE - SMF - net debugging
924
925 // NERVE - SMF - create user set cvars
926 Cvar_Get( "g_userTimeLimit", "0", 0 );
927 Cvar_Get( "g_userAlliedRespawnTime", "0", 0 );
928 Cvar_Get( "g_userAxisRespawnTime", "0", 0 );
929 Cvar_Get( "g_maxlives", "0", 0 );
930 Cvar_Get( "g_noTeamSwitching", "0", CVAR_ARCHIVE );
931 Cvar_Get( "g_altStopwatchMode", "0", CVAR_ARCHIVE );
932 Cvar_Get( "g_minGameClients", "8", CVAR_SERVERINFO );
933 Cvar_Get( "g_complaintlimit", "3", CVAR_ARCHIVE );
934 Cvar_Get( "gamestate", "-1", CVAR_WOLFINFO | CVAR_ROM );
935 Cvar_Get( "g_currentRound", "0", CVAR_WOLFINFO );
936 Cvar_Get( "g_nextTimeLimit", "0", CVAR_WOLFINFO );
937 // -NERVE - SMF
938
939 // TTimo - some UI additions
940 // NOTE: sucks to have this hardcoded really, I suppose this should be in UI
941 Cvar_Get( "g_axismaxlives", "0", 0 );
942 Cvar_Get( "g_alliedmaxlives", "0", 0 );
943 Cvar_Get( "g_fastres", "0", CVAR_ARCHIVE );
944 Cvar_Get( "g_fastResMsec", "1000", CVAR_ARCHIVE );
945
946 // ATVI Tracker Wolfenstein Misc #273
947 Cvar_Get( "g_voteFlags", "255", CVAR_ARCHIVE | CVAR_SERVERINFO );
948
949 // ATVI Tracker Wolfenstein Misc #263
950 Cvar_Get( "g_antilag", "0", CVAR_ARCHIVE | CVAR_SERVERINFO );
951
952 // TTimo - autodownload speed tweaks
953 #ifndef UPDATE_SERVER
954 // the download netcode tops at 18/20 kb/s, no need to make you think you can go above
955 sv_dl_maxRate = Cvar_Get( "sv_dl_maxRate", "42000", CVAR_ARCHIVE );
956 #else
957 // the update server is on steroids, sv_fps 60 and no snapshotMsec limitation, it can go up to 30 kb/s
958 sv_dl_maxRate = Cvar_Get( "sv_dl_maxRate", "60000", CVAR_ARCHIVE );
959 #endif
960
961 // initialize bot cvars so they are listed and can be set before loading the botlib
962 SV_BotInitCvars();
963
964 // init the botlib here because we need the pre-compiler in the UI
965 SV_BotInitBotLib();
966
967 // DHM - Nerve
968 #ifdef UPDATE_SERVER
969 SV_Startup();
970 SV_ParseVersionMapping();
971
972 // serverid should be different each time
973 sv.serverId = com_frameTime + 100;
974 sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe
975 sv.checksumFeedServerId = sv.serverId;
976 Cvar_Set( "sv_serverid", va( "%i", sv.serverId ) );
977 Cvar_Set( "mapname", "Update" );
978
979 // allocate empty config strings
980 {
981 int i;
982
983 for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
984 sv.configstrings[i] = CopyString( "" );
985 }
986 }
987 #endif
988
989 // Load saved bans
990 Cbuf_AddText("rehashbans\n");
991 }
992
993
994 /*
995 ==================
996 SV_FinalMessage
997
998 Used by SV_Shutdown to send a final message to all
999 connected clients before the server goes down. The messages are sent immediately,
1000 not just stuck on the outgoing message list, because the server is going
1001 to totally exit after returning from this function.
1002 ==================
1003 */
SV_FinalMessage(char * message)1004 void SV_FinalMessage( char *message ) {
1005 int i, j;
1006 client_t *cl;
1007
1008 // send it twice, ignoring rate
1009 for ( j = 0 ; j < 2 ; j++ ) {
1010 for ( i = 0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++ ) {
1011 if ( cl->state >= CS_CONNECTED ) {
1012 // don't send a disconnect to a local client
1013 if ( cl->netchan.remoteAddress.type != NA_LOOPBACK ) {
1014 SV_SendServerCommand( cl, "print \"%s\n\"\n", message );
1015 SV_SendServerCommand( cl, "disconnect \"%s\"", message );
1016 }
1017 // force a snapshot to be sent
1018 cl->lastSnapshotTime = 0;
1019 SV_SendClientSnapshot( cl );
1020 }
1021 }
1022 }
1023 }
1024
1025
1026 /*
1027 ================
1028 SV_Shutdown
1029
1030 Called when each game quits,
1031 before Sys_Quit or Sys_Error
1032 ================
1033 */
SV_Shutdown(char * finalmsg)1034 void SV_Shutdown( char *finalmsg ) {
1035 if ( !com_sv_running || !com_sv_running->integer ) {
1036 return;
1037 }
1038
1039 Com_Printf( "----- Server Shutdown (%s) -----\n", finalmsg );
1040
1041 NET_LeaveMulticast6();
1042
1043 if ( svs.clients && !com_errorEntered ) {
1044 SV_FinalMessage( finalmsg );
1045 }
1046
1047 SV_RemoveOperatorCommands();
1048 SV_MasterShutdown();
1049 SV_ShutdownGameProgs();
1050
1051 // free current level
1052 SV_ClearServer();
1053
1054 // free server static data
1055 if(svs.clients)
1056 {
1057 int index;
1058
1059 for(index = 0; index < sv_maxclients->integer; index++)
1060 SV_FreeClient(&svs.clients[index]);
1061
1062 free(svs.clients);
1063 }
1064 memset( &svs, 0, sizeof( svs ) );
1065
1066 Cvar_Set( "sv_running", "0" );
1067
1068 Com_Printf( "---------------------------\n" );
1069
1070 // disconnect any local clients
1071 if( sv_killserver->integer != 2 )
1072 CL_Disconnect( qfalse );
1073 }
1074
1075