Example #1
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;
	const char *p;

	// shut down the existing game if it is running
	SV_ShutdownGameProgs();

	Com_Printf( "------ Server Initialization ------\n" );
	Com_Printf( "Server: %s\n", server );

	// if not running a dedicated server CL_MapLoading will connect the client to the server
	// also print some status stuff
	CL_MapLoading();

	// make sure all the client stuff is unloaded
	CL_ShutdownAll();

	// clear the whole hunk because we're (re)loading the server
	Hunk_Clear();

	// clear collision map data     // (SA) NOTE: TODO: used in missionpack
	CM_ClearMap();

	// wipe the entire per-level structure
	SV_ClearServer();

	// MrE: main zone should be pretty much emtpy at this point
	// except for file system data and cached renderer data
	Z_LogHeap();

	// allocate empty config strings
	for ( i = 0; i < MAX_CONFIGSTRINGS; i++ )
	{
		sv.configstrings[ i ] = CopyString( "" );
		sv.configstringsmodified[ i ] = qfalse;
	}

	// init client structures and svs.numSnapshotEntities
	if ( !Cvar_VariableValue( "sv_running" ) )
	{
		SV_Startup();
	}
	else
	{
		// check for maxclients change
		if ( sv_maxclients->modified )
		{
			SV_ChangeMaxClients();
		}

#ifdef USE_HUB_SERVER

		// if sv_owHubHost was changed, resolve the address again
		if ( sv_owHubHost->modified )
		{
			sv_owHubHost->modified = qfalse;
			SV_ResolveowHubHost();
		}

#endif
	}

	// clear pak references
	FS_ClearPakReferences( 0 );

	// allocate the snapshot entities on the hunk
	svs.snapshotEntities = Hunk_Alloc( sizeof( entityState_t ) * svs.numSnapshotEntities, h_high );
	svs.nextSnapshotEntities = 0;

	// toggle the server bit so clients can detect that a
	// server has changed
	svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;

	// set nextmap to the same map, but it may be overriden
	// by the game startup or another console command
	Cvar_Set( "nextmap", "map_restart 0" );
//  Cvar_Set( "nextmap", va("map %s", server) );

	// Ridah
	// DHM - Nerve :: We want to use the completion bar in multiplayer as well
	// Arnout: just always use it
//  if( !SV_GameIsSinglePlayer() ) {
	SV_SetExpectedHunkUsage( va( "maps/%s.bsp", server ) );
//  } else {
	// just set it to a negative number,so the cgame knows not to draw the percent bar
//      Cvar_Set( "com_expectedhunkusage", "-1" );
//  }

	// make sure we are not paused
	Cvar_Set( "cl_paused", "0" );

#if !defined( DO_LIGHT_DEDICATED )
	// get a new checksum feed and restart the file system
	srand( Sys_Milliseconds() );
	sv.checksumFeed = ( ( ( int ) rand() << 16 ) ^ rand() ) ^ Sys_Milliseconds();

	// DO_LIGHT_DEDICATED
	// only comment out when you need a new pure checksum string and it's associated random feed
	//Com_DPrintf("SV_SpawnServer checksum feed: %p\n", sv.checksumFeed);

#else // DO_LIGHT_DEDICATED implementation below
	// we are not able to randomize the checksum feed since the feed is used as key for pure_checksum computations
	// files.c 1776 : pack->pure_checksum = Com_BlockChecksumKey( fs_headerLongs, 4 * fs_numHeaderLongs, LittleLong(fs_checksumFeed) );
	// we request a fake randomized feed, files.c knows the answer
	srand( Sys_Milliseconds() );
	sv.checksumFeed = FS_RandChecksumFeed();
#endif
	FS_Restart( sv.checksumFeed );

	CM_LoadMap( va( "maps/%s.bsp", server ), qfalse, &checksum );

	// set serverinfo visible name
	Cvar_Set( "mapname", server );

	Cvar_Set( "sv_mapChecksum", va( "%i", checksum ) );

	sv_newGameShlib = Cvar_Get( "sv_newGameShlib", "", CVAR_TEMP );

	// serverid should be different each time
	sv.serverId = com_frameTime;
	sv.restartedServerId = sv.serverId;
	sv.checksumFeedServerId = sv.serverId;
	Cvar_Set( "sv_serverid", va( "%i", sv.serverId ) );

	// clear physics interaction links
	SV_ClearWorld();

	// media configstring setting should be done during
	// the loading stage, so connected clients don't have
	// to load during actual gameplay
	sv.state = SS_LOADING;

	Cvar_Set( "sv_serverRestarting", "1" );

	// load and spawn all other entities
	SV_InitGameProgs();

	// don't allow a map_restart if game is modified
	// Arnout: there isn't any check done against this, obsolete
