void __cdecl My_SV_ClientEnterWorld(client_t* client, usercmd_t* cmd) { clientState_t state = client->state; // State before we call real one. SV_ClientEnterWorld(client, cmd); // gentity is NULL if map changed. // state is CS_PRIMED only if it's the first time they connect to the server, // otherwise the dispatcher would also go off when a game starts and such. if (client->gentity != NULL && state == CS_PRIMED) { ClientLoadedDispatcher(client->gentity->s.clientNum); } }
/* ================ 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; 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 ) { 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; } // 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 ) ); 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 VM_Call (gvm, GAME_RUN_FRAME, sv.time); sv.time += 100; svs.time += 100; }
/* ================== SV_UserMove The message usually contains all the movement commands that were in the last three packets, so that the information in dropped packets can be recovered. On very fast clients, there may be multiple usercmd packed into each of the backup packets. ================== */ static void SV_UserMove( client_t *cl, msg_t *msg ) { int i, start; int cmdNum; int firstNum; int cmdCount; usercmd_t nullcmd; usercmd_t cmds[MAX_PACKET_USERCMDS]; usercmd_t *cmd, *oldcmd; int clientTime; int serverId; cl->reliableAcknowledge = MSG_ReadLong( msg ); serverId = MSG_ReadLong( msg ); clientTime = MSG_ReadLong( msg ); cl->deltaMessage = MSG_ReadLong( msg ); // cmdNum is the command number of the most recent included usercmd cmdNum = MSG_ReadLong( msg ); cmdCount = MSG_ReadByte( msg ); if ( cmdCount < 1 ) { Com_Printf( "cmdCount < 1\n" ); return; } if ( cmdCount > MAX_PACKET_USERCMDS ) { Com_Printf( "cmdCount > MAX_PACKET_USERCMDS\n" ); return; } memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < cmdCount ; i++ ) { cmd = &cmds[i]; MSG_ReadDeltaUsercmd( msg, oldcmd, cmd ); oldcmd = cmd; } // if this is a usercmd from a previous gamestate, // ignore it or retransmit the current gamestate if ( serverId != sv.serverId ) { // if we can tell that the client has dropped the last // gamestate we sent them, resend it if ( cl->netchan.incomingAcknowledged > cl->gamestateMessageNum ) { Com_DPrintf( "%s : dropped gamestate, resending\n", cl->name ); SV_SendClientGameState( cl ); } return; } // if this is the first usercmd we have received // this gamestate, put the client into the world if ( cl->state == CS_PRIMED ) { SV_ClientEnterWorld( cl, &cmds[0], eSavedGameJustLoaded ); if ( sv_mapname->string[0]!='_' ) { char savename[MAX_QPATH]; if ( eSavedGameJustLoaded == eNO ) { SG_WriteSavegame("auto",qtrue); Com_sprintf (savename, sizeof(savename), "auto_%s",sv_mapname->string); SG_WriteSavegame(savename,qtrue);//can't use va becuase it's nested } else if ( qbLoadTransition == qtrue ) { Com_sprintf (savename, sizeof(savename), "hub/%s", sv_mapname->string ); SG_WriteSavegame( savename, qfalse );//save a full one SG_WriteSavegame( "auto", qfalse );//need a copy for auto, too } } eSavedGameJustLoaded = eNO; // the moves can be processed normaly } if ( cl->state != CS_ACTIVE ) { cl->deltaMessage = -1; return; } // if there is a time gap from the last packet to this packet, // fill in with the first command in the packet // with a packetdup of 0, firstNum == cmdNum firstNum = cmdNum - ( cmdCount - 1 ); if ( cl->cmdNum < firstNum - 1 ) { cl->droppedCommands = qtrue; if ( sv_showloss->integer ) { Com_Printf("Lost %i usercmds from %s\n", firstNum - 1 - cl->cmdNum, cl->name); } if ( cl->cmdNum < firstNum - 6 ) { cl->cmdNum = firstNum - 6; // don't generate too many } while ( cl->cmdNum < firstNum - 1 ) { cl->cmdNum++; SV_ClientThink( cl, &cmds[0] ); } } // skip over any usercmd_t we have already executed start = cl->cmdNum - ( firstNum - 1 ); for ( i = start ; i < cmdCount ; i++ ) { SV_ClientThink (cl, &cmds[ i ]); } cl->cmdNum = cmdNum; }
/* ================ 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") || Cvar_VariableValue("g_gametype") == GT_TOURNAMENT) ) { sv.restartTime = svs.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; } // toggle the server bit so clients can detect that a // map_restart has happened svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; // generate a new serverid sv.restartedServerId = sv.serverId; 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, 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 = (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 ); 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; }
/* ================ 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; // 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; } // ydnar: allow multiple delayed server restarts [atvi bug 3813] //% if ( sv.restartTime ) { //% return; //% } if ( Cmd_Argc() > 1 ) { delay = atoi( Cmd_Argv( 1 ) ); } // 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; } // 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 won't 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 ) { 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; } 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_UserMove The message usually contains all the movement commands that were in the last three packets, so that the information in dropped packets can be recovered. On very fast clients, there may be multiple usercmd packed into each of the backup packets. ================== */ static void SV_UserMove( client_t *cl, msg_t *msg, bool delta ) { int i; int cmdCount; usercmd_t nullcmd; usercmd_t cmds[ MAX_PACKET_USERCMDS ]; usercmd_t *cmd, *oldcmd; if ( delta ) { cl->deltaMessage = cl->messageAcknowledge; } else { cl->deltaMessage = -1; } cmdCount = MSG_ReadByte( msg ); if ( cmdCount < 1 ) { Log::Notice( "cmdCount < 1\n" ); return; } if ( cmdCount > MAX_PACKET_USERCMDS ) { Log::Notice( "cmdCount > MAX_PACKET_USERCMDS\n" ); return; } memset( &nullcmd, 0, sizeof( nullcmd ) ); oldcmd = &nullcmd; for ( i = 0; i < cmdCount; i++ ) { cmd = &cmds[ i ]; MSG_ReadDeltaUsercmd( msg, oldcmd, cmd ); // MSG_ReadDeltaUsercmd( msg, oldcmd, cmd ); oldcmd = cmd; } // save time for ping calculation cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time; // if this is the first usercmd we have received // this gamestate, put the client into the world if ( cl->state == clientState_t::CS_PRIMED ) { SV_ClientEnterWorld( cl, &cmds[ 0 ] ); // the moves can be processed normaly } if ( cl->state != clientState_t::CS_ACTIVE ) { cl->deltaMessage = -1; return; } // usually, the first couple commands will be duplicates // of ones we have previously received, but the servertimes // in the commands will cause them to be immediately discarded for ( i = 0; i < cmdCount; i++ ) { // if this is a cmd from before a map_restart ignore it if ( cmds[ i ].serverTime > cmds[ cmdCount - 1 ].serverTime ) { continue; } // extremely lagged or cmd from before a map_restart if ( cmds[ i ].serverTime <= cl->lastUsercmd.serverTime ) { continue; } SV_ClientThink( cl, &cmds[ i ] ); } }
/* ================== SV_UserMove The message usually contains all the movement commands that were in the last three packets, so that the information in dropped packets can be recovered. On very fast clients, there may be multiple usercmd packed into each of the backup packets. ================== */ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) { int i, key; int cmdCount; usercmd_t nullcmd; usercmd_t cmds[MAX_PACKET_USERCMDS]; usercmd_t *cmd, *oldcmd; if ( delta ) { cl->deltaMessage = cl->messageAcknowledge; } else { cl->deltaMessage = -1; } cmdCount = MSG_ReadByte( msg ); if ( cmdCount < 1 ) { Com_Printf( "cmdCount < 1\n" ); return; } if ( cmdCount > MAX_PACKET_USERCMDS ) { Com_Printf( "cmdCount > MAX_PACKET_USERCMDS\n" ); return; } // use the checksum feed in the key key = sv.checksumFeed; // also use the message acknowledge key ^= cl->messageAcknowledge; // also use the last acknowledged server command in the key key ^= Com_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32); Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < cmdCount ; i++ ) { cmd = &cmds[i]; MSG_ReadDeltaUsercmdKey( msg, key, oldcmd, cmd ); oldcmd = cmd; } // save time for ping calculation cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time; // if this is the first usercmd we have received // this gamestate, put the client into the world if ( cl->state == CS_PRIMED ) { SV_ClientEnterWorld( cl, &cmds[0] ); // the moves can be processed normaly } // if (sv_pure->integer != 0 && cl->pureAuthentic == 0) { SV_DropClient( cl, "Cannot validate pure client!"); return; } if ( cl->state != CS_ACTIVE ) { cl->deltaMessage = -1; return; } // usually, the first couple commands will be duplicates // of ones we have previously received, but the servertimes // in the commands will cause them to be immediately discarded for ( i = 0 ; i < cmdCount ; i++ ) { // if this is a cmd from before a map_restart ignore it if ( cmds[i].serverTime > cmds[cmdCount-1].serverTime ) { continue; } // extremely lagged or cmd from before a map_restart //if ( cmds[i].serverTime > svs.time + 3000 ) { // continue; //} // don't execute if this is an old cmd which is already executed // these old cmds are included when cl_packetdup > 0 if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) { continue; } SV_ClientThink (cl, &cmds[ i ]); } }
/* ================== SV_UserMove The message usually contains all the movement commands that were in the last three packets, so that the information in dropped packets can be recovered. On very fast clients, there may be multiple usercmd packed into each of the backup packets. ================== */ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) { int i, key; int cmdCount; usercmd_t nullcmd; usercmd_t cmds[MAX_PACKET_USERCMDS]; usercmd_t *cmd, *oldcmd; if ( delta ) { cl->deltaMessage = cl->messageAcknowledge; } else { cl->deltaMessage = -1; } cmdCount = MSG_ReadByte( msg ); if ( cmdCount < 1 ) { Com_Printf( "cmdCount < 1\n" ); return; } if ( cmdCount > MAX_PACKET_USERCMDS ) { Com_Printf( "cmdCount > MAX_PACKET_USERCMDS\n" ); return; } // use the checksum feed in the key key = sv.checksumFeed; // also use the message acknowledge key ^= cl->messageAcknowledge; // also use the last acknowledged server command in the key key ^= Com_HashKey( SV_GetServerCommand( cl, cl->reliableAcknowledge ), 32); Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < cmdCount ; i++ ) { cmd = &cmds[i]; MSG_ReadDeltaUsercmdKey( msg, key, oldcmd, cmd ); oldcmd = cmd; } // save time for ping calculation cl->frames[ cl->messageAcknowledge & PACKET_MASK ].messageAcked = svs.time; // TTimo // catch the no-cp-yet situation before SV_ClientEnterWorld // if CS_ACTIVE, then it's time to trigger a new gamestate emission // if not, then we are getting remaining parasite usermove commands, which we should ignore if (sv_pure->integer != 0 && cl->pureAuthentic == 0 && !cl->gotCP) { if (cl->state == CS_ACTIVE) { // we didn't get a cp yet, don't assume anything and just send the gamestate all over again Com_DPrintf( "%s: didn't get cp command, resending gamestate\n", cl->name, cl->state ); SV_SendClientGameState( cl ); } return; } // if this is the first usercmd we have received // this gamestate, put the client into the world if ( cl->state == CS_PRIMED ) { SV_ClientEnterWorld( cl, &cmds[0] ); // the moves can be processed normaly } // a bad cp command was sent, drop the client if (sv_pure->integer != 0 && cl->pureAuthentic == 0) { SV_DropClient( cl, "Cannot validate pure client!"); return; } if ( cl->state != CS_ACTIVE ) { cl->deltaMessage = -1; return; } // usually, the first couple commands will be duplicates // of ones we have previously received, but the servertimes // in the commands will cause them to be immediately discarded for ( i = 0 ; i < cmdCount ; i++ ) { // if this is a cmd from before a map_restart ignore it if ( cmds[i].serverTime > cmds[cmdCount-1].serverTime ) { continue; } // extremely lagged or cmd from before a map_restart //if ( cmds[i].serverTime > svs.time + 3000 ) { // continue; //} // don't execute if this is an old cmd which is already executed // these old cmds are included when cl_packetdup > 0 if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) { continue; } SV_ClientThink (cl, &cmds[ i ]); } }
/* ================ 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_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_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(); } }