1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8
9 This file is part of the OpenJK source code.
10
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24
25 #include "server.h"
26 #include "ghoul2/G2.h"
27 #include "qcommon/cm_public.h"
28 #include "qcommon/MiniHeap.h"
29 #include "qcommon/stringed_ingame.h"
30 #include "sv_gameapi.h"
31
32 /*
33 ===============
34 SV_SendConfigstring
35
36 Creates and sends the server command necessary to update the CS index for the
37 given client
38 ===============
39 */
SV_SendConfigstring(client_t * client,int index)40 static void SV_SendConfigstring(client_t *client, int index)
41 {
42 int maxChunkSize = MAX_STRING_CHARS - 24;
43 int len;
44
45 len = strlen(sv.configstrings[index]);
46
47 if( len >= maxChunkSize ) {
48 int sent = 0;
49 int remaining = len;
50 char *cmd;
51 char buf[MAX_STRING_CHARS];
52
53 while (remaining > 0 ) {
54 if ( sent == 0 ) {
55 cmd = "bcs0";
56 }
57 else if( remaining < maxChunkSize ) {
58 cmd = "bcs2";
59 }
60 else {
61 cmd = "bcs1";
62 }
63 Q_strncpyz( buf, &sv.configstrings[index][sent],
64 maxChunkSize );
65
66 SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd,
67 index, buf );
68
69 sent += (maxChunkSize - 1);
70 remaining -= (maxChunkSize - 1);
71 }
72 } else {
73 // standard cs, just send it
74 SV_SendServerCommand( client, "cs %i \"%s\"\n", index,
75 sv.configstrings[index] );
76 }
77 }
78
79 /*
80 ===============
81 SV_UpdateConfigstrings
82
83 Called when a client goes from CS_PRIMED to CS_ACTIVE. Updates all
84 Configstring indexes that have changed while the client was in CS_PRIMED
85 ===============
86 */
SV_UpdateConfigstrings(client_t * client)87 void SV_UpdateConfigstrings(client_t *client)
88 {
89 int index;
90
91 for( index = 0; index < MAX_CONFIGSTRINGS; index++ ) {
92 // if the CS hasn't changed since we went to CS_PRIMED, ignore
93 if(!client->csUpdated[index])
94 continue;
95
96 // do not always send server info to all clients
97 if ( index == CS_SERVERINFO && client->gentity &&
98 (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
99 continue;
100 }
101 SV_SendConfigstring(client, index);
102 client->csUpdated[index] = qfalse;
103 }
104 }
105
106 /*
107 ===============
108 SV_SetConfigstring
109
110 ===============
111 */
SV_SetConfigstring(int index,const char * val)112 void SV_SetConfigstring (int index, const char *val) {
113 int i;
114 client_t *client;
115
116 if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
117 Com_Error (ERR_DROP, "SV_SetConfigstring: bad index %i\n", index);
118 }
119
120 if ( !val ) {
121 val = "";
122 }
123
124 // don't bother broadcasting an update if no change
125 if ( !strcmp( val, sv.configstrings[ index ] ) ) {
126 return;
127 }
128
129 // change the string in sv
130 Z_Free( sv.configstrings[index] );
131 sv.configstrings[index] = CopyString( val );
132
133 // send it to all the clients if we aren't
134 // spawning a new server
135 if ( sv.state == SS_GAME || sv.restarting ) {
136
137 // send the data to all relevent clients
138 for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) {
139 if ( client->state < CS_ACTIVE ) {
140 if ( client->state == CS_PRIMED )
141 client->csUpdated[ index ] = qtrue;
142 continue;
143 }
144 // do not always send server info to all clients
145 if ( index == CS_SERVERINFO && client->gentity && (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
146 continue;
147 }
148
149 SV_SendConfigstring(client, index);
150 }
151 }
152 }
153
154 /*
155 ===============
156 SV_GetConfigstring
157
158 ===============
159 */
SV_GetConfigstring(int index,char * buffer,int bufferSize)160 void SV_GetConfigstring( int index, char *buffer, int bufferSize ) {
161 if ( bufferSize < 1 ) {
162 Com_Error( ERR_DROP, "SV_GetConfigstring: bufferSize == %i", bufferSize );
163 }
164 if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
165 Com_Error (ERR_DROP, "SV_GetConfigstring: bad index %i\n", index);
166 }
167 if ( !sv.configstrings[index] ) {
168 buffer[0] = 0;
169 return;
170 }
171
172 Q_strncpyz( buffer, sv.configstrings[index], bufferSize );
173 }
174
175 /*
176 ===============
177 SV_SetUserinfo
178
179 ===============
180 */
SV_SetUserinfo(int index,const char * val)181 void SV_SetUserinfo( int index, const char *val ) {
182 if ( index < 0 || index >= sv_maxclients->integer ) {
183 Com_Error (ERR_DROP, "SV_SetUserinfo: bad index %i\n", index);
184 }
185
186 if ( !val ) {
187 val = "";
188 }
189
190 Q_strncpyz( svs.clients[index].userinfo, val, sizeof( svs.clients[ index ].userinfo ) );
191 Q_strncpyz( svs.clients[index].name, Info_ValueForKey( val, "name" ), sizeof(svs.clients[index].name) );
192 }
193
194 /*
195 ===============
196 SV_GetUserinfo
197
198 ===============
199 */
SV_GetUserinfo(int index,char * buffer,int bufferSize)200 void SV_GetUserinfo( int index, char *buffer, int bufferSize ) {
201 if ( bufferSize < 1 ) {
202 Com_Error( ERR_DROP, "SV_GetUserinfo: bufferSize == %i", bufferSize );
203 }
204 if ( index < 0 || index >= sv_maxclients->integer ) {
205 Com_Error (ERR_DROP, "SV_GetUserinfo: bad index %i\n", index);
206 }
207 Q_strncpyz( buffer, svs.clients[ index ].userinfo, bufferSize );
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 SV_BoundMaxClients
240
241 ===============
242 */
SV_BoundMaxClients(int minimum)243 void SV_BoundMaxClients( int minimum ) {
244 // get the current maxclients value
245 Cvar_Get( "sv_maxclients", "8", 0 );
246
247 sv_maxclients->modified = qfalse;
248
249 if ( sv_maxclients->integer < minimum ) {
250 Cvar_Set( "sv_maxclients", va("%i", minimum) );
251 } else if ( sv_maxclients->integer > MAX_CLIENTS ) {
252 Cvar_Set( "sv_maxclients", va("%i", MAX_CLIENTS) );
253 }
254 }
255
256 /*
257 ===============
258 SV_Startup
259
260 Called when a host starts a map when it wasn't running
261 one before. Successive map or map_restart commands will
262 NOT cause this to be called, unless the game is exited to
263 the menu system first.
264 ===============
265 */
SV_Startup(void)266 void SV_Startup( void ) {
267 if ( svs.initialized ) {
268 Com_Error( ERR_FATAL, "SV_Startup: svs.initialized" );
269 }
270 SV_BoundMaxClients( 1 );
271
272 svs.clients = (struct client_s *)Z_Malloc (sizeof(client_t) * sv_maxclients->integer, TAG_CLIENTS, qtrue );
273 if ( com_dedicated->integer ) {
274 svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * MAX_SNAPSHOT_ENTITIES;
275 Cvar_Set( "r_ghoul2animsmooth", "0");
276 Cvar_Set( "r_ghoul2unsqashaftersmooth", "0");
277
278 } else {
279 // we don't need nearly as many when playing locally
280 svs.numSnapshotEntities = sv_maxclients->integer * 4 * MAX_SNAPSHOT_ENTITIES;
281 }
282 SV_ChallengeInit();
283 svs.initialized = qtrue;
284
285 // Don't respect sv_killserver unless a server is actually running
286 if ( sv_killserver->integer ) {
287 Cvar_Set( "sv_killserver", "0" );
288 }
289
290 Cvar_Set( "sv_running", "1" );
291 }
292
293 /*
294 Ghoul2 Insert Start
295 */
296
SV_InitSV(void)297 void SV_InitSV(void)
298 {
299 // clear out most of the sv struct
300 memset(&sv, 0, (sizeof(sv)));
301 sv.mLocalSubBSPIndex = -1;
302 }
303 /*
304 Ghoul2 Insert End
305 */
306
307 /*
308 ==================
309 SV_ChangeMaxClients
310 ==================
311 */
SV_ChangeMaxClients(void)312 void SV_ChangeMaxClients( void ) {
313 int oldMaxClients;
314 int i;
315 client_t *oldClients;
316 int count;
317
318 // get the highest client number in use
319 count = 0;
320 for ( i = 0 ; i < sv_maxclients->integer ; i++ ) {
321 if ( svs.clients[i].state >= CS_CONNECTED ) {
322 if (i > count)
323 count = i;
324 }
325 }
326 count++;
327
328 oldMaxClients = sv_maxclients->integer;
329 // never go below the highest client number in use
330 SV_BoundMaxClients( count );
331 // if still the same
332 if ( sv_maxclients->integer == oldMaxClients ) {
333 return;
334 }
335
336 oldClients = (struct client_s *)Hunk_AllocateTempMemory( count * sizeof(client_t) );
337 // copy the clients to hunk memory
338 for ( i = 0 ; i < count ; i++ ) {
339 if ( svs.clients[i].state >= CS_CONNECTED ) {
340 oldClients[i] = svs.clients[i];
341 }
342 else {
343 Com_Memset(&oldClients[i], 0, sizeof(client_t));
344 }
345 }
346
347 // free old clients arrays
348 Z_Free( svs.clients );
349
350 // allocate new clients
351 svs.clients = (struct client_s *)Z_Malloc ( sv_maxclients->integer * sizeof(client_t), TAG_CLIENTS, qtrue );
352 Com_Memset( svs.clients, 0, sv_maxclients->integer * sizeof(client_t) );
353
354 // copy the clients over
355 for ( i = 0 ; i < count ; i++ ) {
356 if ( oldClients[i].state >= CS_CONNECTED ) {
357 svs.clients[i] = oldClients[i];
358 }
359 }
360
361 // free the old clients on the hunk
362 Hunk_FreeTempMemory( oldClients );
363
364 // allocate new snapshot entities
365 if ( com_dedicated->integer ) {
366 svs.numSnapshotEntities = sv_maxclients->integer * PACKET_BACKUP * MAX_SNAPSHOT_ENTITIES;
367 } else {
368 // we don't need nearly as many when playing locally
369 svs.numSnapshotEntities = sv_maxclients->integer * 4 * MAX_SNAPSHOT_ENTITIES;
370 }
371 }
372
373 /*
374 ================
375 SV_ClearServer
376 ================
377 */
SV_ClearServer(void)378 void SV_ClearServer(void) {
379 int i;
380
381 for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
382 if ( sv.configstrings[i] ) {
383 Z_Free( sv.configstrings[i] );
384 }
385 }
386
387 // CM_ClearMap();
388
389 /*
390 Ghoul2 Insert Start
391 */
392
393 // nope, can't do this anymore.. sv contains entitystates with STL in them.
394 // memset (&sv, 0, sizeof(sv));
395 SV_InitSV();
396 /*
397 Ghoul2 Insert End
398 */
399 // Com_Memset (&sv, 0, sizeof(sv));
400 }
401
402 /*
403 ================
404 SV_TouchCGame
405
406 touch the cgame.vm so that a pure client can load it if it's in a seperate pk3
407 ================
408 */
SV_TouchCGame(void)409 void SV_TouchCGame(void) {
410 fileHandle_t f;
411 char filename[MAX_QPATH];
412
413 Com_sprintf( filename, sizeof(filename), "cgamex86.dll" );
414
415 FS_FOpenFileRead( filename, &f, qfalse );
416 if ( f ) {
417 FS_FCloseFile( f );
418 }
419 }
420
SV_SendMapChange(void)421 void SV_SendMapChange(void)
422 {
423 int i;
424
425 if (svs.clients)
426 {
427 for (i=0 ; i<sv_maxclients->integer ; i++)
428 {
429 if (svs.clients[i].state >= CS_CONNECTED)
430 {
431 if ( svs.clients[i].netchan.remoteAddress.type != NA_BOT ||
432 svs.clients[i].demo.demorecording )
433 {
434 SV_SendClientMapChange( &svs.clients[i] ) ;
435 }
436 }
437 }
438 }
439 }
440
441 extern void SV_SendClientGameState( client_t *client );
442 /*
443 ================
444 SV_SpawnServer
445
446 Change the server to a new map, taking all connected
447 clients along with it.
448 This is NOT called for map_restart
449 ================
450 */
SV_SpawnServer(char * server,qboolean killBots,ForceReload_e eForceReload)451 void SV_SpawnServer( char *server, qboolean killBots, ForceReload_e eForceReload ) {
452 int i;
453 int checksum;
454 qboolean isBot;
455 char systemInfo[16384];
456 const char *p;
457
458 SV_StopAutoRecordDemos();
459
460 SV_SendMapChange();
461
462 re->RegisterMedia_LevelLoadBegin(server, eForceReload);
463
464 // shut down the existing game if it is running
465 SV_ShutdownGameProgs();
466 svs.gameStarted = qfalse;
467
468 Com_Printf ("------ Server Initialization ------\n");
469 Com_Printf ("Server: %s\n",server);
470
471 /*
472 Ghoul2 Insert Start
473 */
474 // de allocate the snapshot entities
475 if (svs.snapshotEntities)
476 {
477 delete[] svs.snapshotEntities;
478 svs.snapshotEntities = NULL;
479 }
480 /*
481 Ghoul2 Insert End
482 */
483
484 SV_SendMapChange();
485
486 // if not running a dedicated server CL_MapLoading will connect the client to the server
487 // also print some status stuff
488 CL_MapLoading();
489
490 #ifndef DEDICATED
491 // make sure all the client stuff is unloaded
492 CL_ShutdownAll( qfalse );
493 #endif
494
495 CM_ClearMap();
496
497 // clear the whole hunk because we're (re)loading the server
498 Hunk_Clear();
499
500 re->InitSkins();
501 re->InitShaders(qtrue);
502
503 // init client structures and svs.numSnapshotEntities
504 if ( !Cvar_VariableValue("sv_running") ) {
505 SV_Startup();
506 } else {
507 // check for maxclients change
508 if ( sv_maxclients->modified ) {
509 SV_ChangeMaxClients();
510 }
511 }
512
513 SV_SendMapChange();
514
515 /*
516 Ghoul2 Insert Start
517 */
518 // clear out those shaders, images and Models as long as this
519 // isnt a dedicated server.
520 /*
521 if ( !com_dedicated->integer )
522 {
523 #ifndef DEDICATED
524 R_InitImages();
525
526 R_InitShaders();
527
528 R_ModelInit();
529 #endif
530 }
531 else
532 */
533 if (com_dedicated->integer)
534 {
535 re->SVModelInit();
536 }
537
538 SV_SendMapChange();
539
540 // clear pak references
541 FS_ClearPakReferences(0);
542
543 /*
544 Ghoul2 Insert Start
545 */
546 // allocate the snapshot entities on the hunk
547 // svs.snapshotEntities = (struct entityState_s *)Hunk_Alloc( sizeof(entityState_t)*svs.numSnapshotEntities, h_high );
548 svs.nextSnapshotEntities = 0;
549
550 // allocate the snapshot entities
551 svs.snapshotEntities = new entityState_s[svs.numSnapshotEntities];
552 // we CAN afford to do this here, since we know the STL vectors in Ghoul2 are empty
553 memset(svs.snapshotEntities, 0, sizeof(entityState_t)*svs.numSnapshotEntities);
554
555 /*
556 Ghoul2 Insert End
557 */
558
559 // toggle the server bit so clients can detect that a
560 // server has changed
561 svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;
562
563 // set nextmap to the same map, but it may be overriden
564 // by the game startup or another console command
565 Cvar_Set( "nextmap", "map_restart 0");
566 // Cvar_Set( "nextmap", va("map %s", server) );
567
568 for (i=0 ; i<sv_maxclients->integer ; i++) {
569 // save when the server started for each client already connected
570 if (svs.clients[i].state >= CS_CONNECTED) {
571 svs.clients[i].oldServerTime = svs.time;
572 }
573 }
574
575 // wipe the entire per-level structure
576 SV_ClearServer();
577 for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
578 sv.configstrings[i] = CopyString("");
579 }
580
581 //rww - RAGDOLL_BEGIN
582 re->G2API_SetTime(sv.time,0);
583 //rww - RAGDOLL_END
584
585 // make sure we are not paused
586 Cvar_Set("cl_paused", "0");
587
588 // get a new checksum feed and restart the file system
589 srand(Com_Milliseconds());
590 sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds();
591 FS_Restart( sv.checksumFeed );
592
593 CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum );
594
595 SV_SendMapChange();
596
597 // set serverinfo visible name
598 Cvar_Set( "mapname", server );
599
600 Cvar_Set( "sv_mapChecksum", va("%i",checksum) );
601
602 // serverid should be different each time
603 sv.serverId = com_frameTime;
604 sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe
605 Cvar_Set( "sv_serverid", va("%i", sv.serverId ) );
606
607 time( &sv.realMapTimeStarted );
608 sv.demosPruned = qfalse;
609
610 // clear physics interaction links
611 SV_ClearWorld ();
612
613 // media configstring setting should be done during
614 // the loading stage, so connected clients don't have
615 // to load during actual gameplay
616 sv.state = SS_LOADING;
617
618 // load and spawn all other entities
619 SV_InitGameProgs();
620
621 // don't allow a map_restart if game is modified
622 sv_gametype->modified = qfalse;
623
624 // run a few frames to allow everything to settle
625 for ( i = 0 ;i < 3 ; i++ ) {
626 //rww - RAGDOLL_BEGIN
627 re->G2API_SetTime(sv.time,0);
628 //rww - RAGDOLL_END
629 GVM_RunFrame( sv.time );
630 SV_BotFrame( sv.time );
631 sv.time += 100;
632 svs.time += 100;
633 }
634 //rww - RAGDOLL_BEGIN
635 re->G2API_SetTime(sv.time,0);
636 //rww - RAGDOLL_END
637
638 // create a baseline for more efficient communications
639 SV_CreateBaseline ();
640
641 for (i=0 ; i<sv_maxclients->integer ; i++) {
642 // send the new gamestate to all connected clients
643 if (svs.clients[i].state >= CS_CONNECTED) {
644 char *denied;
645
646 if ( svs.clients[i].netchan.remoteAddress.type == NA_BOT ) {
647 if ( killBots ) {
648 SV_DropClient( &svs.clients[i], "" );
649 continue;
650 }
651 isBot = qtrue;
652 }
653 else {
654 isBot = qfalse;
655 }
656
657 // connect the client again
658 denied = GVM_ClientConnect( i, qfalse, isBot ); // firstTime = qfalse
659 if ( denied ) {
660 // this generally shouldn't happen, because the client
661 // was connected before the level change
662 SV_DropClient( &svs.clients[i], denied );
663 } else {
664 if( !isBot ) {
665 // when we get the next packet from a connected client,
666 // the new gamestate will be sent
667 svs.clients[i].state = CS_CONNECTED;
668 }
669 else {
670 client_t *client;
671 sharedEntity_t *ent;
672
673 client = &svs.clients[i];
674 client->state = CS_ACTIVE;
675 ent = SV_GentityNum( i );
676 ent->s.number = i;
677 client->gentity = ent;
678
679 client->deltaMessage = -1;
680 client->nextSnapshotTime = svs.time; // generate a snapshot immediately
681
682 GVM_ClientBegin( i );
683 }
684 }
685 }
686 }
687
688 // run another frame to allow things to look at all the players
689 GVM_RunFrame( sv.time );
690 SV_BotFrame( sv.time );
691 sv.time += 100;
692 svs.time += 100;
693 //rww - RAGDOLL_BEGIN
694 re->G2API_SetTime(sv.time,0);
695 //rww - RAGDOLL_END
696
697 if ( sv_pure->integer ) {
698 // the server sends these to the clients so they will only
699 // load pk3s also loaded at the server
700 p = FS_LoadedPakChecksums();
701 Cvar_Set( "sv_paks", p );
702 if (strlen(p) == 0) {
703 Com_Printf( "WARNING: sv_pure set but no PK3 files loaded\n" );
704 }
705 p = FS_LoadedPakNames();
706 Cvar_Set( "sv_pakNames", p );
707
708 // if a dedicated pure server we need to touch the cgame because it could be in a
709 // seperate pk3 file and the client will need to load the latest cgame.qvm
710 if ( com_dedicated->integer ) {
711 SV_TouchCGame();
712 }
713 }
714 else {
715 Cvar_Set( "sv_paks", "" );
716 Cvar_Set( "sv_pakNames", "" );
717 }
718 // the server sends these to the clients so they can figure
719 // out which pk3s should be auto-downloaded
720 p = FS_ReferencedPakChecksums();
721 Cvar_Set( "sv_referencedPaks", p );
722 p = FS_ReferencedPakNames();
723 Cvar_Set( "sv_referencedPakNames", p );
724
725 // save systeminfo and serverinfo strings
726 Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) );
727 cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
728 SV_SetConfigstring( CS_SYSTEMINFO, systemInfo );
729
730 SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) );
731 cvar_modifiedFlags &= ~CVAR_SERVERINFO;
732
733 // any media configstring setting now should issue a warning
734 // and any configstring changes should be reliably transmitted
735 // to all clients
736 sv.state = SS_GAME;
737
738 // send a heartbeat now so the master will get up to date info
739 SV_Heartbeat_f();
740
741 Hunk_SetMark();
742
743 /* MrE: 2000-09-13: now called in CL_DownloadsComplete
744 // don't call when running dedicated
745 if ( !com_dedicated->integer ) {
746 // note that this is called after setting the hunk mark with Hunk_SetMark
747 CL_StartHunkUsers();
748 }
749 */
750
751 for ( client_t *client = svs.clients; client - svs.clients < sv_maxclients->integer; client++) {
752 // bots will not request gamestate, so it must be manually sent
753 // cannot do this above where it says it will because mapname is not set at that time
754 if ( client->netchan.remoteAddress.type == NA_BOT && client->demo.demorecording ) {
755 SV_SendClientGameState( client );
756 }
757 }
758
759 SV_BeginAutoRecordDemos();
760 }
761
762
763 /*
764 ===============
765 SV_Init
766
767 Only called at main exe startup, not for each game
768 ===============
769 */
770 void SV_BotInitBotLib(void);
771
772 #ifdef DEDICATED
773
774 #define G2_VERT_SPACE_SERVER_SIZE 256
775 IHeapAllocator *G2VertSpaceServer = NULL;
776 CMiniHeap IHeapAllocator_singleton(G2_VERT_SPACE_SERVER_SIZE * 1024);
777
778
779 /*
780 ================
781 CL_RefPrintf
782
783 DLL glue
784 ================
785 */
SV_RefPrintf(int print_level,const char * fmt,...)786 void QDECL SV_RefPrintf( int print_level, const char *fmt, ...) {
787 va_list argptr;
788 char msg[MAXPRINTMSG];
789
790 va_start (argptr,fmt);
791 Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
792 va_end (argptr);
793
794 if ( print_level == PRINT_ALL ) {
795 Com_Printf ("%s", msg);
796 } else if ( print_level == PRINT_WARNING ) {
797 Com_Printf (S_COLOR_YELLOW "%s", msg); // yellow
798 } else if ( print_level == PRINT_DEVELOPER ) {
799 Com_DPrintf (S_COLOR_RED "%s", msg); // red
800 }
801 }
802
803 qboolean Com_TheHunkMarkHasBeenMade(void);
804
805 //qcommon/vm.cpp
806 extern vm_t *currentVM;
807
808 //qcommon/cm_load.cpp
809 extern void *gpvCachedMapDiskImage;
810 extern qboolean gbUsingCachedMapDataRightNow;
811
GetSharedMemory(void)812 static char *GetSharedMemory( void ) { return sv.mSharedMemory; }
GetCurrentVM(void)813 static vm_t *GetCurrentVM( void ) { return currentVM; }
CM_GetCachedMapDiskImage(void)814 static void *CM_GetCachedMapDiskImage( void ) { return gpvCachedMapDiskImage; }
CM_SetCachedMapDiskImage(void * ptr)815 static void CM_SetCachedMapDiskImage( void *ptr ) { gpvCachedMapDiskImage = ptr; }
CM_SetUsingCache(qboolean usingCache)816 static void CM_SetUsingCache( qboolean usingCache ) { gbUsingCachedMapDataRightNow = usingCache; }
817
818 //server stuff D:
819 extern void SV_GetConfigstring( int index, char *buffer, int bufferSize );
820 extern void SV_SetConfigstring( int index, const char *val );
821
GetG2VertSpaceServer(void)822 static IHeapAllocator *GetG2VertSpaceServer( void ) {
823 return G2VertSpaceServer;
824 }
825
826 refexport_t *re = NULL;
827
SV_InitRef(void)828 static void SV_InitRef( void ) {
829 static refimport_t ri;
830 refexport_t *ret;
831
832 Com_Printf( "----- Initializing Renderer ----\n" );
833
834 memset( &ri, 0, sizeof( ri ) );
835
836 //set up the import table
837 ri.Printf = SV_RefPrintf;
838 ri.Error = Com_Error;
839 ri.OPrintf = Com_OPrintf;
840 ri.Milliseconds = Sys_Milliseconds2; //FIXME: unix+mac need this
841 ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory;
842 ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory;
843 ri.Hunk_Alloc = Hunk_Alloc;
844 ri.Hunk_MemoryRemaining = Hunk_MemoryRemaining;
845 ri.Z_Malloc = Z_Malloc;
846 ri.Z_Free = Z_Free;
847 ri.Z_MemSize = Z_MemSize;
848 ri.Z_MorphMallocTag = Z_MorphMallocTag;
849 ri.Cmd_ExecuteString = Cmd_ExecuteString;
850 ri.Cmd_Argc = Cmd_Argc;
851 ri.Cmd_Argv = Cmd_Argv;
852 ri.Cmd_ArgsBuffer = Cmd_ArgsBuffer;
853 ri.Cmd_AddCommand = Cmd_AddCommand;
854 ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
855 ri.Cvar_Set = Cvar_Set;
856 ri.Cvar_Get = Cvar_Get;
857 ri.Cvar_VariableStringBuffer = Cvar_VariableStringBuffer;
858 ri.Cvar_VariableString = Cvar_VariableString;
859 ri.Cvar_VariableValue = Cvar_VariableValue;
860 ri.Cvar_VariableIntegerValue = Cvar_VariableIntegerValue;
861 ri.Sys_LowPhysicalMemory = Sys_LowPhysicalMemory;
862 ri.SE_GetString = SE_GetString;
863 ri.FS_FreeFile = FS_FreeFile;
864 ri.FS_FreeFileList = FS_FreeFileList;
865 ri.FS_Read = FS_Read;
866 ri.FS_ReadFile = FS_ReadFile;
867 ri.FS_FCloseFile = FS_FCloseFile;
868 ri.FS_FOpenFileRead = FS_FOpenFileRead;
869 ri.FS_FOpenFileWrite = FS_FOpenFileWrite;
870 ri.FS_FOpenFileByMode = FS_FOpenFileByMode;
871 ri.FS_FileExists = FS_FileExists;
872 ri.FS_FileIsInPAK = FS_FileIsInPAK;
873 ri.FS_ListFiles = FS_ListFiles;
874 ri.FS_Write = FS_Write;
875 ri.FS_WriteFile = FS_WriteFile;
876 ri.CM_BoxTrace = CM_BoxTrace;
877 ri.CM_DrawDebugSurface = CM_DrawDebugSurface;
878 // ri.CM_CullWorldBox = CM_CullWorldBox;
879 // ri.CM_TerrainPatchIterate = CM_TerrainPatchIterate;
880 // ri.CM_RegisterTerrain = CM_RegisterTerrain;
881 // ri.CM_ShutdownTerrain = CM_ShutdownTerrain;
882 ri.CM_ClusterPVS = CM_ClusterPVS;
883 ri.CM_LeafArea = CM_LeafArea;
884 ri.CM_LeafCluster = CM_LeafCluster;
885 ri.CM_PointLeafnum = CM_PointLeafnum;
886 ri.CM_PointContents = CM_PointContents;
887 ri.Com_TheHunkMarkHasBeenMade = Com_TheHunkMarkHasBeenMade;
888 // ri.S_RestartMusic = S_RestartMusic;
889 // ri.SND_RegisterAudio_LevelLoadEnd = SND_RegisterAudio_LevelLoadEnd;
890 // ri.CIN_RunCinematic = CIN_RunCinematic;
891 // ri.CIN_PlayCinematic = CIN_PlayCinematic;
892 // ri.CIN_UploadCinematic = CIN_UploadCinematic;
893 // ri.CL_WriteAVIVideoFrame = CL_WriteAVIVideoFrame;
894
895 // g2 data access
896 ri.GetSharedMemory = GetSharedMemory;
897
898 // (c)g vm callbacks
899 ri.GetCurrentVM = GetCurrentVM;
900 // ri.CGVMLoaded = CGVMLoaded;
901 // ri.CGVM_RagCallback = CGVM_RagCallback;
902
903 // ugly win32 backend
904 ri.CM_GetCachedMapDiskImage = CM_GetCachedMapDiskImage;
905 ri.CM_SetCachedMapDiskImage = CM_SetCachedMapDiskImage;
906 ri.CM_SetUsingCache = CM_SetUsingCache;
907
908 //FIXME: Might have to do something about this...
909 ri.GetG2VertSpaceServer = GetG2VertSpaceServer;
910 G2VertSpaceServer = &IHeapAllocator_singleton;
911
912 ret = GetRefAPI( REF_API_VERSION, &ri );
913
914 // Com_Printf( "-------------------------------\n");
915
916 if ( !ret ) {
917 Com_Error (ERR_FATAL, "Couldn't initialize refresh" );
918 }
919
920 re = ret;
921 }
922 #endif
923
SV_Init(void)924 void SV_Init (void) {
925
926 time( &svs.startTime );
927
928 SV_AddOperatorCommands ();
929
930 // serverinfo vars
931 Cvar_Get ("dmflags", "0", CVAR_SERVERINFO);
932 Cvar_Get ("fraglimit", "20", CVAR_SERVERINFO);
933 Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
934 Cvar_Get ("capturelimit", "0", CVAR_SERVERINFO);
935
936 // Get these to establish them and to make sure they have a default before the menus decide to stomp them.
937 Cvar_Get ("g_maxHolocronCarry", "3", CVAR_SERVERINFO);
938 Cvar_Get ("g_privateDuel", "1", CVAR_SERVERINFO );
939 Cvar_Get ("g_saberLocking", "1", CVAR_SERVERINFO );
940 Cvar_Get ("g_maxForceRank", "7", CVAR_SERVERINFO );
941 Cvar_Get ("duel_fraglimit", "10", CVAR_SERVERINFO);
942 Cvar_Get ("g_forceBasedTeams", "0", CVAR_SERVERINFO);
943 Cvar_Get ("g_duelWeaponDisable", "1", CVAR_SERVERINFO);
944
945 sv_gametype = Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH, "Server gametype value" );
946 sv_needpass = Cvar_Get ("g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, "Server needs password to join" );
947 Cvar_Get ("sv_keywords", "", CVAR_SERVERINFO);
948 Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_ROM);
949 sv_mapname = Cvar_Get ("mapname", "nomap", CVAR_SERVERINFO | CVAR_ROM);
950 sv_privateClients = Cvar_Get ("sv_privateClients", "0", CVAR_SERVERINFO, "Number of reserved client slots available with password" );
951 Cvar_CheckRange( sv_privateClients, 0, MAX_CLIENTS, qtrue );
952 sv_hostname = Cvar_Get ("sv_hostname", "*Jedi*", CVAR_SERVERINFO | CVAR_ARCHIVE, "The name of the server that is displayed in the serverlist" );
953 sv_maxclients = Cvar_Get ("sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH, "Max. connected clients" );
954
955
956 //cvar_t *sv_ratePolicy; // 1-2
957 //cvar_t *sv_clientRate;
958 sv_ratePolicy = Cvar_Get( "sv_ratePolicy", "1", CVAR_ARCHIVE_ND, "Determines which policy of enforcement is used for client's \"rate\" cvar" );
959 Cvar_CheckRange(sv_ratePolicy, 1, 2, qtrue);
960 sv_clientRate = Cvar_Get( "sv_clientRate", "50000", CVAR_ARCHIVE_ND);
961 sv_minRate = Cvar_Get ("sv_minRate", "0", CVAR_ARCHIVE_ND | CVAR_SERVERINFO, "Min bandwidth rate allowed on server. Use 0 for unlimited." );
962 sv_maxRate = Cvar_Get ("sv_maxRate", "0", CVAR_ARCHIVE_ND | CVAR_SERVERINFO, "Max bandwidth rate allowed on server. Use 0 for unlimited." );
963 sv_minPing = Cvar_Get ("sv_minPing", "0", CVAR_ARCHIVE_ND | CVAR_SERVERINFO );
964 sv_maxPing = Cvar_Get ("sv_maxPing", "0", CVAR_ARCHIVE_ND | CVAR_SERVERINFO );
965 sv_floodProtect = Cvar_Get ("sv_floodProtect", "1", CVAR_ARCHIVE | CVAR_SERVERINFO, "Protect against flooding of server commands" );
966 sv_floodProtectSlow = Cvar_Get ("sv_floodProtectSlow", "1", CVAR_ARCHIVE | CVAR_SERVERINFO, "Use original method of delaying commands with flood protection" );
967 // systeminfo
968 Cvar_Get ("sv_cheats", "1", CVAR_SYSTEMINFO | CVAR_ROM, "Allow cheats on server if set to 1" );
969 sv_serverid = Cvar_Get ("sv_serverid", "0", CVAR_SYSTEMINFO | CVAR_ROM );
970 sv_pure = Cvar_Get ("sv_pure", "0", CVAR_SYSTEMINFO, "Pure server" );
971 Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
972 Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
973 Cvar_Get ("sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM );
974 Cvar_Get ("sv_referencedPakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
975
976 // server vars
977 sv_rconPassword = Cvar_Get ("rconPassword", "", CVAR_TEMP );
978 sv_privatePassword = Cvar_Get ("sv_privatePassword", "", CVAR_TEMP );
979 sv_snapsMin = Cvar_Get ("sv_snapsMin", "10", CVAR_ARCHIVE_ND ); // 1 <=> sv_snapsMax
980 sv_snapsMax = Cvar_Get ("sv_snapsMax", "40", CVAR_ARCHIVE_ND ); // sv_snapsMin <=> sv_fps
981 sv_snapsPolicy = Cvar_Get ("sv_snapsPolicy", "1", CVAR_ARCHIVE_ND, "Determines which policy of enforcement is used for client's \"snaps\" cvar");
982 Cvar_CheckRange(sv_snapsPolicy, 0, 2, qtrue);
983 sv_fps = Cvar_Get ("sv_fps", "40", CVAR_SERVERINFO, "Server frames per second" );
984 sv_timeout = Cvar_Get ("sv_timeout", "200", CVAR_TEMP );
985 sv_zombietime = Cvar_Get ("sv_zombietime", "2", CVAR_TEMP );
986 Cvar_Get ("nextmap", "", CVAR_TEMP );
987
988 sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO, "Allow clients to download mod files via UDP from the server");
989 sv_master[0] = Cvar_Get ("sv_master1", MASTER_SERVER_NAME, CVAR_PROTECTED );
990 sv_master[1] = Cvar_Get ("sv_master2", JKHUB_MASTER_SERVER_NAME, CVAR_PROTECTED);
991 for(int index = 2; index < MAX_MASTER_SERVERS; index++)
992 sv_master[index] = Cvar_Get(va("sv_master%d", index + 1), "", CVAR_ARCHIVE_ND|CVAR_PROTECTED);
993 sv_reconnectlimit = Cvar_Get ("sv_reconnectlimit", "3", 0);
994 sv_showghoultraces = Cvar_Get ("sv_showghoultraces", "0", 0);
995 sv_showloss = Cvar_Get ("sv_showloss", "0", 0);
996 sv_padPackets = Cvar_Get ("sv_padPackets", "0", 0);
997 sv_killserver = Cvar_Get ("sv_killserver", "0", 0);
998 sv_mapChecksum = Cvar_Get ("sv_mapChecksum", "", CVAR_ROM);
999 sv_lanForceRate = Cvar_Get ("sv_lanForceRate", "1", CVAR_ARCHIVE_ND );
1000
1001 sv_filterCommands = Cvar_Get( "sv_filterCommands", "1", CVAR_ARCHIVE );
1002
1003 // sv_debugserver = Cvar_Get ("sv_debugserver", "0", 0);
1004
1005 sv_autoDemo = Cvar_Get( "sv_autoDemo", "0", CVAR_ARCHIVE_ND | CVAR_SERVERINFO, "Automatically take server-side demos" );
1006 sv_autoDemoBots = Cvar_Get( "sv_autoDemoBots", "0", CVAR_ARCHIVE_ND, "Record server-side demos for bots" );
1007 sv_autoDemoMaxMaps = Cvar_Get( "sv_autoDemoMaxMaps", "0", CVAR_ARCHIVE_ND );
1008
1009 sv_legacyFixes = Cvar_Get( "sv_legacyFixes", "1", CVAR_ARCHIVE );
1010
1011 sv_banFile = Cvar_Get( "sv_banFile", "serverbans.dat", CVAR_ARCHIVE, "File to use to store bans and exceptions" );
1012
1013 sv_maxOOBRate = Cvar_Get("sv_maxOOBRate", "1000", CVAR_ARCHIVE, "Maximum rate of handling incoming server commands" );
1014 sv_maxOOBRateIP = Cvar_Get("sv_maxOOBRateIP", "1", CVAR_ARCHIVE, "Maximum rate of handling incoming server commands per IP address" );
1015 sv_autoWhitelist = Cvar_Get("sv_autoWhitelist", "1", CVAR_ARCHIVE, "Save player IPs to allow them using server during DOS attack" );
1016
1017 // initialize bot cvars so they are listed and can be set before loading the botlib
1018 SV_BotInitCvars();
1019
1020 // init the botlib here because we need the pre-compiler in the UI
1021 SV_BotInitBotLib();
1022
1023 // Load saved bans
1024 Cbuf_AddText("sv_rehashbans\n");
1025
1026 // Load IP whitelist
1027 SVC_LoadWhitelist();
1028
1029 // Only allocated once, no point in moving it around and fragmenting
1030 // create a heap for Ghoul2 to use for game side model vertex transforms used in collision detection
1031 #ifdef DEDICATED
1032 SV_InitRef();
1033 #endif
1034 }
1035
1036
1037 /*
1038 ==================
1039 SV_FinalMessage
1040
1041 Used by SV_Shutdown to send a final message to all
1042 connected clients before the server goes down. The messages are sent immediately,
1043 not just stuck on the outgoing message list, because the server is going
1044 to totally exit after returning from this function.
1045 ==================
1046 */
SV_FinalMessage(char * message)1047 void SV_FinalMessage( char *message ) {
1048 int i, j;
1049 client_t *cl;
1050
1051 // send it twice, ignoring rate
1052 for ( j = 0 ; j < 2 ; j++ ) {
1053 for (i=0, cl = svs.clients ; i < sv_maxclients->integer ; i++, cl++) {
1054 if (cl->state >= CS_CONNECTED) {
1055 // don't send a disconnect to a local client
1056 if ( cl->netchan.remoteAddress.type != NA_LOOPBACK ) {
1057 SV_SendServerCommand( cl, "print \"%s\"", message );
1058 SV_SendServerCommand( cl, "disconnect" );
1059 }
1060 // force a snapshot to be sent
1061 cl->nextSnapshotTime = -1;
1062 SV_SendClientSnapshot( cl );
1063 }
1064 }
1065 }
1066 }
1067
1068
1069 /*
1070 ================
1071 SV_Shutdown
1072
1073 Called when each game quits,
1074 before Sys_Quit or Sys_Error
1075 ================
1076 */
SV_Shutdown(char * finalmsg)1077 void SV_Shutdown( char *finalmsg )
1078 {
1079 if ( !com_sv_running || !com_sv_running->integer )
1080 {
1081 return;
1082 }
1083
1084 // Com_Printf( "----- Server Shutdown -----\n" );
1085
1086 if ( svs.clients && !com_errorEntered ) {
1087 SV_FinalMessage( finalmsg );
1088 }
1089
1090 SV_RemoveOperatorCommands();
1091 SV_MasterShutdown();
1092 SV_ChallengeShutdown();
1093 SV_ShutdownGameProgs();
1094 svs.gameStarted = qfalse;
1095 /*
1096 Ghoul2 Insert Start
1097 */
1098 // de allocate the snapshot entities
1099 if (svs.snapshotEntities)
1100 {
1101 delete[] svs.snapshotEntities;
1102 svs.snapshotEntities = NULL;
1103 }
1104
1105 // free current level
1106 SV_ClearServer();
1107 CM_ClearMap();//jfm: add a clear here since it's commented out in clearServer. This prevents crashing cmShaderTable on exit.
1108
1109 // free server static data
1110 if ( svs.clients ) {
1111 Z_Free( svs.clients );
1112 }
1113 Com_Memset( &svs, 0, sizeof( svs ) );
1114
1115 Cvar_Set( "sv_running", "0" );
1116 Cvar_Set("ui_singlePlayerActive", "0");
1117
1118 // Com_Printf( "---------------------------\n" );
1119
1120 // disconnect any local clients
1121 if( sv_killserver->integer != 2 )
1122 CL_Disconnect( qfalse );
1123 }
1124