//  sv_gametype->modified = qfalse;

	// run a few frames to allow everything to settle
	for ( i = 0; i < GAME_INIT_FRAMES; i++ )
	{
		VM_Call( gvm, GAME_RUN_FRAME, svs.time );
		SV_BotFrame( svs.time );
		svs.time += FRAMETIME;
	}

	// create a baseline for more efficient communications
	SV_CreateBaseline();

	for ( i = 0; i < sv_maxclients->integer; i++ )
	{
		// send the new gamestate to all connected clients
		if ( svs.clients[ i ].state >= CS_CONNECTED )
		{
			char *denied;

			if ( svs.clients[ i ].netchan.remoteAddress.type == NA_BOT )
			{
				if ( killBots || SV_GameIsSinglePlayer() || SV_GameIsCoop() )
				{
					SV_DropClient( &svs.clients[ i ], "" );
					continue;
				}

				isBot = qtrue;
			}
			else
			{
				isBot = qfalse;
			}

			// connect the client again
			denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) );   // firstTime = qfalse

			if ( denied )
			{
				// this generally shouldn't happen, because the client
				// was connected before the level change
				SV_DropClient( &svs.clients[ i ], denied );
			}
			else
			{
				if ( !isBot )
				{
					// when we get the next packet from a connected client,
					// the new gamestate will be sent
					svs.clients[ i ].state = CS_CONNECTED;
				}
				else
				{
					client_t       *client;
					sharedEntity_t *ent;

					client = &svs.clients[ i ];
					client->state = CS_ACTIVE;
					ent = SV_GentityNum( i );
					ent->s.number = i;
					client->gentity = ent;

					client->deltaMessage = -1;
					client->nextSnapshotTime = svs.time; // generate a snapshot immediately

					VM_Call( gvm, GAME_CLIENT_BEGIN, i );
				}
			}
		}
	}

	// run another frame to allow things to look at all the players
	VM_Call( gvm, GAME_RUN_FRAME, svs.time );
	SV_BotFrame( svs.time );
	svs.time += FRAMETIME;

	if ( sv_pure->integer )
	{
		// the server sends these to the clients so they will only
		// load pk3s also loaded at the server
		p = FS_LoadedPakChecksums();
		Cvar_Set( "sv_paks", p );

		if ( strlen( p ) == 0 )
		{
			Com_Printf( "WARNING: sv_pure set but no PK3 files loaded\n" );
		}

		p = FS_LoadedPakNames();
		Cvar_Set( "sv_pakNames", p );

		// if a dedicated pure server we need to touch the cgame because it could be in a
		// seperate pk3 file and the client will need to load the latest cgame.qvm
		if ( com_dedicated->integer )
		{
			SV_TouchCGame();
		}
	}
	else
	{
		Cvar_Set( "sv_paks", "" );
		Cvar_Set( "sv_pakNames", "" );
	}

	// the server sends these to the clients so they can figure
	// out which pk3s should be auto-downloaded
	// NOTE: we consider the referencedPaks as 'required for operation'

	// we want the server to reference the mp_bin pk3 that the client is expected to load from
	SV_TouchCGameDLL();

	p = FS_ReferencedPakChecksums();
	Cvar_Set( "sv_referencedPaks", p );
	p = FS_ReferencedPakNames();
	Cvar_Set( "sv_referencedPakNames", p );

	// save systeminfo and serverinfo strings
	cvar_modifiedFlags &= ~CVAR_SYSTEMINFO;
	SV_SetConfigstring( CS_SYSTEMINFO, Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );

	SV_SetConfigstring( CS_SERVERINFO, Cvar_InfoString( CVAR_SERVERINFO | CVAR_SERVERINFO_NOUPDATE ) );
	cvar_modifiedFlags &= ~CVAR_SERVERINFO;

	// any media configstring setting now should issue a warning
	// and any configstring changes should be reliably transmitted
	// to all clients
	sv.state = SS_GAME;

	// send a heartbeat now so the master will get up to date info
	SV_Heartbeat_f();

	Hunk_SetMark();

	SV_UpdateConfigStrings();

	Cvar_Set( "sv_serverRestarting", "0" );

	Com_Printf( "-----------------------------------\n" );
}
Example #2
0
/**
 * @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)
		{
			// If we are playing/waiting to play/waiting to stop a demo, we use a specialized function that will move real clients slots (so that democlients will be put to their original slots they were affected at the time of the real game)
			if (sv.demoState == DS_WAITINGPLAYBACK || sv.demoState == DS_PLAYBACK || sv.demoState == DS_WAITINGSTOP)
			{
				SV_DemoChangeMaxClients();
			}
			else
			{
				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 mod_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();

	Com_Printf("-----------------------------------\n");

	// start recording a demo
	if (sv_autoDemo->integer)
	{
		SV_DemoAutoDemoRecord();
	}
}