/* ================== SV_KickNum_f Kick a user off of the server ================== */ static void SV_KickNum_f( void ) { player_t *player; client_t *client; // make sure server is running if ( !com_sv_running->integer ) { Com_Printf( "Server is not running.\n" ); return; } if ( Cmd_Argc() != 2 ) { Com_Printf ("Usage: %s <client number>\n", Cmd_Argv(0)); return; } player = SV_GetPlayerByNum(); if ( !player ) { return; } client = player->client; if( client->netchan.remoteAddress.type == NA_LOOPBACK ) { Com_Printf("Cannot kick host player\n"); return; } SV_DropPlayer( player, "was kicked" ); client->lastPacketTime = svs.time; // in case there is a funny zombie }
/* ================ 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_Printf ("Loading level %s...\n", server); Com_DPrintf ("------ Server Initialization ------\n"); Com_DPrintf ("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(qfalse); // clear the whole hunk because we're (re)loading the server Hunk_Clear(); #ifdef DEDICATED // Restart renderer SV_InitDedicatedRef(); #else // Restart renderer, and if dedicated start cgame CL_StartHunkUsers( !com_dedicated->integer ); #endif // 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(); } } // 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].s = CopyString(""); sv.configstrings[i].restricted = qfalse; Com_Memset(&sv.configstrings[i].clientList, 0, sizeof(clientList_t)); } // make sure we are not paused Cvar_Set("cl_paused", "0"); // restart the file system FS_Restart(qfalse); 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 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(); // allocate the snapshot entities on the hunk DA_Init( &svs.snapshotEntities, svs.numSnapshotEntities, sv.gameEntityStateSize, qfalse ); svs.nextSnapshotEntities = 0; // 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; player_t *player; int j; if ( svs.clients[i].netchan.remoteAddress.type == NA_BOT ) { if ( killBots ) { SV_DropClient( &svs.clients[i], "" ); continue; } isBot = qtrue; } else { isBot = qfalse; } for ( j = 0; j < MAX_SPLITVIEW; j++ ) { player = svs.clients[i].localPlayers[j]; if ( !player ) continue; // setup entity before connecting SV_SetupPlayerEntity( player ); // connect the client again denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, player - svs.players, qfalse, isBot, i, j ) ); // firstTime = qfalse player = svs.clients[i].localPlayers[j]; // may be NULL if game dropped player if ( denied && player != NULL ) { // this generally shouldn't happen, because the client // was connected before the level change SV_DropPlayer( player, denied ); } } // check if client was dropped if ( svs.clients[i].state < CS_CONNECTED ) { continue; } 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; client = &svs.clients[i]; client->state = CS_ACTIVE; 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; // Force sv_pure to off if invalid default pk3s if (sv_pure->integer && !com_fs_pure->integer) { Cvar_Set( "sv_pure", "0" ); } 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_DPrintf ("-----------------------------------\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, j; client_t *client; player_t *player; char *denied; qboolean isBot; // 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; } // let game delay or pervent the restart or force full map reload sv.restartTime = VM_Call( gvm, GAME_MAP_RESTART, sv.time, sv.restartTime ); if( sv.restartTime > sv.time ) { return; } // check for changes in variables that can't just be restarted // check for maxclients change if ( sv_maxclients->modified || sv.restartTime < 0 ) { 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 ) ); // 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_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, -1, "map_restart\n" ); for ( j = 0; j < MAX_SPLITVIEW; j++ ) { player = client->localPlayers[j]; if ( !player ) continue; // setup entity before connecting SV_SetupPlayerEntity( player ); // connect the client again, without the firstTime flag denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_PLAYER_CONNECT, player - svs.players, qfalse, isBot, client - svs.clients, j ) ); player = client->localPlayers[j]; // may be NULL if game dropped player if ( denied ) { // this generally shouldn't happen, because the player // was connected before the level change if ( player != NULL ) { SV_DropPlayer( player, denied ); } Com_Printf( "SV_MapRestart_f: dropped client %i - denied!\n", i ); continue; } if(client->state == CS_ACTIVE) SV_PlayerEnterWorld( player, &player->lastUsercmd ); else { // If we don't reset player->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_PlayerEnterWorld( player, NULL ); } } } // 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; }