summaryrefslogtreecommitdiff
path: root/source/sv_init.c
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
committerAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
commitf294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch)
treee8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/sv_init.c
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/sv_init.c')
-rw-r--r--source/sv_init.c497
1 files changed, 497 insertions, 0 deletions
diff --git a/source/sv_init.c b/source/sv_init.c
new file mode 100644
index 0000000..5086cd7
--- /dev/null
+++ b/source/sv_init.c
@@ -0,0 +1,497 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include "sv_local.h"
+#include "mvd_local.h"
+
+server_static_t svs; // persistant server info
+server_t sv; // local server
+
+/*
+================
+SV_FindIndex
+
+================
+*/
+static int SV_FindIndex( const char *name, int start, int max, qboolean create ) {
+ char *string;
+ int i;
+
+ if( !name || !name[0] )
+ return 0;
+
+ for( i = 1; i < max; i++ ) {
+ string = sv.configstrings[start + i];
+ if( !string[0] ) {
+ break;
+ }
+ if( !strcmp( string, name ) ) {
+ return i;
+ }
+ }
+
+ if( !create )
+ return 0;
+
+ if( i == max )
+ Com_Error( ERR_DROP, "SV_FindIndex: overflow" );
+
+ PF_Configstring( i + start, name );
+
+ return i;
+}
+
+
+int SV_ModelIndex (const char *name) {
+ return SV_FindIndex (name, CS_MODELS, MAX_MODELS, qtrue);
+}
+
+int SV_SoundIndex (const char *name) {
+ return SV_FindIndex (name, CS_SOUNDS, MAX_SOUNDS, qtrue);
+}
+
+int SV_ImageIndex (const char *name) {
+ return SV_FindIndex (name, CS_IMAGES, MAX_IMAGES, qtrue);
+}
+
+/*
+=================
+SV_CheckForSavegame
+=================
+*/
+void SV_CheckForSavegame (void) {
+ char name[MAX_OSPATH];
+ int i;
+
+ if ( sv_noreload->integer )
+ return;
+
+ if ( Cvar_VariableInteger( "deathmatch" ) )
+ return;
+
+ Com_sprintf (name, sizeof(name), "save/current/%s.sav", sv.name);
+
+ if( FS_LoadFile( name, NULL ) < 1 )
+ return; // no savegame
+
+
+ SV_ClearWorld ();
+
+ // get configstrings and areaportals
+ SV_ReadLevelFile ();
+
+ if (!sv.loadgame)
+ { // coming back to a level after being in a different
+ // level, so run it for ten seconds
+
+ // rlava2 was sending too many lightstyles, and overflowing the
+ // reliable data. temporarily changing the server state to loading
+ // prevents these from being passed down.
+ server_state_t previousState; // PGM
+
+ previousState = sv.state; // PGM
+ sv.state = ss_loading; // PGM
+ for (i=0 ; i<100 ; i++)
+ ge->RunFrame ();
+
+ sv.state = previousState; // PGM
+ }
+}
+
+void SV_ClientReset( client_t *client ) {
+ if( client->state < cs_connected ) {
+ return;
+ }
+
+ // any partially connected client will be restarted
+ client->state = cs_connected;
+ client->lastframe = -1;
+ client->sendTime = 0;
+ client->surpressCount = 0;
+ memset( &client->lastcmd, 0, sizeof( client->lastcmd ) );
+}
+
+/*
+================
+SV_SpawnServer
+
+Change the server to a new map, taking all connected
+clients along with it.
+================
+*/
+void SV_SpawnServer( char *server, char *spawnpoint, server_state_t serverstate,
+ attractLoop_t attractloop, qboolean loadgame,
+ qboolean fakemap )
+{
+ int i;
+ uint32 checksum;
+ char string[MAX_QPATH];
+ client_t *client;
+ tcpClient_t *t;
+
+ Com_Printf( "------- Server Initialization -------\n" );
+ Com_Printf( "SpawnServer: %s\n", server );
+
+ /* force MVD recording to stop */
+ SV_MvdRecStop();
+
+ CM_FreeMap( &sv.cm );
+
+ if( sv.demofile ) {
+ FS_FCloseFile( sv.demofile );
+ }
+
+ // wipe the entire per-level structure
+ memset( &sv, 0, sizeof( sv ) );
+ svs.realtime = 0;
+ svs.nextEntityStates = 0;
+ svs.nextPlayerStates = 0;
+ sv.loadgame = loadgame;
+ sv.attractloop = attractloop;
+ sv.spawncount = ( rand() | ( rand() << 16 ) ) ^ Sys_Realtime();
+ sv.spawncount &= 0x7FFFFFFF;
+
+ // setup a buffer for accumulating multicast datagrams
+ if( svs.multicast_buffer ) {
+ SZ_Init( &sv.multicast, svs.multicast_buffer, MAX_MSGLEN );
+ }
+
+ // init rate limits
+ SV_RateInit( &svs.ratelimit_status, sv_status_limit->integer, 1000 );
+ SV_RateInit( &svs.ratelimit_badpass, 1, sv_badauth_time->value * 1000 );
+ SV_RateInit( &svs.ratelimit_badrcon, 1, sv_badauth_time->value * 1000 );
+
+ // save name for levels that don't set message
+ Q_strncpyz( sv.configstrings[CS_NAME], server, MAX_QPATH );
+ Q_strncpyz( sv.name, server, sizeof( sv.name ) );
+
+ if( Cvar_VariableInteger( "deathmatch" ) ) {
+ strcpy( sv.configstrings[CS_AIRACCEL],
+ va( "%d", sv_airaccelerate->integer ) );
+ } else {
+ strcpy( sv.configstrings[CS_AIRACCEL], "0" );
+ }
+
+ FOR_EACH_CLIENT( client ) {
+ // needs to reconnect
+ SV_ClientReset( client );
+ }
+
+ if( !fakemap ) {
+ Com_sprintf( string, sizeof( string ), "maps/%s.bsp", server );
+ strcpy( sv.configstrings[CS_MODELS + 1], string );
+ CM_LoadMap( &sv.cm, string, 0, &checksum );
+
+ Com_sprintf( sv.configstrings[CS_MAPCHECKSUM],
+ MAX_QPATH, "%i", ( int )checksum );
+
+ for( i = 1; i < sv.cm.cache->numcmodels; i++ ) {
+ Com_sprintf( sv.configstrings[ CS_MODELS + 1 + i ],
+ MAX_QPATH, "*%i", i );
+ }
+ }
+
+ //
+ // clear physics interaction links
+ //
+ SV_ClearWorld();
+
+ //
+ // spawn the rest of the entities on the map
+ //
+
+ // precache and static commands can be issued during
+ // map initialization
+ sv.state = ss_loading;
+
+ // load and spawn all other entities
+ ge->SpawnEntities ( sv.name, CM_EntityString( &sv.cm ), spawnpoint );
+
+ // run two frames to allow everything to settle
+ ge->RunFrame ();
+ ge->RunFrame ();
+
+ // all precaches are complete
+ sv.state = serverstate;
+
+ // check for a savegame
+ SV_CheckForSavegame ();
+
+ // check maxclients (MVD clients rely on this)
+ if( serverstate < ss_cinematic ) {
+ i = atoi( sv.configstrings[CS_MAXCLIENTS] );
+ if( i != sv_maxclients->integer ) {
+ Com_WPrintf( "Game DLL specified wrong CS_MAXCLIENTS "
+ "(%d instead of %d), fixed.\n", i, sv_maxclients->integer );
+ Com_sprintf( string, sizeof( string ), "%d",
+ sv_maxclients->integer );
+ strcpy( sv.configstrings[CS_MAXCLIENTS], string );
+ }
+ }
+
+ SV_MvdSpawnDummy();
+
+ LIST_FOR_EACH( tcpClient_t, t, &svs.mvdClients, mvdEntry ) {
+ // needs to reconnect
+ if( t->state >= cs_connected ) {
+ t->state = cs_connected;
+ SV_MvdClientNew( t );
+ }
+ }
+
+ // set serverinfo variable
+ Cvar_FullSet( "mapname", sv.name, CVAR_SERVERINFO|CVAR_NOSET,
+ CVAR_SET_DIRECT );
+
+ Cvar_SetInteger( "sv_running", serverstate );
+ Cvar_SetInteger( "sv_paused", 0 );
+ if( serverstate != ss_demo ) {
+ Cvar_Set( "timedemo", "0" );
+ }
+
+ Com_Printf ("-------------------------------------\n");
+}
+
+
+/*
+==============
+SV_InitGame
+
+A brand new game has been started.
+If ismvd is true, load the built-in MVD game module.
+==============
+*/
+void SV_InitGame( qboolean ismvd ){
+ int i, entnum;
+ edict_t *ent;
+ client_t *client;
+
+ if( svs.initialized ) {
+ // cause any connected clients to reconnect
+ SV_Shutdown( "Server restarted\n", KILL_RESTART );
+ } else {
+ // make sure the client is down
+ CL_Disconnect( ERR_SILENT, NULL );
+ SCR_BeginLoadingPlaque();
+
+ CM_FreeMap( &sv.cm );
+ memset( &sv, 0, sizeof( sv ) );
+ }
+
+ // get any latched variable changes (maxclients, etc)
+ Cvar_GetLatchedVars ();
+
+ CL_LocalConnect();
+
+ if( ismvd ) {
+ Cvar_Set( "deathmatch", "1" );
+ Cvar_Set( "coop", "0" );
+ } else {
+ if( Cvar_VariableInteger( "coop" ) &&
+ Cvar_VariableInteger( "deathmatch" ) )
+ {
+ Com_Printf( "Deathmatch and Coop both set, disabling Coop\n" );
+ Cvar_Set( "coop", "0" );
+ }
+
+ // dedicated servers can't be single player and are usually DM
+ // so unless they explicity set coop, force it to deathmatch
+ if( dedicated->integer ) {
+ if( !Cvar_VariableInteger( "coop" ) )
+ Cvar_Set( "deathmatch", "1" );
+ }
+ }
+
+ // init clients
+ if( Cvar_VariableInteger( "deathmatch" ) ) {
+ if( sv_maxclients->integer <= 1 ) {
+ Cvar_SetInteger( "maxclients", 8 );
+ } else if( sv_maxclients->integer > CLIENTNUM_RESERVED ) {
+ Cvar_SetInteger( "maxclients", CLIENTNUM_RESERVED );
+ }
+ svs.gametype = GT_DEATHMATCH;
+ } else if( Cvar_VariableInteger( "coop" ) ) {
+ if( sv_maxclients->integer <= 1 || sv_maxclients->integer > 4 )
+ Cvar_SetInteger( "maxclients", 4 );
+ svs.gametype = GT_COOP;
+ } else { // non-deathmatch, non-coop is one player
+ Cvar_FullSet( "maxclients", "1", CVAR_SERVERINFO|CVAR_LATCH,
+ CVAR_SET_DIRECT );
+ svs.gametype = GT_SINGLEPLAYER;
+ }
+
+ Cvar_ClampInteger( sv_reserved_slots, 0, sv_maxclients->integer - 1 );
+
+ if( sv_maxclients->integer > 1 ) {
+ NET_Config( NET_SERVER );
+
+ if( sv_http_enable->integer ) {
+ if( NET_Listen( qtrue ) == NET_ERROR ) {
+ Com_EPrintf( "%s while opening server TCP port.\n",
+ NET_ErrorString() );
+ Cvar_Set( "sv_http_enable", "0" );
+ }
+ }
+ }
+
+ svs.numEntityStates = sv_maxclients->integer * UPDATE_BACKUP * MAX_PACKET_ENTITIES;
+ svs.numPlayerStates = sv_maxclients->integer * UPDATE_BACKUP;
+ if( sv_mvd_enable->integer && !ismvd ) {
+ svs.numEntityStates += MAX_EDICTS * 2;
+ svs.numPlayerStates += sv_maxclients->integer * 2;
+ svs.multicast_buffer = SV_Malloc( MAX_MSGLEN );
+ }
+
+ svs.clientpool = SV_Mallocz( sizeof( client_t ) * sv_maxclients->integer );
+ svs.entityStates = SV_Mallocz( sizeof( entity_state_t ) * svs.numEntityStates );
+ svs.playerStates = SV_Mallocz( sizeof( player_state_t ) * svs.numPlayerStates );
+
+#if USE_ZLIB
+ if( deflateInit2( &svs.z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 9,
+ Z_DEFAULT_STRATEGY ) != Z_OK )
+ {
+ Com_Error( ERR_FATAL, "deflateInit2() failed" );
+ }
+#endif
+
+ // heartbeats will always be sent to the id master
+ svs.last_heartbeat = -99999; // send immediately
+
+ // init game
+ if( ismvd ) {
+ if( ge ) {
+ SV_ShutdownGameProgs();
+ }
+ ge = &mvd_ge;
+ ge->Init();
+ } else {
+ SV_InitGameProgs();
+ }
+
+ List_Init( &svs.clients );
+ List_Init( &svs.mvdClients );
+ List_Init( &svs.tcpClients );
+
+ for( i = 0; i < sv_maxclients->integer; i++ ) {
+ client = svs.clientpool + i;
+ entnum = i + 1;
+ ent = EDICT_NUM( entnum );
+ ent->s.number = entnum;
+ client->edict = ent;
+ client->number = i;
+ }
+
+ svs.initialized = qtrue;
+}
+
+
+/*
+======================
+SV_Map
+
+ the full syntax is:
+
+ map [*]<map>$<startspot>+<nextserver>
+
+command from the console or progs.
+Map can also be a.cin, .pcx, or .dm2 file
+Nextserver is used to allow a cinematic to play, then proceed to
+another level:
+
+ map tram.cin+jail_e3
+======================
+*/
+void SV_Map (attractLoop_t attractloop, char *levelstring, qboolean loadgame)
+{
+ char level[MAX_QPATH];
+ char *ch;
+ int l;
+ char spawnpoint[MAX_QPATH];
+ server_state_t state;
+ qboolean fakemap;
+
+ // skip the end-of-unit flag if necessary
+ if( *levelstring == '*' ) {
+ levelstring++;
+ }
+
+ // moved here, because levelstring typically points to cmd_argv
+ // and may be clobbered by SV_InitGame
+ Q_strncpyz( level, levelstring, sizeof( level ) );
+
+ if( ( sv.state == ss_dead && !sv.loadgame ) ||
+ sv.state == ss_broadcast )
+ {
+ SV_InitGame ( qfalse ); // the game is just starting
+ }
+
+ // if there is a + in the map, set nextserver to the remainder
+ ch = strstr(level, "+");
+ if (ch)
+ {
+ *ch = 0;
+ Cvar_Set ("nextserver", va("gamemap \"%s\"", ch+1));
+ }
+ else
+ Cvar_Set ("nextserver", "");
+
+ //ZOID special hack for end game screen in coop mode
+ if (Cvar_VariableValue ("coop") && !Q_stricmp(level, "victory.pcx"))
+ Cvar_Set ("nextserver", "gamemap \"*base1\"");
+
+ // if there is a $, use the remainder as a spawnpoint
+ ch = strstr( level, "$" );
+ if( ch ) {
+ *ch = 0;
+ strcpy( spawnpoint, ch + 1 );
+ } else {
+ spawnpoint[0] = 0;
+ }
+
+ l = strlen( level );
+ if( l > 4 && !strcmp( level + l - 4, ".cin" ) ) {
+ fakemap = qtrue;
+ state = ss_cinematic;
+ } else if( l > 4 && !strcmp( level + l - 4, ".dm2" ) ) {
+ fakemap = qtrue;
+ state = ss_demo;
+ } else if( l > 4 && !strcmp( level + l - 4, ".pcx" ) ) {
+ fakemap = qtrue;
+ state = ss_pic;
+ } else {
+ fakemap = qfalse;
+ state = ss_game;
+ }
+
+ SCR_BeginLoadingPlaque(); // for local system
+ SV_BroadcastCommand( "changing\n" );
+ /* temporary change server state to loading so
+ * SV_SendClientMessages will not send dummy svc_frame */
+ if( sv.state > ss_loading ) {
+ sv.state = ss_loading;
+ }
+ SV_SendClientMessages();
+ SV_SendAsyncPackets();
+ SV_SpawnServer( level, spawnpoint, state, attractloop,
+ loadgame, fakemap );
+
+ SV_BroadcastCommand( "reconnect\n" );
+}
+