/* =============== SV_BoundMaxClients =============== */ void SV_BoundMaxClients( int minimum ) { // get the current maxclients value #ifdef __MACOS__ Cvar_Get( "sv_maxclients", "16", 0 ); //DAJ HOG #else Cvar_Get( "sv_maxclients", "20", 0 ); // NERVE - SMF - changed to 20 from 8 #endif // START xkan, 10/03/2002 // allow many bots in single player. note that this pretty much means all previous // settings will be ignored (including the one set through "seta sv_maxclients <num>" // in user profile's wolfconfig_mp.cfg). also that if the user subsequently start // the server in multiplayer mode, the number of clients will still be the number // set here, which may be wrong - we can certainly just set it to a sensible number // when it is not in single player mode in the else part of the if statement when // necessary if ( SV_GameIsSinglePlayer() || SV_GameIsCoop() ) { Cvar_Set( "sv_maxclients", "64" ); } // END xkan, 10/03/2002 sv_maxclients->modified = qfalse; if ( sv_maxclients->integer < minimum ) { Cvar_Set( "sv_maxclients", va( "%i", minimum ) ); } else if ( sv_maxclients->integer > MAX_CLIENTS ) { Cvar_Set( "sv_maxclients", va( "%i", MAX_CLIENTS ) ); } }
/* ================ SV_TransitionGameState NERVE - SMF ================ */ static qboolean SV_TransitionGameState(gamestate_t new_gs, gamestate_t old_gs, int delay) { if (!SV_GameIsSinglePlayer() && !SV_GameIsCoop()) { // we always do a warmup before starting match if (old_gs == GS_INTERMISSION && new_gs == GS_PLAYING) { new_gs = GS_WARMUP; } } // check if its a valid state transition if (!SV_CheckTransitionGameState(new_gs, old_gs)) { return qfalse; } if (new_gs == GS_RESET) { new_gs = GS_WARMUP; } Cvar_Set("gamestate", va("%i", new_gs)); return qtrue; }
/* =============== SV_BotLibSetup =============== */ int SV_BotLibSetup(void) { static cvar_t *bot_norcd; static cvar_t *bot_frameroutingupdates; #ifdef PRE_RELEASE_DEMO return 0; #endif if(!bot_enable) { return 0; } if(!botlib_export) { Com_Printf(S_COLOR_RED "Error: SV_BotLibSetup without SV_BotInitBotLib\n"); return -1; } #if defined(USE_BOTLIB) // RF, set RCD calculation status bot_norcd = Cvar_Get("bot_norcd", "0", 0); botlib_export->BotLibVarSet("bot_norcd", bot_norcd->string); // RF, set AAS routing max per frame if(SV_GameIsSinglePlayer()) { bot_frameroutingupdates = Cvar_Get("bot_frameroutingupdates", "9999999", 0); } else { // more restrictive in multiplayer bot_frameroutingupdates = Cvar_Get("bot_frameroutingupdates", "1000", 0); } botlib_export->BotLibVarSet("bot_frameroutingupdates", bot_frameroutingupdates->string); // START Arnout changes, 28-08-2002. // added single player return botlib_export->BotLibSetup((SV_GameIsSinglePlayer() || SV_GameIsCoop())); // END Arnout changes, 28-08-2002. #endif }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. This is NOT called for map_restart ================ */ void SV_SpawnServer( char *server, qboolean killBots ) { int i; int checksum; qboolean isBot; const char *p; // shut down the existing game if it is running SV_ShutdownGameProgs(); Com_Printf( "------ Server Initialization ------\n" ); Com_Printf( "Server: %s\n", server ); // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); // make sure all the client stuff is unloaded CL_ShutdownAll(); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); // clear collision map data // (SA) NOTE: TODO: used in missionpack CM_ClearMap(); // wipe the entire per-level structure SV_ClearServer(); // MrE: main zone should be pretty much emtpy at this point // except for file system data and cached renderer data Z_LogHeap(); // allocate empty config strings for ( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { sv.configstrings[ i ] = CopyString( "" ); sv.configstringsmodified[ i ] = qfalse; } // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue( "sv_running" ) ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } #ifdef USE_HUB_SERVER // if sv_owHubHost was changed, resolve the address again if ( sv_owHubHost->modified ) { sv_owHubHost->modified = qfalse; SV_ResolveowHubHost(); } #endif } // clear pak references FS_ClearPakReferences( 0 ); // allocate the snapshot entities on the hunk svs.snapshotEntities = Hunk_Alloc( sizeof( entityState_t ) * svs.numSnapshotEntities, h_high ); svs.nextSnapshotEntities = 0; // toggle the server bit so clients can detect that a // server has changed svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // set nextmap to the same map, but it may be overriden // by the game startup or another console command Cvar_Set( "nextmap", "map_restart 0" ); // Cvar_Set( "nextmap", va("map %s", server) ); // Ridah // DHM - Nerve :: We want to use the completion bar in multiplayer as well // Arnout: just always use it // if( !SV_GameIsSinglePlayer() ) { SV_SetExpectedHunkUsage( va( "maps/%s.bsp", server ) ); // } else { // just set it to a negative number,so the cgame knows not to draw the percent bar // Cvar_Set( "com_expectedhunkusage", "-1" ); // } // make sure we are not paused Cvar_Set( "cl_paused", "0" ); #if !defined( DO_LIGHT_DEDICATED ) // get a new checksum feed and restart the file system srand( Sys_Milliseconds() ); sv.checksumFeed = ( ( ( int ) rand() << 16 ) ^ rand() ) ^ Sys_Milliseconds(); // DO_LIGHT_DEDICATED // only comment out when you need a new pure checksum string and it's associated random feed //Com_DPrintf("SV_SpawnServer checksum feed: %p\n", sv.checksumFeed); #else // DO_LIGHT_DEDICATED implementation below // we are not able to randomize the checksum feed since the feed is used as key for pure_checksum computations // files.c 1776 : pack->pure_checksum = Com_BlockChecksumKey( fs_headerLongs, 4 * fs_numHeaderLongs, LittleLong(fs_checksumFeed) ); // we request a fake randomized feed, files.c knows the answer srand( Sys_Milliseconds() ); sv.checksumFeed = FS_RandChecksumFeed(); #endif FS_Restart( sv.checksumFeed ); CM_LoadMap( va( "maps/%s.bsp", server ), qfalse, &checksum ); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va( "%i", checksum ) ); sv_newGameShlib = Cvar_Get( "sv_newGameShlib", "", CVAR_TEMP ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = sv.serverId; sv.checksumFeedServerId = sv.serverId; Cvar_Set( "sv_serverid", va( "%i", sv.serverId ) ); // clear physics interaction links SV_ClearWorld(); // media configstring setting should be done during // the loading stage, so connected clients don't have // to load during actual gameplay sv.state = SS_LOADING; Cvar_Set( "sv_serverRestarting", "1" ); // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified // Arnout: there isn't any check done against this, obsolete // sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for ( i = 0; i < GAME_INIT_FRAMES; i++ ) { VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += FRAMETIME; } // create a baseline for more efficient communications SV_CreateBaseline(); for ( i = 0; i < sv_maxclients->integer; i++ ) { // send the new gamestate to all connected clients if ( svs.clients[ i ].state >= CS_CONNECTED ) { char *denied; if ( svs.clients[ i ].netchan.remoteAddress.type == NA_BOT ) { if ( killBots || SV_GameIsSinglePlayer() || SV_GameIsCoop() ) { SV_DropClient( &svs.clients[ i ], "" ); continue; } isBot = qtrue; } else { isBot = qfalse; } // connect the client again denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) ); // firstTime = qfalse if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( &svs.clients[ i ], denied ); } else { if ( !isBot ) { // when we get the next packet from a connected client, // the new gamestate will be sent svs.clients[ i ].state = CS_CONNECTED; } else { client_t *client; sharedEntity_t *ent; client = &svs.clients[ i ]; client->state = CS_ACTIVE; ent = SV_GentityNum( i ); ent->s.number = i; client->gentity = ent; client->deltaMessage = -1; client->nextSnapshotTime = svs.time; // generate a snapshot immediately VM_Call( gvm, GAME_CLIENT_BEGIN, i ); } } } } // run another frame to allow things to look at all the players VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += FRAMETIME; if ( sv_pure->integer ) { // the server sends these to the clients so they will only // load pk3s also loaded at the server p = FS_LoadedPakChecksums(); Cvar_Set( "sv_paks", p ); if ( strlen( p ) == 0 ) { Com_Printf( "WARNING: sv_pure set but no PK3 files loaded\n" ); } p = FS_LoadedPakNames(); Cvar_Set( "sv_pakNames", p ); // if a dedicated pure server we need to touch the cgame because it could be in a // seperate pk3 file and the client will need to load the latest cgame.qvm if ( com_dedicated->integer ) { SV_TouchCGame(); } } else { Cvar_Set( "sv_paks", "" ); Cvar_Set( "sv_pakNames", "" ); } // the server sends these to the clients so they can figure // out which pk3s should be auto-downloaded // NOTE: we consider the referencedPaks as 'required for operation' // we want the server to reference the mp_bin pk3 that the client is expected to load from SV_TouchCGameDLL(); p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO | CVAR_SERVERINFO_NOUPDATE ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; // any media configstring setting now should issue a warning // and any configstring changes should be reliably transmitted // to all clients sv.state = SS_GAME; // send a heartbeat now so the master will get up to date info SV_Heartbeat_f(); Hunk_SetMark(); SV_UpdateConfigStrings(); Cvar_Set( "sv_serverRestarting", "0" ); Com_Printf( "-----------------------------------\n" ); }
void SV_UpdateConfigStrings( void ) { int len, i, index; client_t *client; int maxChunkSize = MAX_STRING_CHARS - 24; for ( index = 0; index < MAX_CONFIGSTRINGS; index++ ) { if ( !sv.configstringsmodified[ index ] ) { continue; } sv.configstringsmodified[ index ] = qfalse; // send it to all the clients if we aren't // spawning a new server if ( sv.state == SS_GAME || sv.restarting ) { // send the data to all relevent clients for ( i = 0, client = svs.clients; i < sv_maxclients->integer; i++, client++ ) { if ( client->state < CS_PRIMED ) { continue; } // do not always send server info to all clients if ( index == CS_SERVERINFO && client->gentity && ( client->gentity->r.svFlags & SVF_NOSERVERINFO ) ) { continue; } // RF, don't send to bot/AI // Gordon: Note: might want to re-enable later for bot support // RF, re-enabled // Arnout: removed hardcoded gametype // Arnout: added coop if ( ( SV_GameIsSinglePlayer() || SV_GameIsCoop() ) && client->gentity && ( client->gentity->r.svFlags & SVF_BOT ) ) { continue; } len = strlen( sv.configstrings[ index ] ); if ( len >= maxChunkSize ) { int sent = 0; int remaining = len; char *cmd; char buf[ MAX_STRING_CHARS ]; while ( remaining > 0 ) { if ( sent == 0 ) { cmd = "bcs0"; } else if ( remaining < maxChunkSize ) { cmd = "bcs2"; } else { cmd = "bcs1"; } Q_strncpyz( buf, &sv.configstrings[ index ][ sent ], maxChunkSize ); SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd, index, buf ); sent += ( maxChunkSize - 1 ); remaining -= ( maxChunkSize - 1 ); } } else { // standard cs, just send it SV_SendServerCommand( client, "cs %i \"%s\"\n", index, sv.configstrings[ index ] ); } } } } }
/* ================ SV_MapRestart_f Completely restarts a level, but doesn't send a new gamestate to the clients. This allows fair starts with variable load times. ================ */ static void SV_MapRestart_f(void) { int i, delay = 0; client_t *client; char *denied; qboolean isBot; gamestate_t new_gs, old_gs; // NERVE - SMF // make sure we aren't restarting twice in the same frame if(com_frameTime == sv.serverId) { return; } // make sure server is running if(!com_sv_running->integer) { Com_Printf("Server is not running.\n"); return; } if(Cmd_Argc() > 1) { delay = atoi(Cmd_Argv(1)); } if(delay) { sv.restartTime = svs.time + delay * 1000; SV_SetConfigstring(CS_WARMUP, va("%i", sv.restartTime)); return; } // NERVE - SMF - read in gamestate or just default to GS_PLAYING old_gs = (gamestate_t)atoi(Cvar_VariableString("gamestate")); if(SV_GameIsSinglePlayer() || SV_GameIsCoop()) { new_gs = GS_PLAYING; } else { if(Cmd_Argc() > 2) { new_gs = (gamestate_t)atoi(Cmd_Argv(2)); } else { new_gs = GS_PLAYING; } } if(!SV_TransitionGameState(new_gs, old_gs, delay)) { return; } // check for changes in variables that can't just be restarted // check for maxclients change if(sv_maxclients->modified) { char mapname[MAX_QPATH]; Com_Printf("sv_maxclients variable change -- restarting.\n"); // restart the map the slow way Q_strncpyz(mapname, Cvar_VariableString("mapname"), sizeof(mapname)); SV_SpawnServer(mapname, qfalse); return; } // Check for loading a saved game if(Cvar_VariableIntegerValue("savegame_loading")) { // open the current savegame, and find out what the time is, everything else we can ignore char savemap[MAX_QPATH], *cl_profileStr = Cvar_VariableString("cl_profile"); byte *buffer; int size, savegameTime; if(com_gameInfo.usesProfiles) { Com_sprintf(savemap, sizeof(savemap), "profiles/%s/save/current.sav", cl_profileStr); } else { Q_strncpyz(savemap, "save/current.sav", sizeof(savemap)); } size = FS_ReadFile(savemap, NULL); if(size < 0) { Com_Printf("Can't find savegame %s\n", savemap); return; } //buffer = Hunk_AllocateTempMemory(size); FS_ReadFile(savemap, (void **)&buffer); // the mapname is at the very start of the savegame file savegameTime = *(int *)(buffer + sizeof(int) + MAX_QPATH); if(savegameTime >= 0) { svs.time = savegameTime; } Hunk_FreeTempMemory(buffer); } // done. // toggle the server bit so clients can detect that a // map_restart has happened svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // generate a new serverid // TTimo - don't update restartedserverId there, otherwise we won't deal correctly with multiple map_restart sv.serverId = com_frameTime; Cvar_Set("sv_serverid", va("%i", sv.serverId)); // reset all the vm data in place without changing memory allocation // note that we do NOT set sv.state = SS_LOADING, so configstrings that // had been changed from their default values will generate broadcast updates sv.state = SS_LOADING; sv.restarting = qtrue; Cvar_Set("sv_serverRestarting", "1"); SV_RestartGameProgs(); // run a few frames to allow everything to settle for(i = 0; i < GAME_INIT_FRAMES; i++) { VM_Call(gvm, GAME_RUN_FRAME, svs.time); svs.time += FRAMETIME; } // create a baseline for more efficient communications // Gordon: meh, this wont work here as the client doesn't know it has happened // SV_CreateBaseline (); sv.state = SS_GAME; sv.restarting = qfalse; // connect and begin all the clients for(i = 0; i < sv_maxclients->integer; i++) { client = &svs.clients[i]; // send the new gamestate to all connected clients if(client->state < CS_CONNECTED) { continue; } if(client->netchan.remoteAddress.type == NA_BOT) { if(SV_GameIsSinglePlayer() || SV_GameIsCoop()) { continue; // dont carry across bots in single player } isBot = qtrue; } else { isBot = qfalse; } // add the map_restart command SV_AddServerCommand(client, "map_restart\n"); // connect the client again, without the firstTime flag denied = (char*)VM_ExplicitArgPtr(gvm, VM_Call(gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot)); if(denied) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient(client, denied); if((!SV_GameIsSinglePlayer()) || (!isBot)) { Com_Printf("SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i); // bk010125 } continue; } client->state = CS_ACTIVE; SV_ClientEnterWorld(client, &client->lastUsercmd); } // run another frame to allow things to look at all the players VM_Call(gvm, GAME_RUN_FRAME, svs.time); svs.time += FRAMETIME; Cvar_Set("sv_serverRestarting", "0"); }