void SV_Frame( int msec,float fractionMsec ) { int frameMsec; int startTime=0; // the menu kills the server with this cvar if ( sv_killserver->integer ) { SV_Shutdown ("Server was killed.\n"); Cvar_Set( "sv_killserver", "0" ); return; } if ( !com_sv_running->integer ) { return; } extern void SE_CheckForLanguageUpdates(void); SE_CheckForLanguageUpdates(); // will fast-return else load different language if menu changed it // allow pause if only the local client is connected if ( SV_CheckPaused() ) { return; } // go ahead and let time slip if the server really hitched badly if ( msec > 1000 ) { Com_DPrintf( "SV_Frame: Truncating msec of %i to 1000\n", msec ); msec = 1000; } // if it isn't time for the next frame, do nothing if ( sv_fps->integer < 1 ) { Cvar_Set( "sv_fps", "10" ); } frameMsec = 1000 / sv_fps->integer ; sv.timeResidual += msec; sv.timeResidualFraction+=fractionMsec; if (sv.timeResidualFraction>=1.0f) { sv.timeResidualFraction-=1.0f; if (cl_newClock&&cl_newClock->integer) { sv.timeResidual++; } } if ( sv.timeResidual < frameMsec ) { return; } // if time is about to hit the 32nd bit, restart the // level, which will force the time back to zero, rather // than checking for negative time wraparound everywhere. // 2giga-milliseconds = 23 days, so it won't be too often if ( sv.time > 0x70000000 ) { SV_Shutdown( "Restarting server due to time wrapping" ); Com_Printf("You win. if you can play this long and not die, you deserve to win.\n"); return; } // update infostrings if anything has been changed if ( cvar_modifiedFlags & CVAR_SERVERINFO ) { SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; } if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) { SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString( CVAR_SYSTEMINFO ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; } if ( com_speeds->integer ) { startTime = Sys_Milliseconds (); } // SV_BotFrame( sv.time ); // run the game simulation in chunks while ( sv.timeResidual >= frameMsec ) { sv.timeResidual -= frameMsec; sv.time += frameMsec; re.G2API_SetTime(sv.time,G2T_SV_TIME); // let everything in the world think and move ge->RunFrame( sv.time ); } if ( com_speeds->integer ) { time_game = Sys_Milliseconds () - startTime; } SG_TestSave(); // returns immediately if not active, used for fake-save-every-cycle to test (mainly) Icarus disk code // check timeouts SV_CheckTimeouts (); // update ping based on the last known frame from all clients SV_CalcPings (); // send messages back to the clients SV_SendClientMessages (); }
/* ================== Cmd_CallVote_f ================== */ __cdecl void Cmd_CallVote_f( gentity_t *ent ) { int i, activePlayers; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; char arg3[MAX_STRING_TOKENS]; char cleanName[64]; // JPW NERVE int mask = 0; mvabuf; if ( !g_allowVote->boolean ) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_VOTINGNOTENABLED\"\0", 0x65)); return; } if ( level.voteTime ) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_VOTEALLREADYINPROGRESS\"\0", 0x65)); return; } if ( ent->client->pers.voteCount >= g_voteMaxVotes->integer ) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"You have called too many votes\"\0", 0x65)); return; } if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_NOSPECTATORCALLVOTE\"\0", 0x65)); return; } // make sure it is a valid command to vote on SV_Cmd_ArgvBuffer( 1, arg1, sizeof( arg1 ) ); SV_Cmd_ArgvBuffer( 2, arg2, sizeof( arg2 ) ); SV_Cmd_ArgvBuffer( 3, arg3, sizeof( arg3 ) ); if ( strchr( arg1, ';' ) || strchr( arg2, ';' ) || strchr( arg3, ';' ) ) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_INVALIDVOTESTRING\"\0", 0x65)); return; } if ( !Q_stricmp( arg1, "map_restart" ) ) { mask = VOTEFLAGS_RESTART; } else if ( !Q_stricmp( arg1, "map_rotate" ) ) { mask = VOTEFLAGS_NEXTMAP; } else if ( !Q_stricmp( arg1, "map" ) ) { mask = VOTEFLAGS_MAP; } else if ( !Q_stricmp( arg1, "typemap" ) ) { mask = VOTEFLAGS_TYPE; } else if ( !Q_stricmp( arg1, "kick" ) ) { mask = VOTEFLAGS_KICK; } else if ( !Q_stricmp( arg1, "tempbanuser" ) ) { mask = VOTEFLAGS_KICK; } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { // NERVE - SMF mask = VOTEFLAGS_GAMETYPE; // jpw } else { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_INVALIDVOTESTRING\"\0", 0x65)); SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_VOTECOMMANDSARE\x15 map_restart, map_rotate, map <mapname>, g_gametype <gametype>, kick <player or clientnum>, typemap <gametype> <map>\"\0", 0x65)); return; } if ( !( g_voteFlags & mask ) ) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"Voting for %s is disabled on this server\"\0", 0x65, arg1 ) ); return; } // if there is still a vote to be executed if ( level.voteExecuteTime ) { level.voteExecuteTime = 0; Cbuf_AddText( va( "%s\n", level.voteString ) ); } // special case for g_gametype, check for bad values if ( !Q_stricmp( arg1, "typemap" ) ) { if(*g_voteVoteGametypes->string) { if(!strstr(g_voteVoteGametypes->string, arg2)) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"Voting for gametype %s is disabled on this server\"\0", 0x65, arg2)); return; } } for( i = 0; i < g_gametypes->numGametypes; i++) { if(!Q_stricmp( arg2, g_gametypes->gametype[i].gametypename) ) break; } if ( i == g_gametypes->numGametypes) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_INVALIDGAMETYPE\"\0", 0x65)); return; } if( !(g_voteFlags & VOTEFLAGS_ANYMAP) ) { if(!strstr(SV_GetMapRotation(), va("map %s",arg3))) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"Voting for map %s is disabled on this server\"\0", 0x65, arg3)); return; } } Com_sprintf( level.voteString, sizeof( level.voteString ), "set g_votedGametype %s; set g_votedMapName %s\n", arg2, arg3); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "Set next map to: %s and gametype to: \x14%s", arg3, g_gametypes->gametype[i].gametypereadable); } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { if(*g_voteVoteGametypes->string) { if(!strstr(g_voteVoteGametypes->string, arg2)) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"Voting for gametype %s is disabled on this server\"\0", 0x65, arg2)); return; } } for( i = 0; i < g_gametypes->numGametypes; i++) { if(!Q_stricmp( arg2, g_gametypes->gametype[i].gametypename) ) break; } if ( i == g_gametypes->numGametypes) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_INVALIDGAMETYPE\"\0", 0x65)); return; } Com_sprintf( level.voteString, sizeof( level.voteString ), "set g_gametype %s; map_restart\n", arg2); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "Set gametype to: \x15%s and restart", g_gametypes->gametype[i].gametypename); } else if ( !Q_stricmp( arg1, "map_restart" ) ) { // NERVE - SMF - do a warmup when we restart maps Com_sprintf( level.voteString, sizeof( level.voteString ), "map_restart\n"); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "Restart current game" ); } else if ( !Q_stricmp( arg1, "map" ) ) { if( !(g_voteFlags & VOTEFLAGS_ANYMAP) ) { if(!strstr(SV_GetMapRotation(), va("map %s", arg2))) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"This server does not allow voting for maps which aren't part of map-rotation\"\0", 0x65)); return; } } Com_sprintf( level.voteString, sizeof( level.voteString ), "set g_votedMapName %s\n", arg2 ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "Set next map to: %s", arg2 ); } else if ( !Q_stricmp( arg1, "map_rotate" ) ) { if(*g_votedMapName->string) { if(*g_votedGametype->string) Com_sprintf( level.voteString, sizeof( level.voteString ), "set g_gametype %s; map %s; set g_votedGametype \"\"; set g_votedMapName \"\"\n", g_votedGametype->string, g_votedMapName->string); else Com_sprintf( level.voteString, sizeof( level.voteString ), "map %s; set g_votedMapName \"\"\n", g_votedMapName->string); //Com_sprintf( s, sizeof( s ), g_votedMapName->string); } else { Com_sprintf( level.voteString, sizeof( level.voteString ), "map_rotate\n" ); //Com_sprintf( s, sizeof( s ), g_votedMapName->string); } Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "Switch to next map now"); // JPW NERVE } else if ( !Q_stricmp( arg1,"tempbanuser") || !Q_stricmp( arg1,"kick")) { int i, kicknum = MAX_CLIENTS; for ( i = 0, activePlayers = 0; i < level.maxclients; i++ ) { if ( level.clients[i].pers.connected != CON_CONNECTED || level.clients[i].sess.sessionTeam == TEAM_SPECTATOR) { continue; } activePlayers++; } if(activePlayers < g_voteKickMinPlayers->integer) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_VOTINGNOTENOUGHPLAYERS\"\0", 0x65)); return; } for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( level.clients[i].pers.connected != CON_CONNECTED ) { continue; } // strip the color crap out Q_strncpyz( cleanName, level.clients[i].pers.netname, sizeof( cleanName ) ); Q_CleanStr( cleanName ); if ( !Q_stricmp( cleanName, arg2 ) ) { kicknum = i; } } if ( kicknum != MAX_CLIENTS ) { // found a client # to kick, so override votestring with better one Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s wants to kick and tempban: %s", ent->client->pers.netname ,level.clients[kicknum].pers.netname ); if(svs.clients[kicknum].power > 5) { SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"Can not call a vote against a protected player\"\0", 0x65)); level.voteDisplayString[0] = 0; return; } else { Com_sprintf( level.voteString, sizeof( level.voteString ),"tempban \"%d\" %im Vote kick; kick \"%d\" Vote kick\n", kicknum, g_voteBanTime->integer, kicknum); } } else { // if it can't do a name match, don't allow kick (to prevent votekick text spam wars) SV_GameSendServerCommand( ent - g_entities, 0, va("%c \"GAME_CLIENTNOTONSERVER\"\0", 0x65)); level.voteDisplayString[0] = 0; return; } // jpw } else { // Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 ); // Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); } SV_GameSendServerCommand( -1, 0, va("%c \"GAME_CALLEDAVOTE\x15%s\"\0", 0x65, ent->client->pers.netname)); ent->client->pers.voteCount++; // start the voting, the caller autoamtically votes yes level.voteTime = level.time + 1000*g_voteTime->integer; level.voteYes = 1; level.voteNo = 0; for ( i = 0 ; i < level.maxclients ; i++ ) { level.clients[i].ps.eFlags &= ~EF_VOTED; } ent->client->ps.eFlags |= EF_VOTED; SV_SetConfigstring( CS_VOTE_TIME, va( "%i %i", level.voteTime, sv_serverid->integer) ); SV_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString ); SV_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteYes ) ); SV_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteNo ) ); }
/* ================ 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; client_t *client; char *denied; qboolean isBot; int delay; // 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 ( sv.restartTime ) { return; } if (Cmd_Argc() > 1 ) { delay = atoi( Cmd_Argv(1) ); } else { delay = 5; } if( delay ) { sv.restartTime = sv.time + delay * 1000; SV_SetConfigstring( CS_WARMUP, va("%i", sv.restartTime) ); return; } // check for changes in variables that can't just be restarted // check for maxclients change if ( sv_maxclients->modified || sv_gametype->modified ) { char mapname[MAX_QPATH]; Com_Printf( "variable change -- restarting.\n" ); // restart the map the slow way Q_strncpyz( mapname, Cvar_VariableString( "mapname" ), sizeof( mapname ) ); SV_SpawnServer( mapname, qfalse, eForceReload_NOTHING ); return; } SV_StopAutoRecordDemos(); // 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 ) ); time( &sv.realMapTimeStarted ); sv.demosPruned = qfalse; // if a map_restart occurs while a client is changing maps, we need // to give them the correct time so that when they finish loading // they don't violate the backwards time check in cl_cgame.c for (i=0 ; i<sv_maxclients->integer ; i++) { if (svs.clients[i].state == CS_PRIMED) { svs.clients[i].oldServerTime = sv.restartTime; } } // 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; SV_RestartGame(); // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { GVM_RunFrame( sv.time ); sv.time += 100; svs.time += 100; } 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 ) { 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 = GVM_ClientConnect( i, qfalse, isBot ); if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( client, denied ); Com_Printf( "SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i ); continue; } if(client->state == CS_ACTIVE) SV_ClientEnterWorld(client, &client->lastUsercmd); else { // If we don't reset client->lastUsercmd and are restarting during map load, // the client will hang because we'll use the last Usercmd from the previous map, // which is wrong obviously. SV_ClientEnterWorld(client, NULL); } } // run another frame to allow things to look at all the players GVM_RunFrame( sv.time ); sv.time += 100; svs.time += 100; SV_BeginAutoRecordDemos(); }
void SV_SpawnServer( char *server, qboolean killBots, ForceReload_e eForceReload ) { int i; int checksum; qboolean isBot; char systemInfo[16384]; const char *p; SV_SendMapChange(); RE_RegisterMedia_LevelLoadBegin(server, eForceReload); // shut down the existing game if it is running SV_ShutdownGameProgs(); Com_Printf ("------ Server Initialization ------\n"); Com_Printf ("Server: %s\n",server); /* Ghoul2 Insert Start */ // de allocate the snapshot entities if (svs.snapshotEntities) { delete[] svs.snapshotEntities; svs.snapshotEntities = NULL; } /* Ghoul2 Insert End */ SV_SendMapChange(); #ifdef _XBOX // disable vsync during load for speed qglDisable(GL_VSYNC); #endif // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); #ifndef DEDICATED // make sure all the client stuff is unloaded CL_ShutdownAll(); #endif CM_ClearMap(); #ifdef _XBOX R_DeleteTextures(); #endif // clear the whole hunk because we're (re)loading the server Hunk_Clear(); #ifdef _XBOX SV_ClearLastLevel(); ClientManager::ActivateClient(0); #endif R_InitSkins(); R_InitShaders(qtrue); // This was in SV_DedicatedSpawn, but it gets in the way of my memory maps: if( com_dedicated->integer ) { // Textures have been blown away - need to kill font system so it // will re-register shaders when UI re-scans menu files below: extern void R_ShutdownFonts( void ); R_ShutdownFonts(); } ClientManager::ClientActiveRelocate( !com_dedicated->integer && !ClientManager::splitScreenMode ); #if defined(_XBOX) && !defined(FINAL_BUILD) //Useful for memory debugging. Please don't delete. Comment out if //necessary. extern void Z_DisplayLevelMemory(int, int, int); extern void Z_Details_f(void); extern void Z_TagPointers(memtag_t); Z_DisplayLevelMemory(0, 0, 0); Z_TagPointers( TAG_ALL ); Z_Details_f(); #endif // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue("sv_running") ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } // Do dedicated server-specific startup if ( com_dedicated->integer ) { SV_DedicatedSpawn(server); } // Xbox - Correct various problems with broken rules settings when people // change gametype in-game, etc... SV_FixBrokenRules(); SV_SendMapChange(); /* Ghoul2 Insert Start */ // clear out those shaders, images and Models as long as this // isnt a dedicated server. /* if ( !com_dedicated->integer ) { #ifndef DEDICATED R_InitImages(); R_InitShaders(); R_ModelInit(); #endif } else */ if (com_dedicated->integer) { R_SVModelInit(); } SV_SendMapChange(); // clear pak references FS_ClearPakReferences(0); /* Ghoul2 Insert Start */ // allocate the snapshot entities on the hunk // svs.snapshotEntities = (struct entityState_s *)Hunk_Alloc( sizeof(entityState_t)*svs.numSnapshotEntities, h_high ); svs.nextSnapshotEntities = 0; // allocate the snapshot entities svs.snapshotEntities = new entityState_s[svs.numSnapshotEntities]; // we CAN afford to do this here, since we know the STL vectors in Ghoul2 are empty memset(svs.snapshotEntities, 0, sizeof(entityState_t)*svs.numSnapshotEntities); /* Ghoul2 Insert End */ // 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) ); // wipe the entire per-level structure SV_ClearServer(); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } //rww - RAGDOLL_BEGIN G2API_SetTime(svs.time,0); //rww - RAGDOLL_END // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system srand(Com_Milliseconds()); sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); FS_Restart( sv.checksumFeed ); #ifdef _XBOX CL_StartHunkUsers(); CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); // RE_LoadWorldMap(va("maps/%s.bsp", server)); // Start up voice system if it isn't running yet. (ie, if we're on syslink) if( !logged_on ) g_Voice.Initialize(); #else CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); #endif SV_SendMapChange(); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = 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; // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { //rww - RAGDOLL_BEGIN G2API_SetTime(svs.time,0); //rww - RAGDOLL_END VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += 100; } //rww - RAGDOLL_BEGIN G2API_SetTime(svs.time,0); //rww - RAGDOLL_END // 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_DropClient( &svs.clients[i], "" ); continue; } isBot = qtrue; } else { isBot = qfalse; } // connect the client again denied = (char *)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 ); SV_DropClient( &svs.clients[i], "@MENUS_LOST_CONNECTION" ); } 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 += 100; //rww - RAGDOLL_BEGIN G2API_SetTime(svs.time,0); //rww - RAGDOLL_END 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 p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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(); /* MrE: 2000-09-13: now called in CL_DownloadsComplete // don't call when running dedicated if ( !com_dedicated->integer ) { // note that this is called after setting the hunk mark with Hunk_SetMark CL_StartHunkUsers(); } */ // Xbox - Dedicated servers need to do extra work here. Most of this is done in // cl_parse normally, but that never runs in this case: if ( com_dedicated->integer ) { // Normally, we start advertising when we get the first snapshot. // Do it now. This is also necessary so that Net_GetXNKID works below. XBL_MM_Advertise(); // We need to put ourselves into the playerlist. xbOnlineInfo.localIndex = DEDICATED_SERVER_INDEX; XBPlayerInfo *plyrInfo = &xbOnlineInfo.xbPlayerList[DEDICATED_SERVER_INDEX]; memset( plyrInfo, 0, sizeof(XBPlayerInfo) ); // We get the first refIndex plyrInfo->refIndex = svs.clientRefNum++; // Address information plyrInfo->xbAddr = *Net_GetXNADDR( NULL ); XNetXnAddrToInAddr( &plyrInfo->xbAddr, Net_GetXNKID(), &plyrInfo->inAddr ); // Gamertag and XUID Q_strncpyz( plyrInfo->name, Cvar_VariableString("name"), sizeof(plyrInfo->name) ); XONLINE_USER *pUser; if (logged_on && (pUser = &XBLLoggedOnUsers[ IN_GetMainController() ]) && (pUser->hr == S_OK)) plyrInfo->xuid = pUser->xuid; else plyrInfo->xuid.qwUserID = plyrInfo->refIndex; plyrInfo->isActive = true; // Start up the voice chat session g_Voice.JoinSession(); // And mark ourselves as playing, so that others can join our game: XBL_F_SetState( XONLINE_FRIENDSTATE_FLAG_PLAYING, true ); } }
/** * @brief 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) { int i; int checksum; qboolean isBot; const char *p; // broadcast a level change to all connected clients if (svs.clients && !com_errorEntered) { SV_FinalCommand("spawnserver", qfalse); } // 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 CM_ClearMap(); // wipe the entire per-level structure SV_ClearServer(); // 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(); } } // 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"); SV_SetExpectedHunkUsage(va("maps/%s.bsp", server)); // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system srand(Sys_Milliseconds()); sv.checksumFeed = (((int) rand() << 16) ^ rand()) ^ Sys_Milliseconds(); // 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); 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)); // 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; // load and spawn all other entities SV_InitGameProgs(); // 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 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) { 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); 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); } 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; SV_SetConfigstring(CS_WOLFINFO, Cvar_InfoString(CVAR_WOLFINFO)); cvar_modifiedFlags &= ~CVAR_WOLFINFO; // 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 if (sv_advert->integer & SVA_MASTER) { SV_Heartbeat_f(); } else // let admin's know it's disabled { Com_Printf("Not sending heartbeats to master servers - disabled by sv_advert.\n"); } Hunk_SetMark(); SV_UpdateConfigStrings(); #ifdef FEATURE_MYSQL if (Com_DB_Ready()) { Com_DB_SetMap(server); } #endif /* FEATURE_MYSQL */ Com_Printf("-----------------------------------\n"); }
/* ================ 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( const char *server ) { int i; bool isBot; // shut down the existing game if it is running SV_ShutdownGameProgs(); PrintBanner( "Server Initialization" ) Com_Printf( "Server: %s\n", server ); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); // clear collision map data CM_ClearMap(); // wipe the entire per-level structure SV_ClearServer(); // allocate empty config strings for ( i = 0; i < MAX_CONFIGSTRINGS; i++ ) { sv.configstrings[ i ] = CopyString( "" ); sv.configstringsmodified[ i ] = false; } // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue( "sv_running" ) ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } // allocate the snapshot entities on the hunk svs.snapshotEntities = ( entityState_t * ) 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 sv_nextmap to the same map, but it may be overridden // by the game startup or another console command Cvar_Set( "sv_nextmap", "map_restart 0" ); // make sure we are not paused Cvar_Set( "cl_paused", "0" ); // get a new checksum feed and restart the file system srand( Sys_Milliseconds() ); sv.checksumFeed = ( ( rand() << 16 ) ^ rand() ) ^ Sys_Milliseconds(); FS::PakPath::ClearPaks(); FS_LoadBasePak(); if (!FS_LoadPak(va("map-%s", server))) Com_Error(ERR_DROP, "Could not load map pak\n"); CM_LoadMap(server); // set serverinfo visible name Cvar_Set( "mapname", server ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = sv.serverId; Cvar_Set( "sv_serverid", va( "%i", sv.serverId ) ); // 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; // load and spawn all other entities SV_InitGameProgs(); // run a few frames to allow everything to settle for ( i = 0; i < GAME_INIT_FRAMES; i++ ) { gvm.GameRunFrame( sv.time ); svs.time += FRAMETIME; sv.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 ) { bool denied; char reason[ MAX_STRING_CHARS ]; isBot = SV_IsBot(&svs.clients[i]); // connect the client again denied = gvm.GameClientConnect( reason, sizeof( reason ), i, false, isBot ); // firstTime = false if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( &svs.clients[ i ], reason ); } 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 gvm.GameClientBegin( i ); } } } } // run another frame to allow things to look at all the players gvm.GameRunFrame( sv.time ); svs.time += FRAMETIME; sv.time += FRAMETIME; // the server sends these to the clients so they can figure // out which pk3s should be auto-downloaded Cvar_Set( "sv_paks", FS_LoadedPaks() ); // save systeminfo and serverinfo strings cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString( CVAR_SYSTEMINFO, true ) ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO, false ) ); 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(); SV_AddOperatorCommands(); Com_Printf( "-----------------------------------\n" ); }
/* ==================== SV_GameSystemCalls The module is making a system call ==================== */ intptr_t SV_GameSystemCalls(intptr_t * args) { switch (args[0]) { case G_PRINT: Com_Printf("%s", (char *)VMA(1)); return 0; case G_ERROR: Com_Error(ERR_DROP, "%s", (char *)VMA(1)); return 0; case G_MILLISECONDS: return Sys_Milliseconds(); case G_CVAR_REGISTER: Cvar_Register((vmCvar_t*)VMA(1), (char*)VMA(2), (char*)VMA(3), args[4]); return 0; case G_CVAR_UPDATE: Cvar_Update((vmCvar_t*)VMA(1)); return 0; case G_CVAR_SET: Cvar_Set((const char *)VMA(1), (const char *)VMA(2)); return 0; case G_CVAR_VARIABLE_INTEGER_VALUE: return Cvar_VariableIntegerValue((const char *)VMA(1)); case G_CVAR_VARIABLE_STRING_BUFFER: Cvar_VariableStringBuffer((char *)VMA(1), (char*)VMA(2), args[3]); return 0; case G_CVAR_LATCHEDVARIABLESTRINGBUFFER: Cvar_LatchedVariableStringBuffer((char *)VMA(1), (char*)VMA(2), args[3]); return 0; case G_ARGC: return Cmd_Argc(); case G_ARGV: Cmd_ArgvBuffer(args[1], (char*)VMA(2), args[3]); return 0; case G_SEND_CONSOLE_COMMAND: Cbuf_ExecuteText(args[1], (char *)VMA(2)); return 0; case G_FS_FOPEN_FILE: return FS_FOpenFileByMode((char *)VMA(1), (fileHandle_t*)VMA(2), (fsMode_t)args[3]); case G_FS_READ: FS_Read2(VMA(1), args[2], args[3]); return 0; case G_FS_WRITE: return FS_Write(VMA(1), args[2], args[3]); case G_FS_RENAME: FS_Rename((char *)VMA(1), (char *)VMA(2)); return 0; case G_FS_FCLOSE_FILE: FS_FCloseFile(args[1]); return 0; case G_FS_GETFILELIST: return FS_GetFileList((char *)VMA(1), (char *)VMA(2), (char*)VMA(3), args[4]); case G_LOCATE_GAME_DATA: SV_LocateGameData((sharedEntity_t*)VMA(1), args[2], args[3], (playerState_t*)VMA(4), args[5]); return 0; case G_DROP_CLIENT: SV_GameDropClient(args[1], (char*)VMA(2), args[3]); return 0; case G_SEND_SERVER_COMMAND: SV_GameSendServerCommand(args[1], (char*)VMA(2)); return 0; case G_LINKENTITY: SV_LinkEntity((sharedEntity_t*)VMA(1)); return 0; case G_UNLINKENTITY: SV_UnlinkEntity((sharedEntity_t*)VMA(1)); return 0; case G_ENTITIES_IN_BOX: return SV_AreaEntities((float*)VMA(1), (float*)VMA(2), (int*)VMA(3), args[4]); case G_ENTITY_CONTACT: return SV_EntityContact((float*)VMA(1), (float*)VMA(2), (sharedEntity_t*)VMA(3), TT_AABB); case G_ENTITY_CONTACTCAPSULE: return SV_EntityContact((float*)VMA(1), (float*)VMA(2), (sharedEntity_t*)VMA(3), TT_CAPSULE); case G_TRACE: SV_Trace((trace_t*)VMA(1), (float*)VMA(2), (float*)VMA(3), (float*)VMA(4), (float*)VMA(5), args[6], args[7], TT_AABB); return 0; case G_TRACECAPSULE: SV_Trace((trace_t*)VMA(1), (float*)VMA(2), (float*)VMA(3), (float*)VMA(4), (float*)VMA(5), args[6], args[7], TT_CAPSULE); return 0; case G_POINT_CONTENTS: return SV_PointContents((float*)VMA(1), args[2]); case G_SET_BRUSH_MODEL: SV_SetBrushModel((sharedEntity_t*)VMA(1), (char*)VMA(2)); return 0; case G_IN_PVS: return SV_inPVS((float*)VMA(1), (float*)VMA(2)); case G_IN_PVS_IGNORE_PORTALS: return SV_inPVSIgnorePortals((float*)VMA(1), (float*)VMA(2)); case G_SET_CONFIGSTRING: SV_SetConfigstring(args[1], (char*)VMA(2)); return 0; case G_GET_CONFIGSTRING: SV_GetConfigstring(args[1], (char*)VMA(2), args[3]); return 0; case G_SET_CONFIGSTRING_RESTRICTIONS: SV_SetConfigstringRestrictions( args[1], (clientList_t*)VMA(2) ); return 0; case G_SET_USERINFO: SV_SetUserinfo(args[1], (char*)VMA(2)); return 0; case G_GET_USERINFO: SV_GetUserinfo(args[1], (char*)VMA(2), args[3]); return 0; case G_GET_SERVERINFO: SV_GetServerinfo((char*)VMA(1), args[2]); return 0; case G_ADJUST_AREA_PORTAL_STATE: SV_AdjustAreaPortalState((sharedEntity_t*)VMA(1),(qboolean)args[2]); return 0; case G_AREAS_CONNECTED: return CM_AreasConnected(args[1], args[2]); case G_UPDATE_SHARED_CONFIG: SV_UpdateSharedConfig( args[1], (char*)VMA(2) ); return 0; case G_BOT_ALLOCATE_CLIENT: return SV_BotAllocateClient(args[1]); case G_BOT_FREE_CLIENT: SV_BotFreeClient(args[1]); return 0; case G_GET_USERCMD: SV_GetUsercmd(args[1], (usercmd_t*)VMA(2)); return 0; case G_GET_ENTITY_TOKEN: { const char *s; s = COM_Parse(&sv.entityParsePoint); Q_strncpyz((char*)VMA(1), s, args[2]); if(!sv.entityParsePoint && !s[0]) { return qfalse; } else { return qtrue; } } case G_DEBUG_POLYGON_CREATE: return BotImport_DebugPolygonCreate(args[1], args[2], (vec3_t*)VMA(3)); case G_DEBUG_POLYGON_DELETE: BotImport_DebugPolygonDelete(args[1]); return 0; case G_REAL_TIME: return Com_RealTime((qtime_t*)VMA(1)); case G_SNAPVECTOR: Q_SnapVector((float*)VMA(1)); return 0; case G_SEND_GAMESTAT: SV_MasterGameStat( (char*)VMA(1) ); return 0; case G_ADDCOMMAND: Cmd_AddCommand( (char*)VMA(1), NULL, (char*)VMA(3) ); return 0; case G_REMOVECOMMAND: Cmd_RemoveCommand( (char*)VMA(1) ); return 0; case G_GETTAG: return SV_GetTag(args[1], args[2], (char*)VMA(3), (orientation_t*)VMA(4)); case G_REGISTERTAG: return SV_LoadTag((char*)VMA(1)); case G_REGISTERSOUND: return S_RegisterSound((char*)VMA(1), (qboolean)args[2]); case G_GET_SOUND_LENGTH: return S_GetSoundLength(args[1]); case G_PARSE_ADD_GLOBAL_DEFINE: return Parse_AddGlobalDefine( (char*)VMA(1) ); case G_PARSE_LOAD_SOURCE: return Parse_LoadSourceHandle( (char*)VMA(1) ); case G_PARSE_FREE_SOURCE: return Parse_FreeSourceHandle( args[1] ); case G_PARSE_READ_TOKEN: return Parse_ReadTokenHandle( args[1], (pc_token_t*)VMA(2) ); case G_PARSE_SOURCE_FILE_AND_LINE: return Parse_SourceFileAndLine( args[1], (char*)VMA(2), (int*)VMA(3) ); case BOTLIB_SETUP: return SV_BotLibSetup(); case BOTLIB_SHUTDOWN: return SV_BotLibShutdown(); case BOTLIB_LIBVAR_SET: return botlib_export->BotLibVarSet((char*)VMA(1), (char*)VMA(2)); case BOTLIB_LIBVAR_GET: return botlib_export->BotLibVarGet((char*)VMA(1), (char*)VMA(2), args[3]); case BOTLIB_PC_ADD_GLOBAL_DEFINE: return Parse_AddGlobalDefine( (char*)VMA(1) ); case BOTLIB_PC_LOAD_SOURCE: return Parse_LoadSourceHandle((char*)VMA(1)); case BOTLIB_PC_FREE_SOURCE: return Parse_FreeSourceHandle(args[1]); case BOTLIB_PC_READ_TOKEN: return Parse_ReadTokenHandle(args[1], (pc_token_t*)VMA(2)); case BOTLIB_PC_SOURCE_FILE_AND_LINE: return Parse_SourceFileAndLine(args[1], (char*)VMA(2), (int*)VMA(3)); case BOTLIB_PC_UNREAD_TOKEN: Parse_UnreadLastTokenHandle(args[1]); return 0; case BOTLIB_START_FRAME: return botlib_export->BotLibStartFrame(VMF(1)); case BOTLIB_LOAD_MAP: return botlib_export->BotLibLoadMap((char*)VMA(1)); case BOTLIB_UPDATENTITY: return botlib_export->BotLibUpdateEntity(args[1], (bot_entitystate_t*)VMA(2)); case BOTLIB_TEST: return botlib_export->Test( args[1], (char*)VMA(2), (float*)VMA(3), (float*)VMA(4) ); case BOTLIB_GET_SNAPSHOT_ENTITY: return SV_BotGetSnapshotEntity(args[1], args[2]); case BOTLIB_GET_CONSOLE_MESSAGE: return SV_BotGetConsoleMessage(args[1], (char*)VMA(2), args[3]); case BOTLIB_USER_COMMAND: SV_ClientThink(&svs.clients[args[1]], (usercmd_t*)VMA(2)); return 0; case BOTLIB_AAS_ENTITY_INFO: botlib_export->aas.AAS_EntityInfo(args[1], (aas_entityinfo_s*)VMA(2)); return 0; case BOTLIB_AAS_INITIALIZED: return botlib_export->aas.AAS_Initialized(); case BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX: botlib_export->aas.AAS_PresenceTypeBoundingBox( args[1], (float*)VMA(2), (float*)VMA(3) ); return 0; case BOTLIB_AAS_TIME: return FloatAsInt(botlib_export->aas.AAS_Time()); case BOTLIB_AAS_SETCURRENTWORLD: botlib_export->aas.AAS_SetCurrentWorld(args[1]); return 0; case BOTLIB_AAS_POINT_AREA_NUM: return botlib_export->aas.AAS_PointAreaNum( (float*)VMA(1) ); case BOTLIB_AAS_TRACE_AREAS: return botlib_export->aas.AAS_TraceAreas( (float*)VMA(1), (float*)VMA(2), (int*)VMA(3), (vec3_t*)VMA(4), args[5] ); case BOTLIB_AAS_BBOX_AREAS: return botlib_export->aas.AAS_BBoxAreas( (float*)VMA(1), (float*)VMA(2), (int*)VMA(3), args[4] ); case BOTLIB_AAS_AREA_CENTER: botlib_export->aas.AAS_AreaCenter(args[1], (float*)VMA(2)); return 0; case BOTLIB_AAS_AREA_WAYPOINT: return botlib_export->aas.AAS_AreaWaypoint(args[1], (float*)VMA(2)); case BOTLIB_AAS_POINT_CONTENTS: return botlib_export->aas.AAS_PointContents((float*)VMA(1)); case BOTLIB_AAS_NEXT_BSP_ENTITY: return botlib_export->aas.AAS_NextBSPEntity(args[1]); case BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_ValueForBSPEpairKey(args[1], (char*)VMA(2), (char*)VMA(3), args[4]); case BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_VectorForBSPEpairKey(args[1], (char*)VMA(2), (float*)VMA(3)); case BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_FloatForBSPEpairKey(args[1], (char*)VMA(2), (float*)VMA(3)); case BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_IntForBSPEpairKey(args[1], (char*)VMA(2), (int*)VMA(3)); case BOTLIB_AAS_AREA_REACHABILITY: return botlib_export->aas.AAS_AreaReachability(args[1]); case BOTLIB_AAS_AREA_LADDER: return botlib_export->aas.AAS_AreaLadder(args[1]); case BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA: return botlib_export->aas.AAS_AreaTravelTimeToGoalArea(args[1], (float*)VMA(2), args[3], args[4]); case BOTLIB_AAS_SWIMMING: return botlib_export->aas.AAS_Swimming((float*)VMA(1)); case BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT: return botlib_export->aas.AAS_PredictClientMovement((aas_clientmove_s*)VMA(1), args[2], (float*)VMA(3), args[4], args[5], (float*)VMA(6), (float*)VMA(7), args[8], args[9], VMF(10), args[11], args[12], args[13]); case BOTLIB_AAS_RT_SHOWROUTE: botlib_export->aas.AAS_RT_ShowRoute((float*)VMA(1), args[2], args[3]); return 0; case BOTLIB_AAS_NEARESTHIDEAREA: return botlib_export->aas.AAS_NearestHideArea(args[1], (float*)VMA(2), args[3], args[4], (float*)VMA(5), args[6], args[7], VMF(8), (float*)VMA(9)); case BOTLIB_AAS_LISTAREASINRANGE: return botlib_export->aas.AAS_ListAreasInRange((float*)VMA(1), args[2], VMF(3), args[4], (vec3_t*)VMA(5), args[6]); case BOTLIB_AAS_AVOIDDANGERAREA: return botlib_export->aas.AAS_AvoidDangerArea((float*)VMA(1), args[2], (float*)VMA(3), args[4], VMF(5), args[6]); case BOTLIB_AAS_RETREAT: return botlib_export->aas.AAS_Retreat((int*)VMA(1), args[2], (float*)VMA(3), args[4], (float*)VMA(5), args[6], VMF(7), VMF(8), args[9]); case BOTLIB_AAS_ALTROUTEGOALS: return botlib_export->aas.AAS_AlternativeRouteGoals((float*)VMA(1), (float*)VMA(2), args[3], (aas_altroutegoal_t*)VMA(4), args[5], args[6]); case BOTLIB_AAS_SETAASBLOCKINGENTITY: botlib_export->aas.AAS_SetAASBlockingEntity((float*)VMA(1), (float*)VMA(2), args[3]); return 0; case BOTLIB_AAS_RECORDTEAMDEATHAREA: botlib_export->aas.AAS_RecordTeamDeathArea((float*)VMA(1), args[2], args[3], args[4], args[5]); return 0; case BOTLIB_EA_SAY: botlib_export->ea.EA_Say(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_SAY_TEAM: botlib_export->ea.EA_SayTeam(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_USE_ITEM: botlib_export->ea.EA_UseItem(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_DROP_ITEM: botlib_export->ea.EA_DropItem(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_USE_INV: botlib_export->ea.EA_UseInv(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_DROP_INV: botlib_export->ea.EA_DropInv(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_GESTURE: botlib_export->ea.EA_Gesture(args[1]); return 0; case BOTLIB_EA_COMMAND: botlib_export->ea.EA_Command(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_SELECT_WEAPON: botlib_export->ea.EA_SelectWeapon(args[1], args[2]); return 0; case BOTLIB_EA_TALK: botlib_export->ea.EA_Talk(args[1]); return 0; case BOTLIB_EA_ATTACK: botlib_export->ea.EA_Attack(args[1]); return 0; case BOTLIB_EA_RELOAD: botlib_export->ea.EA_Reload(args[1]); return 0; case BOTLIB_EA_USE: botlib_export->ea.EA_Use(args[1]); return 0; case BOTLIB_EA_RESPAWN: botlib_export->ea.EA_Respawn(args[1]); return 0; case BOTLIB_EA_JUMP: botlib_export->ea.EA_Jump(args[1]); return 0; case BOTLIB_EA_DELAYED_JUMP: botlib_export->ea.EA_DelayedJump(args[1]); return 0; case BOTLIB_EA_CROUCH: botlib_export->ea.EA_Crouch(args[1]); return 0; case BOTLIB_EA_WALK: botlib_export->ea.EA_Walk(args[1]); return 0; case BOTLIB_EA_MOVE_UP: botlib_export->ea.EA_MoveUp(args[1]); return 0; case BOTLIB_EA_MOVE_DOWN: botlib_export->ea.EA_MoveDown(args[1]); return 0; case BOTLIB_EA_MOVE_FORWARD: botlib_export->ea.EA_MoveForward(args[1]); return 0; case BOTLIB_EA_MOVE_BACK: botlib_export->ea.EA_MoveBack(args[1]); return 0; case BOTLIB_EA_MOVE_LEFT: botlib_export->ea.EA_MoveLeft(args[1]); return 0; case BOTLIB_EA_MOVE_RIGHT: botlib_export->ea.EA_MoveRight(args[1]); return 0; case BOTLIB_EA_MOVE: botlib_export->ea.EA_Move(args[1], (float*)VMA(2), VMF(3)); return 0; case BOTLIB_EA_VIEW: botlib_export->ea.EA_View(args[1], (float*)VMA(2)); return 0; case BOTLIB_EA_PRONE: botlib_export->ea.EA_Prone(args[1]); return 0; case BOTLIB_EA_END_REGULAR: botlib_export->ea.EA_EndRegular(args[1], VMF(2)); return 0; case BOTLIB_EA_GET_INPUT: botlib_export->ea.EA_GetInput(args[1], VMF(2), (bot_input_t*)VMA(3)); return 0; case BOTLIB_EA_RESET_INPUT: botlib_export->ea.EA_ResetInput(args[1], (bot_input_t*)VMA(2)); return 0; case BOTLIB_AI_LOAD_CHARACTER: return botlib_export->ai.BotLoadCharacter((char*)VMA(1), args[2]); case BOTLIB_AI_FREE_CHARACTER: botlib_export->ai.BotFreeCharacter(args[1]); return 0; case BOTLIB_AI_CHARACTERISTIC_FLOAT: return FloatAsInt(botlib_export->ai.Characteristic_Float(args[1], args[2])); case BOTLIB_AI_CHARACTERISTIC_BFLOAT: return FloatAsInt(botlib_export->ai.Characteristic_BFloat(args[1], args[2], VMF(3), VMF(4))); case BOTLIB_AI_CHARACTERISTIC_INTEGER: return botlib_export->ai.Characteristic_Integer(args[1], args[2]); case BOTLIB_AI_CHARACTERISTIC_BINTEGER: return botlib_export->ai.Characteristic_BInteger(args[1], args[2], args[3], args[4]); case BOTLIB_AI_CHARACTERISTIC_STRING: botlib_export->ai.Characteristic_String(args[1], args[2], (char*)VMA(3), args[4]); return 0; case BOTLIB_AI_ALLOC_CHAT_STATE: return botlib_export->ai.BotAllocChatState(); case BOTLIB_AI_FREE_CHAT_STATE: botlib_export->ai.BotFreeChatState(args[1]); return 0; case BOTLIB_AI_QUEUE_CONSOLE_MESSAGE: botlib_export->ai.BotQueueConsoleMessage(args[1], args[2], (char*)VMA(3)); return 0; case BOTLIB_AI_REMOVE_CONSOLE_MESSAGE: botlib_export->ai.BotRemoveConsoleMessage(args[1], args[2]); return 0; case BOTLIB_AI_NEXT_CONSOLE_MESSAGE: return botlib_export->ai.BotNextConsoleMessage(args[1], (bot_consolemessage_s*)VMA(2)); case BOTLIB_AI_NUM_CONSOLE_MESSAGE: return botlib_export->ai.BotNumConsoleMessages(args[1]); case BOTLIB_AI_INITIAL_CHAT: botlib_export->ai.BotInitialChat(args[1], (char*)VMA(2), args[3], (char*)VMA(4), (char*)VMA(5), (char*)VMA(6), (char*)VMA(7), (char*)VMA(8), (char*)VMA(9), (char*)VMA(10), (char*)VMA(11)); return 0; case BOTLIB_AI_NUM_INITIAL_CHATS: return botlib_export->ai.BotNumInitialChats(args[1], (char*)VMA(2)); case BOTLIB_AI_REPLY_CHAT: return botlib_export->ai.BotReplyChat(args[1], (char*)VMA(2), args[3], args[4], (char*)VMA(5), (char*)VMA(6), (char*)VMA(7), (char*)VMA(8), (char*)VMA(9), (char*)VMA(10), (char*)VMA(11), (char*)VMA(12)); case BOTLIB_AI_CHAT_LENGTH: return botlib_export->ai.BotChatLength(args[1]); case BOTLIB_AI_ENTER_CHAT: botlib_export->ai.BotEnterChat(args[1], args[2], args[3]); return 0; case BOTLIB_AI_GET_CHAT_MESSAGE: botlib_export->ai.BotGetChatMessage(args[1], (char*)VMA(2), args[3]); return 0; case BOTLIB_AI_STRING_CONTAINS: return botlib_export->ai.StringContains((char*)VMA(1), (char*)VMA(2), args[3]); case BOTLIB_AI_FIND_MATCH: return botlib_export->ai.BotFindMatch((char*)VMA(1), (bot_match_s*)VMA(2), args[3]); case BOTLIB_AI_MATCH_VARIABLE: botlib_export->ai.BotMatchVariable((bot_match_s*)VMA(1), args[2], (char*)VMA(3), args[4]); return 0; case BOTLIB_AI_UNIFY_WHITE_SPACES: botlib_export->ai.UnifyWhiteSpaces((char*)VMA(1)); return 0; case BOTLIB_AI_REPLACE_SYNONYMS: botlib_export->ai.BotReplaceSynonyms((char*)VMA(1), args[2]); return 0; case BOTLIB_AI_LOAD_CHAT_FILE: return botlib_export->ai.BotLoadChatFile(args[1], (char*)VMA(2), (char*)VMA(3)); case BOTLIB_AI_SET_CHAT_GENDER: botlib_export->ai.BotSetChatGender(args[1], args[2]); return 0; case BOTLIB_AI_SET_CHAT_NAME: botlib_export->ai.BotSetChatName(args[1], (char*)VMA(2)); return 0; case BOTLIB_AI_RESET_GOAL_STATE: botlib_export->ai.BotResetGoalState(args[1]); return 0; case BOTLIB_AI_RESET_AVOID_GOALS: botlib_export->ai.BotResetAvoidGoals(args[1]); return 0; case BOTLIB_AI_REMOVE_FROM_AVOID_GOALS: botlib_export->ai.BotRemoveFromAvoidGoals(args[1], args[2]); return 0; case BOTLIB_AI_PUSH_GOAL: botlib_export->ai.BotPushGoal(args[1], (bot_goal_s*)VMA(2)); return 0; case BOTLIB_AI_POP_GOAL: botlib_export->ai.BotPopGoal(args[1]); return 0; case BOTLIB_AI_EMPTY_GOAL_STACK: botlib_export->ai.BotEmptyGoalStack(args[1]); return 0; case BOTLIB_AI_DUMP_AVOID_GOALS: botlib_export->ai.BotDumpAvoidGoals(args[1]); return 0; case BOTLIB_AI_DUMP_GOAL_STACK: botlib_export->ai.BotDumpGoalStack(args[1]); return 0; case BOTLIB_AI_GOAL_NAME: botlib_export->ai.BotGoalName(args[1], (char*)VMA(2), args[3]); return 0; case BOTLIB_AI_GET_TOP_GOAL: return botlib_export->ai.BotGetTopGoal(args[1], (bot_goal_s*)VMA(2)); case BOTLIB_AI_GET_SECOND_GOAL: return botlib_export->ai.BotGetSecondGoal(args[1], (bot_goal_s*)VMA(2)); case BOTLIB_AI_CHOOSE_LTG_ITEM: return botlib_export->ai.BotChooseLTGItem(args[1], (float*)VMA(2), (int*)VMA(3), args[4]); case BOTLIB_AI_CHOOSE_NBG_ITEM: return botlib_export->ai.BotChooseNBGItem(args[1], (float*)VMA(2), (int*)VMA(3), args[4], (bot_goal_s*)VMA(5), VMF(6)); case BOTLIB_AI_TOUCHING_GOAL: return botlib_export->ai.BotTouchingGoal((float*)VMA(1), (bot_goal_s*)VMA(2)); case BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE: return botlib_export->ai.BotItemGoalInVisButNotVisible(args[1], (float*)VMA(2), (float*)VMA(3), (bot_goal_s*)VMA(4)); case BOTLIB_AI_GET_LEVEL_ITEM_GOAL: return botlib_export->ai.BotGetLevelItemGoal(args[1], (char*)VMA(2), (bot_goal_s*)VMA(3)); case BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL: return botlib_export->ai.BotGetNextCampSpotGoal(args[1], (bot_goal_s*)VMA(2)); case BOTLIB_AI_GET_MAP_LOCATION_GOAL: return botlib_export->ai.BotGetMapLocationGoal((char*)VMA(1), (bot_goal_s*)VMA(2)); case BOTLIB_AI_AVOID_GOAL_TIME: return FloatAsInt(botlib_export->ai.BotAvoidGoalTime(args[1], args[2])); case BOTLIB_AI_INIT_LEVEL_ITEMS: botlib_export->ai.BotInitLevelItems(); return 0; case BOTLIB_AI_UPDATE_ENTITY_ITEMS: botlib_export->ai.BotUpdateEntityItems(); return 0; case BOTLIB_AI_LOAD_ITEM_WEIGHTS: return botlib_export->ai.BotLoadItemWeights(args[1], (char*)VMA(2)); case BOTLIB_AI_FREE_ITEM_WEIGHTS: botlib_export->ai.BotFreeItemWeights(args[1]); return 0; case BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC: botlib_export->ai.BotInterbreedGoalFuzzyLogic(args[1], args[2], args[3]); return 0; case BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC: botlib_export->ai.BotSaveGoalFuzzyLogic(args[1], (char*)VMA(2)); return 0; case BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC: botlib_export->ai.BotMutateGoalFuzzyLogic(args[1], VMF(2)); return 0; case BOTLIB_AI_ALLOC_GOAL_STATE: return botlib_export->ai.BotAllocGoalState(args[1]); case BOTLIB_AI_FREE_GOAL_STATE: botlib_export->ai.BotFreeGoalState(args[1]); return 0; case BOTLIB_AI_RESET_MOVE_STATE: botlib_export->ai.BotResetMoveState(args[1]); return 0; case BOTLIB_AI_MOVE_TO_GOAL: botlib_export->ai.BotMoveToGoal((bot_moveresult_s*)VMA(1), args[2], (bot_goal_s*)VMA(3), args[4]); return 0; case BOTLIB_AI_MOVE_IN_DIRECTION: return botlib_export->ai.BotMoveInDirection(args[1], (float*)VMA(2), VMF(3), args[4]); case BOTLIB_AI_RESET_AVOID_REACH: botlib_export->ai.BotResetAvoidReach(args[1]); return 0; case BOTLIB_AI_RESET_LAST_AVOID_REACH: botlib_export->ai.BotResetLastAvoidReach(args[1]); return 0; case BOTLIB_AI_REACHABILITY_AREA: return botlib_export->ai.BotReachabilityArea((float*)VMA(1), args[2]); case BOTLIB_AI_MOVEMENT_VIEW_TARGET: return botlib_export->ai.BotMovementViewTarget(args[1], (bot_goal_s*)VMA(2), args[3], VMF(4), (float*)VMA(5)); case BOTLIB_AI_PREDICT_VISIBLE_POSITION: return botlib_export->ai.BotPredictVisiblePosition((float*)VMA(1), args[2], (bot_goal_s*)VMA(3), args[4], (vec_t*)VMA(5)); case BOTLIB_AI_ALLOC_MOVE_STATE: return botlib_export->ai.BotAllocMoveState(); case BOTLIB_AI_FREE_MOVE_STATE: botlib_export->ai.BotFreeMoveState(args[1]); return 0; case BOTLIB_AI_INIT_MOVE_STATE: botlib_export->ai.BotInitMoveState(args[1], (bot_initmove_s*)VMA(2)); return 0; case BOTLIB_AI_INIT_AVOID_REACH: botlib_export->ai.BotInitAvoidReach(args[1]); return 0; case BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON: return botlib_export->ai.BotChooseBestFightWeapon(args[1], (int*)VMA(2)); case BOTLIB_AI_GET_WEAPON_INFO: botlib_export->ai.BotGetWeaponInfo(args[1], args[2], (weaponinfo_s*)VMA(3)); return 0; case BOTLIB_AI_LOAD_WEAPON_WEIGHTS: return botlib_export->ai.BotLoadWeaponWeights(args[1], (char*)VMA(2)); case BOTLIB_AI_ALLOC_WEAPON_STATE: return botlib_export->ai.BotAllocWeaponState(); case BOTLIB_AI_FREE_WEAPON_STATE: botlib_export->ai.BotFreeWeaponState(args[1]); return 0; case BOTLIB_AI_RESET_WEAPON_STATE: botlib_export->ai.BotResetWeaponState(args[1]); return 0; case BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION: return botlib_export->ai.GeneticParentsAndChildSelection(args[1], (float*)VMA(2), (int*)VMA(3), (int*)VMA(4), (int*)VMA(5)); case G_ADD_PHYSICS_ENTITY: #ifdef USE_PHYSICS CMod_PhysicsAddEntity((sharedEntity_t*)VMA(1)); #endif return 0; case G_ADD_PHYSICS_STATIC: #ifdef USE_PHYSICS CMod_PhysicsAddStatic((sharedEntity_t*)VMA(1)); #endif return 0; case TRAP_MEMSET: memset(VMA(1), args[2], args[3]); return 0; case TRAP_MEMCPY: memcpy(VMA(1), VMA(2), args[3]); return 0; case TRAP_STRNCPY: return (intptr_t)strncpy( (char*)VMA( 1 ), (char*)VMA( 2 ), args[3] ); case TRAP_SIN: return FloatAsInt(sin(VMF(1))); case TRAP_COS: return FloatAsInt(cos(VMF(1))); case TRAP_ATAN2: return FloatAsInt(atan2(VMF(1), VMF(2))); case TRAP_SQRT: return FloatAsInt(sqrt(VMF(1))); case TRAP_MATRIXMULTIPLY: AxisMultiply((vec3_t*)VMA(1), (vec3_t*)VMA(2), (vec3_t*)VMA(3)); return 0; case TRAP_ANGLEVECTORS: AngleVectors((vec_t*)VMA(1), (vec_t*)VMA(2), (vec_t*)VMA(3), (vec_t*)VMA(4)); return 0; case TRAP_PERPENDICULARVECTOR: PerpendicularVector((vec_t*)VMA(1), (vec_t*)VMA(2)); return 0; case TRAP_FLOOR: return FloatAsInt(floor(VMF(1))); case TRAP_CEIL: return FloatAsInt(ceil(VMF(1))); case G_SENDMESSAGE: SV_SendBinaryMessage(args[1], (char*)VMA(2), args[3]); return 0; case G_MESSAGESTATUS: return SV_BinaryMessageStatus(args[1]); #if defined(ET_MYSQL) case G_SQL_RUNQUERY: return OW_RunQuery( (char*)VMA(1) ); case G_SQL_FINISHQUERY: OW_FinishQuery( args[1] ); return 0; case G_SQL_NEXTROW: return OW_NextRow( args[1] ); case G_SQL_ROWCOUNT: return OW_RowCount( args[1] ); case G_SQL_GETFIELDBYID: OW_GetFieldByID( args[1], args[2], (char*)VMA(3), args[4] ); return 0; case G_SQL_GETFIELDBYNAME: OW_GetFieldByName( args[1], (char*)VMA(2), (char*)VMA(3), args[4] ); return 0; case G_SQL_GETFIELDBYID_INT: return OW_GetFieldByID_int( args[1], args[2] ); case G_SQL_GETFIELDBYNAME_INT: return OW_GetFieldByName_int( args[1], (char*)VMA(2) ); case G_SQL_FIELDCOUNT: return OW_FieldCount( args[1] ); case G_SQL_CLEANSTRING: OW_CleanString( (char*)VMA(1), (char*)VMA(2), args[3] ); return 0; #endif case G_RSA_GENMSG: return SV_RSAGenMsg( (char*)VMA(1), (char*)VMA(2), (char*)VMA(3) ); default: Com_Error( ERR_DROP, "Bad game system trap: %ld", (long int) args[0] ); } return -1; }
/* ============ CalculateRanks Recalculates the score ranks of all players This will be called on every client connect, begin, disconnect, death, and team change. ============ */ void CalculateRanks() { theLevel.follow1_ = -1; theLevel.follow2_ = -1; theLevel.numConnectedClients_ = 0; theLevel.numNonSpectatorClients_ = 0; theLevel.numPlayingClients_ = 0; theLevel.numVotingClients_ = 0; // don't count bots for( int i = 0; i < ClientBase::TEAM_NUM_TEAMS; i++ ) theLevel.numteamVotingClients_[i] = 0; for( int i = 1 ; i <= theLevel.maxclients_ ; i++ ) { GameClient* client = theLevel.getClient(i); if( client && client->pers_.connected_ != GameClient::ClientPersistant::CON_DISCONNECTED ) { theLevel.sortedClients_[theLevel.numConnectedClients_] = i; theLevel.numConnectedClients_++; if( client->sess_.sessionTeam_ != ClientBase::TEAM_SPECTATOR ) { theLevel.numNonSpectatorClients_++; // decide if this should be auto-followed if ( client->pers_.connected_ == GameClient::ClientPersistant::CON_CONNECTED ) { theLevel.numPlayingClients_++; if( !(theLevel.getEntity(i)->r.svFlags & SVF_BOT) ) { theLevel.numVotingClients_++; if( client->sess_.sessionTeam_ == ClientBase::TEAM_RED ) theLevel.numteamVotingClients_[0]++; else if( client->sess_.sessionTeam_ == ClientBase::TEAM_BLUE ) theLevel.numteamVotingClients_[1]++; } if( theLevel.follow1_ == -1 ) theLevel.follow1_ = i; else if( theLevel.follow2_ == -1 ) theLevel.follow2_ = i; } } } } qsort( theLevel.sortedClients_, theLevel.numConnectedClients_, sizeof(theLevel.sortedClients_[0]), SortRanks ); // set the rank value for all clients that are connected and not spectators if( g_gametype.integer >= GT_TEAM ) { // in team games, rank is just the order of the teams, 0=red, 1=blue, 2=tied for ( int i = 0; i < theLevel.numConnectedClients_; i++ ) { GameClient *cl = theLevel.getClient(theLevel.sortedClients_[i]); if( theLevel.teamScores_[ClientBase::TEAM_RED] == theLevel.teamScores_[ClientBase::TEAM_BLUE] ) cl->ps_.persistant[PERS_RANK] = 2; else if( theLevel.teamScores_[ClientBase::TEAM_RED] > theLevel.teamScores_[ClientBase::TEAM_BLUE] ) cl->ps_.persistant[PERS_RANK] = 0; else cl->ps_.persistant[PERS_RANK] = 1; } } else { int rank = -1; int score = 0; for( int i = 0; i < theLevel.numPlayingClients_; i++ ) { GameClient *cl = theLevel.getClient(theLevel.sortedClients_[i]); int newScore = cl->ps_.persistant[PERS_SCORE]; if( i == 0 || newScore != score ) { rank = i; // assume we aren't tied until the next client is checked theLevel.getClient(theLevel.sortedClients_[i])->ps_.persistant[PERS_RANK] = rank; } else { // we are tied with the previous client theLevel.getClient(theLevel.sortedClients_[i-1])->ps_.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; theLevel.getClient(theLevel.sortedClients_[i])->ps_.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; } score = newScore; if( g_gametype.integer == GT_SINGLE_PLAYER && theLevel.numPlayingClients_ == 1 ) theLevel.getClient(theLevel.sortedClients_[i])->ps_.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; } } // set the CS_SCORES1/2 configstrings, which will be visible to everyone if( g_gametype.integer >= GT_TEAM ) { SV_SetConfigstring( CS_SCORES1, va("%i", theLevel.teamScores_[ClientBase::TEAM_RED] ) ); SV_SetConfigstring( CS_SCORES2, va("%i", theLevel.teamScores_[ClientBase::TEAM_BLUE] ) ); } else { if( theLevel.numConnectedClients_ == 0 ) { SV_SetConfigstring( CS_SCORES1, va("%i", SCORE_NOT_PRESENT) ); SV_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) ); } else if( theLevel.numConnectedClients_ == 1 ) { SV_SetConfigstring( CS_SCORES1, va("%i", theLevel.getClient(theLevel.sortedClients_[0])->ps_.persistant[PERS_SCORE] ) ); SV_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) ); } else { SV_SetConfigstring( CS_SCORES1, va("%i", theLevel.getClient(theLevel.sortedClients_[0])->ps_.persistant[PERS_SCORE] ) ); SV_SetConfigstring( CS_SCORES2, va("%i", theLevel.getClient(theLevel.sortedClients_[1])->ps_.persistant[PERS_SCORE] ) ); } } // see if it is time to end the level CheckExitRules(); // if we are at the intermission, send the New info to everyone if( theLevel.intermissiontime_ ) SendScoreboardMessageToAllClients(); }
static void SV_SpawnServer_after_FS_Restart( cb_context_t *context, int status ) { int i; int checksum; qboolean isBot; char systemInfo[16384]; const char *p; spawnserver_data_t *data; cb_context_t *after; char mapname[MAX_QPATH]; qboolean killBots; data = (spawnserver_data_t*)context->data; Q_strncpyz(mapname, data->mapname, MAX_QPATH); killBots = data->killBots; after = data->after; cb_free_context(context); CM_LoadMap( va("maps/%s.bsp", mapname), qfalse, &checksum ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe 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; // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for (i = 0;i < 3; i++) { VM_Call (gvm, GAME_RUN_FRAME, sv.time); SV_BotFrame (sv.time); sv.time += 100; svs.time += 100; } // 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_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->lastSnapshotTime = 0; // 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, sv.time); SV_BotFrame (sv.time); sv.time += 100; svs.time += 100; 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 p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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(); #ifndef DEDICATED if ( com_dedicated->integer ) { // restart renderer in order to show console for dedicated servers // launched through the regular binary CL_StartHunkUsers( qtrue ); } #endif Com_Printf ("-----------------------------------\n"); if (after) { cb_run(after, 0); } }
void SV_SpawnServer( char *server, qboolean killBots, ForceReload_e eForceReload ) { int i; int checksum; qboolean isBot; char systemInfo[16384]; const char *p; Com_Printf("------ Server Initialization ------\n"); Com_Printf("Server: %s\n", server); SV_SendMapChange(); RE_RegisterMedia_LevelLoadBegin(server, eForceReload); // shut down the existing game if it is running SV_ShutdownGameProgs(); FixGhoul2InfoLeaks(false,true); /* Ghoul2 Insert Start */ // de allocate the snapshot entities if (svs.snapshotEntities) { delete[] svs.snapshotEntities; svs.snapshotEntities = NULL; } /* Ghoul2 Insert End */ SV_SendMapChange(); // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); #ifndef DEDICATED // make sure all the client stuff is unloaded CL_ShutdownAll(); #endif CM_ClearMap(); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); /* Ghoul2 Insert Start */ // clear out those shaders, images and Models as long as this // isnt a dedicated server. if ( !com_dedicated->integer ) { #ifndef DEDICATED R_InitImages(); R_InitShaders(); R_ModelInit(); #endif } else { R_SVModelInit(); #ifdef G2_COLLISION_ENABLED if (!G2VertSpaceServer) { G2VertSpaceServer = new CMiniHeap(G2_VERT_SPACE_SERVER_SIZE * 1024); } #endif } SV_SendMapChange(); // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue("sv_running") ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } SV_SendMapChange(); // clear pak references FS_ClearPakReferences(0); /* Ghoul2 Insert Start */ // allocate the snapshot entities on the hunk // svs.snapshotEntities = (struct entityState_s *)Hunk_Alloc( sizeof(entityState_t)*svs.numSnapshotEntities, h_high ); svs.nextSnapshotEntities = 0; // allocate the snapshot entities svs.snapshotEntities = new entityState_s[svs.numSnapshotEntities]; // we CAN afford to do this here, since we know the STL vectors in Ghoul2 are empty memset(svs.snapshotEntities, 0, sizeof(entityState_t)*svs.numSnapshotEntities); /* Ghoul2 Insert End */ // 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) ); // wipe the entire per-level structure SV_ClearServer(); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } // decide which serverversion to host mv_serverversion = Cvar_Get("mv_serverversion", "1.04", CVAR_ARCHIVE | CVAR_LATCH | CVAR_GLOBAL); if (FS_AllPath_Base_FileExists("assets5.pk3") && (!strcmp(mv_serverversion->string, "auto") || !strcmp(mv_serverversion->string, "1.04"))) { Com_Printf("serverversion set to 1.04\n"); MV_SetCurrentGameversion(VERSION_1_04); } else if (FS_AllPath_Base_FileExists("assets2.pk3") && (!strcmp(mv_serverversion->string, "auto") || !strcmp(mv_serverversion->string, "1.03"))) { Com_Printf("serverversion set to 1.03\n"); MV_SetCurrentGameversion(VERSION_1_03); } else { Com_Printf("serverversion set to 1.02\n"); MV_SetCurrentGameversion(VERSION_1_02); } Cvar_Set("protocol", va("%i", MV_GetCurrentProtocol())); // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system srand(Com_Milliseconds()); sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); FS_PureServerSetReferencedPaks("", ""); FS_Restart( sv.checksumFeed ); CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); SV_SendMapChange(); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = 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; // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += 100; } // 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_DropClient( &svs.clients[i], "" ); continue; } isBot = qtrue; } else { isBot = qfalse; } // connect the client again denied = (char *)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 += 100; 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 p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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(); Com_Printf ("-----------------------------------\n"); /* MrE: 2000-09-13: now called in CL_DownloadsComplete // don't call when running dedicated if ( !com_dedicated->integer ) { // note that this is called after setting the hunk mark with Hunk_SetMark CL_StartHunkUsers(); } */ // shutdown webserver if (mgsrv && ((mv_httpdownloads->latchedString && !atoi(mv_httpdownloads->latchedString)) || mv_httpserverport->latchedString)) { SV_MV_Websrv_Shutdown(); } // here because latched mv_httpdownloads = Cvar_Get("mv_httpdownloads", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_LATCH); mv_httpserverport = Cvar_Get("mv_httpserverport", "0", CVAR_ARCHIVE | CVAR_LATCH); // start webserver if (mv_httpdownloads->integer) { if (Q_stristr(mv_httpserverport->string, "http://")) { Com_Printf("HTTP Downloads: redirecting to %s\n", mv_httpserverport->string); } else if (!mgsrv) { const char *err = NULL; int port; mgsrv = mg_create_server(NULL, SV_MV_Websrv_Request_ExtThread); mg_set_option(mgsrv, "document_root", Cvar_Get("fs_basepath", "", 0)->string); if (mv_httpserverport->integer) { port = mv_httpserverport->integer; err = mg_set_option(mgsrv, "listening_port", va("%i", port)); } else { for (port = HTTPSRV_STDPORT; port <= HTTPSRV_STDPORT + 15; port++) { err = mg_set_option(mgsrv, "listening_port", va("%i", port)); if (!err) { break; } } } if (!err) { sv.http_port = port; Com_Printf("HTTP Downloads: webserver running on port %i...\n", port); } else { Com_Error(ERR_DROP, "HTTP Downloads: webserver startup failed: %s", err); } mg_start_thread(SV_MV_Websrv_Loop_ExtThread, mgsrv); } } }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. ================ */ void SV_SpawnServer( const char *server, ForceReload_e eForceReload, qboolean bAllowScreenDissolve ) { int i; int checksum; re.RegisterMedia_LevelLoadBegin( server, eForceReload, bAllowScreenDissolve ); Cvar_SetValue( "cl_paused", 0 ); Cvar_Set( "timescale", "1" );//jic we were skipping // shut down the existing game if it is running SV_ShutdownGameProgs(qtrue); Com_Printf ("------ Server Initialization ------\n%s\n", com_version->string); Com_Printf ("Server: %s\n",server); // Moved up from below to help reduce fragmentation if (svs.snapshotEntities) { Z_Free(svs.snapshotEntities); svs.snapshotEntities = NULL; } // don't let sound stutter and dump all stuff on the hunk CL_MapLoading(); if (!CM_SameMap(server)) { //rww - only clear if not loading the same map CM_ClearMap(); } else if (CM_HasTerrain()) { //always clear when going between maps with terrain CM_ClearMap(); } // Miniheap never changes sizes, so I just put it really early in mem. G2VertSpaceServer->ResetHeap(); Hunk_Clear(); // wipe the entire per-level structure // Also moved up, trying to do all freeing before new allocs for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { if ( sv.configstrings[i] ) { Z_Free( sv.configstrings[i] ); sv.configstrings[i] = NULL; } } // Collect all the small allocations done by the cvar system // This frees, then allocates. Make it the last thing before other // allocations begin! Cvar_Defrag(); /* This is useful for debugging memory fragmentation. Please don't remove it. */ // init client structures and svs.numSnapshotEntities // This is moved down quite a bit, but should be safe. And keeps // svs.clients right at the beginning of memory if ( !Cvar_VariableIntegerValue("sv_running") ) { SV_Startup(); } // clear out those shaders, images and Models /*R_InitImages(); R_InitShaders(); R_ModelInit();*/ re.SVModelInit(); // allocate the snapshot entities svs.snapshotEntities = (entityState_t *) Z_Malloc (sizeof(entityState_t)*svs.numSnapshotEntities, TAG_CLIENTS, qtrue ); Music_SetLevelName(server); // 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", va("map %s", server) ); memset (&sv, 0, sizeof(sv)); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } sv.time = 1000; re.G2API_SetTime(sv.time,G2T_SV_TIME); CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum, qfalse ); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time sv.serverId = com_frameTime; 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; // load and spawn all other entities SV_InitGameProgs(); // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { ge->RunFrame( sv.time ); sv.time += 100; re.G2API_SetTime(sv.time,G2T_SV_TIME); } #ifndef JK2_MODE ge->ConnectNavs(sv_mapname->string, sv_mapChecksum->integer); #endif // create a baseline for more efficient communications SV_CreateBaseline (); for (i=0 ; i<1 ; i++) { // clear all time counters, because we have reset sv.time svs.clients[i].lastPacketTime = 0; svs.clients[i].lastConnectTime = 0; svs.clients[i].nextSnapshotTime = 0; // send the new gamestate to all connected clients if (svs.clients[i].state >= CS_CONNECTED) { char *denied; // connect the client again denied = ge->ClientConnect( i, qfalse, eNO/*qfalse*/ ); // firstTime = qfalse, qbFromSavedGame if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( &svs.clients[i], denied ); } else { svs.clients[i].state = CS_CONNECTED; // when we get the next packet from a connected client, // the new gamestate will be sent } } } // run another frame to allow things to look at all connected clients ge->RunFrame( sv.time ); sv.time += 100; re.G2API_SetTime(sv.time,G2T_SV_TIME); // save systeminfo and serverinfo strings SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString( CVAR_SYSTEMINFO ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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; Hunk_SetMark(); Z_Validate(); Z_Validate(); Z_Validate(); Com_Printf ("-----------------------------------\n"); }
/* ================ 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, bool killBots ) { int i; int checksum; bool isBot; char systemInfo[16384]; 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 CM_ClearMap(); // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue("sv_running") ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } // clear pak references FS_ClearPakReferences(0); // allocate the snapshot entities on the hunk theSVS.snapshotEntities_ = reinterpret_cast<entityState_t*>( Hunk_Alloc( sizeof(entityState_t)*theSVS.numSnapshotEntities_, h_high )); theSVS.nextSnapshotEntities_ = 0; // toggle the server bit so clients can detect that a // server has changed theSVS.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) ); // wipe the entire per-level structure SV_ClearServer(); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { theSV.configstrings_[i] = CopyString(""); } // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a New checksum feed and restart the file system srand(Com_Milliseconds()); theSV.checksumFeed_ = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); FS_Restart( theSV.checksumFeed_ ); CM_LoadMap( va("maps/%s.bsp", server), false, &checksum ); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time theSV.serverId_ = com_frameTime; theSV.restartedServerId_ = theSV.serverId_; // I suppose the init here is just to be safe theSV.checksumFeedServerId_ = theSV.serverId_; Cvar_Set( "sv_serverid", va("%i", theSV.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 theSV.state_ = Server::SS_LOADING; // load and spawn all other entities SV_InitGame(); // don't allow a map_restart if game is modified sv_gametype->modified = false; // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { //VM_Call( gvm, GAME_RUN_FRAME, svs.time ); theSG.gameRunFrame( theSVS.time_ ); theSVS.time_ += 100; } // create a baseline for more efficient communications SV_CreateBaseline (); for( i = 1 ; i <= sv_maxclients->integer ; i++ ) { SV_Client* cl = theSVS.svClients_.at(i); if( !cl ) continue; // send the New gamestate to all connected clients if( cl->state_ >= SV_Client::CS_CONNECTED ) { char *denied; if( cl->netchan_.remoteAddress.type == NA_BOT ) { if( killBots ) { SV_DropClient( cl, "" ); continue; } isBot = true; } else { isBot = false; } // connect the client again //denied = reinterpret_cast<char*>(VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, false, isBot ) )); // firstTime = false denied = theSG.clientConnect( i, false, isBot ); if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( cl, denied ); } else { if( !isBot ) { // when we get the next packet from a connected client, // the New gamestate will be sent cl->state_ = SV_Client::CS_CONNECTED; } else { EntityBase *ent; cl->state_ = SV_Client::CS_ACTIVE; //#pragma message("does the entity already exist there ??") ent = theSV.getEntityByNum( i ); ent->s.number = i; //cl->gentity_ = ent; cl->deltaMessage_ = -1; cl->nextSnapshotTime_ = theSVS.time_; // generate a snapshot immediately //VM_Call( gvm, GAME_CLIENT_BEGIN, i ); theSG.clientBegin( i ); } } } } // run another frame to allow things to look at all the players //VM_Call( gvm, GAME_RUN_FRAME, svs.time ); theSG.gameRunFrame( theSVS.time_ ); theSVS.time_ += 100; 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 p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; // any media configstring setting now should issue a warning // and any configstring changes should be reliably transmitted // to all clients theSV.state_ = Server::SS_GAME; // send a heartbeat now so the master will get up to date info SV_Heartbeat_f(); Hunk_SetMark(); Com_Printf ("-----------------------------------\n"); }
/* ================ 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; client_t *client; char *denied; qboolean isBot; int delay = 0; gamestate_t new_gs, old_gs; // 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; } // read in gamestate or just default to GS_PLAYING old_gs = atoi(Cvar_VariableString("gamestate")); if (Cmd_Argc() > 2) { new_gs = 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); return; } SV_DemoStopAll(); // toggle the server bit so clients can detect that a // map_restart has happened svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // generate a new serverid // 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; 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; } 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) { 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 = 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 (!isBot) { Com_Printf("SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i); // bk010125 } continue; } // Player won't enter the world until the download is done if (client->download == 0) { if (client->state == CS_ACTIVE) { SV_ClientEnterWorld(client, &client->lastUsercmd); } else { // If we don't reset client->lastUsercmd and are restarting during map load, // the client will hang because we'll use the last Usercmd from the previous map, // which is wrong obviously. SV_ClientEnterWorld(client, NULL); } } } // run another frame to allow things to look at all the players VM_Call(gvm, GAME_RUN_FRAME, svs.time); svs.time += FRAMETIME; // start recording a demo if (sv_autoDemo->integer) { SV_DemoAutoDemoRecord(); } }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. ================ */ void SV_SpawnServer( char *server, ForceReload_e eForceReload, qboolean bAllowScreenDissolve ) { int i; int checksum; RE_RegisterMedia_LevelLoadBegin( server, eForceReload, bAllowScreenDissolve ); Cvar_SetValue( "cl_paused", 0 ); Cvar_Set( "timescale", "1" );//jic we were skipping // shut down the existing game if it is running SV_ShutdownGameProgs(); Com_Printf ("------ Server Initialization ------\n%s\n", com_version->string); Com_Printf ("Server: %s\n",server); // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableIntegerValue("sv_running") ) { SV_Startup(); } // don't let sound stutter and dump all stuff on the hunk CL_MapLoading(); Hunk_Clear(); // clear out those shaders, images and Models R_InitImages(); R_InitShaders(); R_ModelInit(); // create a heap for Ghoul2 to use for game side model vertex transforms used in collision detection if (!G2VertSpaceServer) { static const int MiniHeapSize=128 * 1024; // maxsize of ghoul2 miniheap G2VertSpaceServer = new CMiniHeap(MiniHeapSize); } if (svs.snapshotEntities) { Z_Free(svs.snapshotEntities); } // allocate the snapshot entities svs.snapshotEntities = (entityState_t *) Z_Malloc (sizeof(entityState_t)*svs.numSnapshotEntities, TAG_CLIENTS, qtrue ); Music_SetLevelName(server); // 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", va("map %s", server) ); // wipe the entire per-level structure for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { if ( sv.configstrings[i] ) { Z_Free( sv.configstrings[i] ); } } memset (&sv, 0, sizeof(sv)); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } sv.time = 1000; G2API_SetTime(sv.time,G2T_SV_TIME); CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time sv.serverId = com_frameTime; 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; // load and spawn all other entities SV_InitGameProgs(); // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { ge->RunFrame( sv.time ); sv.time += 100; G2API_SetTime(sv.time,G2T_SV_TIME); } // create a baseline for more efficient communications SV_CreateBaseline (); for (i=0 ; i<1 ; i++) { // clear all time counters, because we have reset sv.time svs.clients[i].lastPacketTime = 0; svs.clients[i].lastConnectTime = 0; svs.clients[i].nextSnapshotTime = 0; // send the new gamestate to all connected clients if (svs.clients[i].state >= CS_CONNECTED) { char *denied; // connect the client again denied = ge->ClientConnect( i, qfalse, eNO/*qfalse*/ ); // firstTime = qfalse, qbFromSavedGame if ( denied ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropClient( &svs.clients[i], denied ); } else { svs.clients[i].state = CS_CONNECTED; // when we get the next packet from a connected client, // the new gamestate will be sent } } } // run another frame to allow things to look at all connected clients ge->RunFrame( sv.time ); sv.time += 100; G2API_SetTime(sv.time,G2T_SV_TIME); // save systeminfo and serverinfo strings SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString( CVAR_SYSTEMINFO ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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 svs.nextHeartbeatTime = -9999999; Hunk_SetMark(); Com_Printf ("-----------------------------------\n"); }
/* ================== SV_Frame Player movement occurs as a result of packet events, which happen before SV_Frame is called ================== */ void SV_Frame(int msec) { int frameMsec, startTime, frameStartTime = 0, frameEndTime; char mapname[MAX_QPATH]; // Dushan static int start, end; start = Sys_Milliseconds (); svs.stats.idle += (double)(start - end) / 1000; // the menu kills the server with this cvar if(sv_killserver->integer) { SV_Shutdown("Server was killed.\n"); Cvar_Set("sv_killserver", "0"); return; } if(!com_sv_running->integer) { // Running as a server, but no map loaded #if defined (DEDICATED) // Block until something interesting happens Sys_Sleep(-1); #endif return; } // allow pause if only the local client is connected if(SV_CheckPaused()) { return; } if(com_dedicated->integer) { frameStartTime = Sys_Milliseconds(); } // if it isn't time for the next frame, do nothing if(sv_fps->integer < 1) { Cvar_Set("sv_fps", "10"); } frameMsec = 1000 / sv_fps->integer * com_timescale->value; // don't let it scale below 1ms if(frameMsec < 1) { Cvar_Set("timescale", va("%f", sv_fps->integer / 1000.0f)); frameMsec = 1; } sv.timeResidual += msec; if(!com_dedicated->integer) { SV_BotFrame(svs.time + sv.timeResidual); } if(com_dedicated->integer && sv.timeResidual < frameMsec) { // NET_Sleep will give the OS time slices until either get a packet // or time enough for a server frame has gone by NET_Sleep(frameMsec - sv.timeResidual); return; } // if time is about to hit the 32nd bit, kick all clients // and clear sv.time, rather // than checking for negative time wraparound everywhere. // 2giga-milliseconds = 23 days, so it won't be too often if(svs.time > 0x70000000) { Q_strncpyz(mapname, sv_mapname->string, MAX_QPATH); SV_Shutdown("Restarting server due to time wrapping"); // TTimo // show_bug.cgi?id=388 // there won't be a map_restart if you have shut down the server // since it doesn't restart a non-running server // instead, re-run the current map Cbuf_AddText(va("map %s\n", mapname)); return; } // this can happen considerably earlier when lots of clients play and the map doesn't change if(svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities) { Q_strncpyz(mapname, sv_mapname->string, MAX_QPATH); SV_Shutdown("Restarting server due to numSnapshotEntities wrapping"); // TTimo see above Cbuf_AddText(va("map %s\n", mapname)); return; } if(sv.restartTime && svs.time >= sv.restartTime) { sv.restartTime = 0; Cbuf_AddText("map_restart 0\n"); return; } // update infostrings if anything has been changed if(cvar_modifiedFlags & CVAR_SERVERINFO) { SV_SetConfigstring(CS_SERVERINFO, Cvar_InfoString(CVAR_SERVERINFO | CVAR_SERVERINFO_NOUPDATE)); cvar_modifiedFlags &= ~CVAR_SERVERINFO; } if(cvar_modifiedFlags & CVAR_SERVERINFO_NOUPDATE) { SV_SetConfigstringNoUpdate(CS_SERVERINFO, Cvar_InfoString(CVAR_SERVERINFO | CVAR_SERVERINFO_NOUPDATE)); cvar_modifiedFlags &= ~CVAR_SERVERINFO_NOUPDATE; } if(cvar_modifiedFlags & CVAR_SYSTEMINFO) { SV_SetConfigstring(CS_SYSTEMINFO, Cvar_InfoString_Big(CVAR_SYSTEMINFO)); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; } // NERVE - SMF if(cvar_modifiedFlags & CVAR_WOLFINFO) { SV_SetConfigstring(CS_WOLFINFO, Cvar_InfoString(CVAR_WOLFINFO)); cvar_modifiedFlags &= ~CVAR_WOLFINFO; } if(com_speeds->integer) { startTime = Sys_Milliseconds(); } else { startTime = 0; // quite a compiler warning } // update ping based on the all received frames SV_CalcPings(); if(com_dedicated->integer) { SV_BotFrame(svs.time); } // run the game simulation in chunks while(sv.timeResidual >= frameMsec) { sv.timeResidual -= frameMsec; svs.time += frameMsec; // let everything in the world think and move #if !defined (UPDATE_SERVER) VM_Call(gvm, GAME_RUN_FRAME, svs.time); #endif #ifdef USE_PHYSICS CMod_PhysicsUpdate(svs.time, 1.0f / sv_fps->integer); #endif } if(com_speeds->integer) { time_game = Sys_Milliseconds() - startTime; } // check timeouts SV_CheckTimeouts(); // check user info buffer thingy SV_CheckClientUserinfoTimer(); // send messages back to the clients SV_SendClientMessages(); // send a heartbeat to the master if needed SV_MasterHeartbeat(HEARTBEAT_GAME); if(com_dedicated->integer) { frameEndTime = Sys_Milliseconds(); svs.totalFrameTime += (frameEndTime - frameStartTime); svs.currentFrameIndex++; //if( svs.currentFrameIndex % 50 == 0 ) // Com_Printf( "currentFrameIndex: %i\n", svs.currentFrameIndex ); if(svs.currentFrameIndex == SERVER_PERFORMANCECOUNTER_FRAMES) { int averageFrameTime; averageFrameTime = svs.totalFrameTime / SERVER_PERFORMANCECOUNTER_FRAMES; svs.sampleTimes[svs.currentSampleIndex % SERVER_PERFORMANCECOUNTER_SAMPLES] = averageFrameTime; svs.currentSampleIndex++; if(svs.currentSampleIndex > SERVER_PERFORMANCECOUNTER_SAMPLES) { int totalTime, i; totalTime = 0; for(i = 0; i < SERVER_PERFORMANCECOUNTER_SAMPLES; i++) { totalTime += svs.sampleTimes[i]; } if(!totalTime) { totalTime = 1; } averageFrameTime = totalTime / SERVER_PERFORMANCECOUNTER_SAMPLES; svs.serverLoad = (averageFrameTime / (float)frameMsec) * 100; } //Com_Printf( "serverload: %i (%i/%i)\n", svs.serverLoad, averageFrameTime, frameMsec ); svs.totalFrameTime = 0; svs.currentFrameIndex = 0; } } else { svs.serverLoad = -1; } // collect timing statistics end = Sys_Milliseconds (); svs.stats.active += ((double) (end-start)) / 1000; if (++svs.stats.count == STATFRAMES) { svs.stats.latched_active = svs.stats.active; svs.stats.latched_idle = svs.stats.idle; svs.stats.latched_packets = svs.stats.packets; svs.stats.active = 0; svs.stats.idle = 0; svs.stats.packets = 0; svs.stats.count = 0; } }
/* ============= CheckTournament Once a frame, check for changes in tournement player state ============= */ void CheckTournament() { // check because we run 3 game frames before calling Connect and/or ClientBegin // for clients on a map_restart if( theLevel.numPlayingClients_ == 0 ) return; if( g_gametype.integer == GT_TOURNAMENT ) { // pull in a spectator if needed if( theLevel.numPlayingClients_ < 2 ) AddTournamentPlayer(); // if we don't have two players, go back to "waiting for players" if( theLevel.numPlayingClients_ != 2 ) { if( theLevel.warmupTime_ != -1 ) { theLevel.warmupTime_ = -1; SV_SetConfigstring( CS_WARMUP, va("%i", theLevel.warmupTime_) ); G_LogPrintf( "Warmup:\n" ); } return; } if( theLevel.warmupTime_ == 0 ) return; // if the warmup is changed at the console, restart it if( g_warmup.modificationCount != theLevel.warmupModificationCount_ ) { theLevel.warmupModificationCount_ = g_warmup.modificationCount; theLevel.warmupTime_ = -1; } // if all players have arrived, start the countdown if( theLevel.warmupTime_ < 0 ) { if( theLevel.numPlayingClients_ == 2 ) { // fudge by -1 to account for extra delays theLevel.warmupTime_ = theLevel.time_ + ( g_warmup.integer - 1 ) * 1000; SV_SetConfigstring( CS_WARMUP, va("%i", theLevel.warmupTime_) ); } return; } // if the warmup time has counted down, restart if( theLevel.time_ > theLevel.warmupTime_ ) { theLevel.warmupTime_ += 10000; Cvar_Set( "g_restarted", "1" ); Cbuf_ExecuteText( EXEC_APPEND, "map_restart 0\n" ); theLevel.restarted_ = true; return; } } else if( g_gametype.integer != GT_SINGLE_PLAYER && g_gametype.integer != GT_MISSION_EDITOR && theLevel.warmupTime_ != 0 ) { int counts[ClientBase::TEAM_NUM_TEAMS]; bool notEnough = false; if( g_gametype.integer > GT_TEAM ) { counts[ClientBase::TEAM_BLUE] = TeamCount( -1, ClientBase::TEAM_BLUE ); counts[ClientBase::TEAM_RED] = TeamCount( -1, ClientBase::TEAM_RED ); if (counts[ClientBase::TEAM_RED] < 1 || counts[ClientBase::TEAM_BLUE] < 1) notEnough = true; } else if( theLevel.numPlayingClients_ < 2 ) notEnough = true; if( notEnough ) { if( theLevel.warmupTime_ != -1 ) { theLevel.warmupTime_ = -1; SV_SetConfigstring( CS_WARMUP, va("%i", theLevel.warmupTime_) ); G_LogPrintf( "Warmup:\n" ); } return; // still waiting for team members } if( theLevel.warmupTime_ == 0 ) return; // if the warmup is changed at the console, restart it if( g_warmup.modificationCount != theLevel.warmupModificationCount_ ) { theLevel.warmupModificationCount_ = g_warmup.modificationCount; theLevel.warmupTime_ = -1; } // if all players have arrived, start the countdown if( theLevel.warmupTime_ < 0 ) { // fudge by -1 to account for extra delays theLevel.warmupTime_ = theLevel.time_ + ( g_warmup.integer - 1 ) * 1000; SV_SetConfigstring( CS_WARMUP, va("%i", theLevel.warmupTime_) ); return; } // if the warmup time has counted down, restart if( theLevel.time_ > theLevel.warmupTime_ ) { theLevel.warmupTime_ += 10000; Cvar_Set( "g_restarted", "1" ); Cbuf_ExecuteText( EXEC_APPEND, "map_restart 0\n" ); theLevel.restarted_ = true; return; } } }
/* ================ 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"); }
/* ================== SV_Frame Player movement occurs as a result of packet events, which happen before SV_Frame is called ================== */ void SV_Frame( int msec ) { int frameMsec; int startTime; // the menu kills the server with this cvar if ( sv_killserver->integer ) { SV_Shutdown ("Server was killed.\n"); Cvar_Set( "sv_killserver", "0" ); return; } if ( !com_sv_running->integer ) { return; } // allow pause if only the local client is connected if ( SV_CheckPaused() ) { return; } // if it isn't time for the next frame, do nothing if ( sv_fps->integer < 1 ) { Cvar_Set( "sv_fps", "10" ); } frameMsec = 1000 / sv_fps->integer ; sv.timeResidual += msec; if (!com_dedicated->integer) SV_BotFrame( svs.time + sv.timeResidual ); if ( com_dedicated->integer && sv.timeResidual < frameMsec && (!com_timescale || com_timescale->value >= 1) ) { // NET_Sleep will give the OS time slices until either get a packet // or time enough for a server frame has gone by NET_Sleep(frameMsec - sv.timeResidual); return; } // if time is about to hit the 32nd bit, kick all clients // and clear sv.time, rather // than checking for negative time wraparound everywhere. // 2giga-milliseconds = 23 days, so it won't be too often if ( svs.time > 0x70000000 ) { SV_Shutdown( "Restarting server due to time wrapping" ); Cbuf_AddText( "vstr nextmap\n" ); return; } // this can happen considerably earlier when lots of clients play and the map doesn't change if ( svs.nextSnapshotEntities >= 0x7FFFFFFE - svs.numSnapshotEntities ) { SV_Shutdown( "Restarting server due to numSnapshotEntities wrapping" ); Cbuf_AddText( "vstr nextmap\n" ); return; } if( sv.restartTime && svs.time >= sv.restartTime ) { sv.restartTime = 0; Cbuf_AddText( "map_restart 0\n" ); return; } // update infostrings if anything has been changed if ( cvar_modifiedFlags & CVAR_SERVERINFO ) { SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; } if ( cvar_modifiedFlags & CVAR_SYSTEMINFO ) { SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; } if ( com_speeds->integer ) { startTime = Sys_Milliseconds (); } else { startTime = 0; // quite a compiler warning } // update ping based on the all received frames SV_CalcPings(); if (com_dedicated->integer) SV_BotFrame( svs.time ); // run the game simulation in chunks while ( sv.timeResidual >= frameMsec ) { sv.timeResidual -= frameMsec; svs.time += frameMsec; // let everything in the world think and move VM_Call( gvm, GAME_RUN_FRAME, svs.time ); } if ( com_speeds->integer ) { time_game = Sys_Milliseconds () - startTime; } // check timeouts SV_CheckTimeouts(); // send messages back to the clients SV_SendClientMessages(); // send a heartbeat to the master if needed SV_MasterHeartbeat(); }
/* ================ 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, ForceReload_e eForceReload ) { int i; int checksum; qboolean isBot; char systemInfo[16384]; const char *p; SV_SendMapChange(); re->RegisterMedia_LevelLoadBegin(server, eForceReload); // shut down the existing game if it is running SV_ShutdownGameProgs(); svs.gameStarted = qfalse; Com_Printf ("------ Server Initialization ------\n"); Com_Printf ("Server: %s\n",server); /* Ghoul2 Insert Start */ // de allocate the snapshot entities if (svs.snapshotEntities) { delete[] svs.snapshotEntities; svs.snapshotEntities = NULL; } /* Ghoul2 Insert End */ SV_SendMapChange(); // if not running a dedicated server CL_MapLoading will connect the client to the server // also print some status stuff CL_MapLoading(); #ifndef DEDICATED // make sure all the client stuff is unloaded CL_ShutdownAll( qfalse ); #endif CM_ClearMap(); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); re->InitSkins(); re->InitShaders(qtrue); // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue("sv_running") ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } SV_SendMapChange(); /* Ghoul2 Insert Start */ // clear out those shaders, images and Models as long as this // isnt a dedicated server. /* if ( !com_dedicated->integer ) { #ifndef DEDICATED R_InitImages(); R_InitShaders(); R_ModelInit(); #endif } else */ if (com_dedicated->integer) { re->SVModelInit(); } SV_SendMapChange(); // clear pak references FS_ClearPakReferences(0); /* Ghoul2 Insert Start */ // allocate the snapshot entities on the hunk // svs.snapshotEntities = (struct entityState_s *)Hunk_Alloc( sizeof(entityState_t)*svs.numSnapshotEntities, h_high ); svs.nextSnapshotEntities = 0; // allocate the snapshot entities svs.snapshotEntities = new entityState_s[svs.numSnapshotEntities]; // we CAN afford to do this here, since we know the STL vectors in Ghoul2 are empty memset(svs.snapshotEntities, 0, sizeof(entityState_t)*svs.numSnapshotEntities); /* Ghoul2 Insert End */ // 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) ); for (i=0 ; i<sv_maxclients->integer ; i++) { // save when the server started for each client already connected if (svs.clients[i].state >= CS_CONNECTED) { svs.clients[i].oldServerTime = svs.time; } } // wipe the entire per-level structure SV_ClearServer(); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } //rww - RAGDOLL_BEGIN re->G2API_SetTime(sv.time,0); //rww - RAGDOLL_END // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system srand(Com_Milliseconds()); sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); FS_Restart( sv.checksumFeed ); CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); SV_SendMapChange(); // set serverinfo visible name Cvar_Set( "mapname", server ); Cvar_Set( "sv_mapChecksum", va("%i",checksum) ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe 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; // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for ( i = 0 ;i < 3 ; i++ ) { //rww - RAGDOLL_BEGIN re->G2API_SetTime(sv.time,0); //rww - RAGDOLL_END GVM_RunFrame( sv.time ); SV_BotFrame( sv.time ); sv.time += 100; svs.time += 100; } //rww - RAGDOLL_BEGIN re->G2API_SetTime(sv.time,0); //rww - RAGDOLL_END // 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_DropClient( &svs.clients[i], "" ); continue; } isBot = qtrue; } else { isBot = qfalse; } // connect the client again denied = GVM_ClientConnect( 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 GVM_ClientBegin( i, qfalse ); } } } } // run another frame to allow things to look at all the players GVM_RunFrame( sv.time ); SV_BotFrame( sv.time ); sv.time += 100; svs.time += 100; //rww - RAGDOLL_BEGIN re->G2API_SetTime(sv.time,0); //rww - RAGDOLL_END 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 p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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(); /* MrE: 2000-09-13: now called in CL_DownloadsComplete // don't call when running dedicated if ( !com_dedicated->integer ) { // note that this is called after setting the hunk mark with Hunk_SetMark CL_StartHunkUsers(); } */ }
/* ================ 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; char systemInfo[16384]; const char *p; // shut down the existing game if it is running SV_ShutdownGameProgs(); Com_VPrintf( "------ Server Initialization ------\n" ); Com_VPrintf( "Server: %s\n", server ); // load map configurations SV_MapConfig( 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(qfalse); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); // clear collision map data CM_ClearMap(); // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue("sv_running") ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } // 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) ); for (i=0 ; i<sv_maxclients->integer ; i++) { // save when the server started for each client already connected if (svs.clients[i].state >= CS_CONNECTED) { svs.clients[i].oldServerTime = sv.time; } } // wipe the entire per-level structure SV_ClearServer(); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); 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) ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe 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; // load and spawn all other entities SV_InitGameProgs(); // don't allow a map_restart if game is modified sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for (i = 0;i < 3; i++) { VM_Call (gvm, GAME_RUN_FRAME, sv.time); SV_BotFrame (sv.time); sv.time += 100; svs.time += 100; } // 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_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->lastSnapshotTime = 0; // 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, sv.time); SV_BotFrame (sv.time); sv.time += 100; svs.time += 100; 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 p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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(); #ifndef DEDICATED if ( com_dedicated->integer ) { // restart renderer in order to show console for dedicated servers // launched through the regular binary CL_StartHunkUsers( qtrue ); } #endif Com_VPrintf( "-------------------------------\n" ); }
/* ================== BotSetInfoConfigString ================== */ void BotSetInfoConfigString(bot_state_t *bs) { char goalname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char action[MAX_MESSAGE_SIZE]; char *leader, carrying[32], *cs; bot_goal_t goal; // ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; else leader = " "; strcpy(carrying, " "); if (gametype == GT_CTF) { if (BotCTFCarryingFlag(bs)) { strcpy(carrying, "F "); } } switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "helping %s", goalname); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "accompanying %s", goalname); break; } case LTG_DEFENDKEYAREA: { botlib_export->ai.BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "defending %s", goalname); break; } case LTG_GETITEM: { botlib_export->ai.BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "getting item %s", goalname); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "killing %s", goalname); break; } case LTG_CAMP: case LTG_CAMPORDER: { Com_sprintf(action, sizeof(action), "camping"); break; } case LTG_PATROL: { Com_sprintf(action, sizeof(action), "patrolling"); break; } case LTG_GETFLAG: { Com_sprintf(action, sizeof(action), "capturing flag"); break; } case LTG_RUSHBASE: { Com_sprintf(action, sizeof(action), "rushing base"); break; } case LTG_RETURNFLAG: { Com_sprintf(action, sizeof(action), "returning flag"); break; } case LTG_ATTACKENEMYBASE: { Com_sprintf(action, sizeof(action), "attacking the enemy base"); break; } case LTG_HARVEST: { Com_sprintf(action, sizeof(action), "harvesting"); break; } default: { botlib_export->ai.BotGetTopGoal(bs->gs, &goal); botlib_export->ai.BotGoalName(goal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "roaming %s", goalname); break; } } cs = va("l\\%s\\c\\%s\\a\\%s", leader, carrying, action); SV_SetConfigstring (CS_BOTINFO + bs->client, cs); }
/* ================ 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; client_t *client; char *denied; qboolean isBot; int delay; // 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 ( sv.restartTime ) { return; } if (Cmd_Argc() > 1 ) { delay = atoi( Cmd_Argv(1) ); } else { delay = 5; } if( delay && !Cvar_VariableValue("g_doWarmup") ) { sv.restartTime = sv.time + delay * 1000; SV_SetConfigstring( CS_WARMUP, va("%i", sv.restartTime) ); return; } // check for changes in variables that can't just be restarted // check for maxclients change if ( sv_maxclients->modified || sv_gametype->modified ) { char mapname[MAX_QPATH]; Com_Printf( "variable change -- restarting.\n" ); // restart the map the slow way Q_strncpyz( mapname, Cvar_VariableString( "mapname" ), sizeof( mapname ) ); SV_SpawnServer( mapname, qfalse ); return; } // 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; SV_RestartGameProgs(); // run a few frames to allow everything to settle for (i = 0; i < 3; i++) { VM_Call (gvm, GAME_RUN_FRAME, sv.time); sv.time += 100; svs.time += 100; } 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 ) { 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 = 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 ); Com_Printf( "SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i ); 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, sv.time); sv.time += 100; svs.time += 100; }
/* ================ 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; char systemInfo[16384]; 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(); #ifndef DEDICATED // Restart renderer CL_StartHunkUsers( qtrue ); #endif // clear collision map data CM_ClearMap(); // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue("sv_running") ) { SV_Startup(); } else { // check for maxclients or democlients change if ( sv_maxclients->modified || sv_democlients->modified ) { SV_ChangeMaxClients(); } } // 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; for (i=0 ; i<sv_maxclients->integer ; i++) { // save when the server started for each client already connected if (svs.clients[i].state >= CS_CONNECTED) { svs.clients[i].oldServerTime = sv.time; } } // wipe the entire per-level structure SV_ClearServer(); for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { sv.configstrings[i] = CopyString(""); } // make sure we are not paused Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); 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) ); // serverid should be different each time sv.serverId = com_frameTime; sv.restartedServerId = sv.serverId; // I suppose the init here is just to be safe 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; // load and spawn all other entities SV_InitGameProgs(); // run a few frames to allow everything to settle for (i = 0;i < 3; i++) { VM_Call (gvm, GAME_RUN_FRAME, sv.time); sv.time += 100; svs.time += 100; } // 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; // connect the client again denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse ) ); // 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 { // when we get the next packet from a connected client, // the new gamestate will be sent svs.clients[i].state = CS_CONNECTED; } } } // run another frame to allow things to look at all the players VM_Call (gvm, GAME_RUN_FRAME, sv.time); sv.time += 100; svs.time += 100; // if a dedicated 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(); } // the server sends these to the clients so they will only // load pk3s also loaded at the server Cvar_Set( "sv_paks", sv_pure->integer ? FS_LoadedPakChecksums() : "" ); Cvar_Set( "sv_pakNames", sv_pure->integer ? FS_LoadedPakNames() : "" ); // the server sends these to the clients so they can figure // out which pk3s should be auto-downloaded p = FS_ReferencedPakChecksums(); Cvar_Set( "sv_referencedPaks", p ); p = FS_ReferencedPakNames(); Cvar_Set( "sv_referencedPakNames", p ); // save systeminfo and serverinfo strings Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); 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(); Com_Printf ("-----------------------------------\n"); // start recording a demo if ( sv_autoDemo->integer ) { qtime_t now; Com_RealTime( &now ); Cbuf_AddText( va( "demo_record %04d%02d%02d%02d%02d%02d-%s\n", 1900 + now.tm_year, 1 + now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, server ) ); } }
/* ================ 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; client_t *client; char *denied; qboolean isBot; int delay = 0; gamestate_t new_gs, old_gs; // NERVE - SMF int worldspawnflags; // DHM - Nerve int nextgt; // DHM - Nerve sharedEntity_t *world; // 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 ( sv.restartTime ) { return; } // DHM - Nerve :: Check for invalid gametype sv_gametype = Cvar_Get( "g_gametype", "5", CVAR_SERVERINFO | CVAR_LATCH ); nextgt = sv_gametype->integer; world = SV_GentityNum( ENTITYNUM_WORLD ); worldspawnflags = world->r.worldflags; if ( ( nextgt == GT_WOLF && ( worldspawnflags & 1 ) ) || ( nextgt == GT_WOLF_STOPWATCH && ( worldspawnflags & 2 ) ) || ( ( nextgt == GT_WOLF_CP || nextgt == GT_WOLF_CPH ) && ( worldspawnflags & 4 ) ) ) { if ( !( worldspawnflags & 1 ) ) { Cvar_Set( "g_gametype", "5" ); } else { Cvar_Set( "g_gametype", "7" ); } sv_gametype = Cvar_Get( "g_gametype", "5", CVAR_SERVERINFO | CVAR_LATCH ); } // dhm 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 = atoi( Cvar_VariableString( "gamestate" ) ); if ( Cmd_Argc() > 2 ) { new_gs = 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; } // 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 < 3 ; i++ ) { VM_Call( gvm, GAME_RUN_FRAME, svs.time ); svs.time += 100; } 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 ) { 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 = 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 ); 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 += 100; Cvar_Set( "sv_serverRestarting", "0" ); }
/* ================ 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; char systemInfo[MAX_INFO_STRING]; 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( "" ); } // init client structures and svs.numSnapshotEntities if ( !Cvar_VariableValue( "sv_running" ) ) { SV_Startup(); } else { // check for maxclients change if ( sv_maxclients->modified ) { SV_ChangeMaxClients(); } } // 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 if ( sv_gametype->integer == GT_SINGLE_PLAYER || sv_gametype->integer >= GT_WOLF ) { 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 ) ); // 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 sv_gametype->modified = qfalse; // run a few frames to allow everything to settle for ( i = 0 ; i < 3 ; i++ ) { VM_Call( gvm, GAME_RUN_FRAME, svs.time ); SV_BotFrame( svs.time ); svs.time += 100; } // 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 || Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) { 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 += 100; 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 ); } 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 Q_strncpyz( systemInfo, Cvar_InfoString_Big( CVAR_SYSTEMINFO ), sizeof( systemInfo ) ); cvar_modifiedFlags &= ~CVAR_SYSTEMINFO; SV_SetConfigstring( CS_SYSTEMINFO, systemInfo ); SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO ) ); cvar_modifiedFlags &= ~CVAR_SERVERINFO; // NERVE - SMF SV_SetConfigstring( CS_WOLFINFO, Cvar_InfoString( CVAR_WOLFINFO ) ); cvar_modifiedFlags &= ~CVAR_WOLFINFO; // 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(); Cvar_Set( "sv_serverRestarting", "0" ); Com_Printf( "-----------------------------------\n" ); }