diff options
Diffstat (limited to 'src/sv_init.c')
-rw-r--r-- | src/sv_init.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/src/sv_init.c b/src/sv_init.c new file mode 100644 index 0000000..72e1bff --- /dev/null +++ b/src/sv_init.c @@ -0,0 +1,402 @@ +/* +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" + +server_static_t svs; // persistant server info +server_t sv; // local server + +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->frames_nodelta = 0; + client->send_delta = 0; + client->surpressCount = 0; + memset( &client->lastcmd, 0, sizeof( client->lastcmd ) ); +} + +#if USE_FPS +static void set_frame_time( void ) { + int i = sv_fps->integer / 10; + + clamp( i, 1, 6 ); + + sv.frametime = 100 / i; + sv.framemult = i; + + Cvar_SetInteger( sv_fps, i * 10, CVAR_SET_DIRECT ); +} +#endif + +#if !USE_CLIENT +static void resolve_masters( void ) { + master_t *m; + time_t now, delta; + + now = time( NULL ); + FOR_EACH_MASTER( m ) { + // re-resolve valid address after one day, + // resolve invalid address after three hours + delta = m->adr.port ? 24*60*60 : 3*60*60; + if( now < m->last_resolved ) { + m->last_resolved = now; + continue; + } + if( now - m->last_resolved < delta ) { + continue; + } + if( NET_StringToAdr( m->name, &m->adr, PORT_MASTER ) ) { + Com_DPrintf( "Master server at %s.\n", NET_AdrToString( &m->adr ) ); + } else { + Com_WPrintf( "Couldn't resolve master: %s\n", m->name ); + m->adr.port = 0; + } + m->last_resolved = now = time( NULL ); + } +} +#endif + +/* +================ +SV_SpawnServer + +Change the server to a new map, taking all connected +clients along with it. +================ +*/ +static void SV_SpawnServer( cm_t *cm, const char *server, const char *spawnpoint ) { + int i; + client_t *client; + + Com_Printf( "------- Server Initialization -------\n" ); + Com_Printf( "SpawnServer: %s\n", server ); + + CM_FreeMap( &sv.cm ); + + // wipe the entire per-level structure + memset( &sv, 0, sizeof( sv ) ); + sv.spawncount = ( rand() | ( rand() << 16 ) ) ^ Sys_Milliseconds(); + sv.spawncount &= 0x7FFFFFFF; + + // reset entity counter + svs.nextEntityStates = 0; + + // save name for levels that don't set message + Q_strlcpy( sv.configstrings[CS_NAME], server, MAX_QPATH ); + Q_strlcpy( sv.name, server, sizeof( sv.name ) ); + + if( Cvar_VariableInteger( "deathmatch" ) ) { + sprintf( sv.configstrings[CS_AIRACCEL], + "%d", sv_airaccelerate->integer ); + } else { + strcpy( sv.configstrings[CS_AIRACCEL], "0" ); + } + + FOR_EACH_CLIENT( client ) { + // needs to reconnect + SV_ClientReset( client ); + client->spawncount = sv.spawncount; + } + +#if !USE_CLIENT + resolve_masters(); +#endif + + sv.cm = *cm; + sprintf( sv.configstrings[CS_MAPCHECKSUM], "%d", ( int )cm->cache->checksum ); + + // set inline model names + Q_concat( sv.configstrings[CS_MODELS + 1], MAX_QPATH, "maps/", server, ".bsp", NULL ); + for( i = 1; i < cm->cache->nummodels; i++ ) { + sprintf( sv.configstrings[ CS_MODELS + 1 + i ], "*%d", i ); + } + + // + // clear physics interaction links + // + SV_ClearWorld(); + + // + // spawn the rest of the entities on the map + // +#if USE_FPS + set_frame_time(); +#endif + + // 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->cache->entitystring, spawnpoint ); + + // run two frames to allow everything to settle + ge->RunFrame (); + ge->RunFrame (); + + // make sure maxclients string is correct + sprintf( sv.configstrings[CS_MAXCLIENTS], "%d", sv_maxclients->integer ); + + // all precaches are complete + sv.state = ss_game; + +#if USE_MVD_SERVER + // respawn dummy MVD client, set base states, etc + SV_MvdMapChanged(); +#endif + + // set serverinfo variable + SV_InfoSet( "mapname", sv.name ); + SV_InfoSet( "port", net_port->string ); + + Cvar_SetInteger( sv_running, ss_game, FROM_CODE ); + Cvar_Set( "sv_paused", "0" ); + Cvar_Set( "timedemo", "0" ); + + EXEC_TRIGGER( sv_changemapcmd ); + +#if USE_SYSCON + SV_SetConsoleTitle(); +#endif + + 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 { +#if USE_CLIENT + // make sure the client is down + CL_Disconnect( ERR_SILENT, NULL ); + SCR_BeginLoadingPlaque(); +#endif + + CM_FreeMap( &sv.cm ); + memset( &sv, 0, sizeof( sv ) ); + } + + // get any latched variable changes (maxclients, etc) + Cvar_GetLatchedVars (); + +#if !USE_CLIENT + Cvar_Reset( sv_recycle ); +#endif + + 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( Com_IsDedicated() ) { + if( !Cvar_VariableInteger( "coop" ) ) + Cvar_Set( "deathmatch", "1" ); + } + } + + // init clients + if( Cvar_VariableInteger( "deathmatch" ) ) { + if( sv_maxclients->integer <= 1 ) { + Cvar_SetInteger( sv_maxclients, 8, FROM_CODE ); + } else if( sv_maxclients->integer > CLIENTNUM_RESERVED ) { + Cvar_SetInteger( sv_maxclients, CLIENTNUM_RESERVED, FROM_CODE ); + } + } else if( Cvar_VariableInteger( "coop" ) ) { + if( sv_maxclients->integer <= 1 || sv_maxclients->integer > 4 ) + Cvar_Set( "maxclients", "4" ); + } else { // non-deathmatch, non-coop is one player + Cvar_FullSet( "maxclients", "1", CVAR_SERVERINFO|CVAR_LATCH, FROM_CODE ); + } + + // enable networking + if( sv_maxclients->integer > 1 ) { + NET_Config( NET_SERVER ); + } + + svs.udp_client_pool = SV_Mallocz( sizeof( client_t ) * sv_maxclients->integer ); + + svs.numEntityStates = sv_maxclients->integer * UPDATE_BACKUP * MAX_PACKET_ENTITIES; + svs.entityStates = SV_Mallocz( sizeof( entity_state_t ) * svs.numEntityStates ); + +#if USE_MVD_SERVER + // initialize MVD server + if( !ismvd ) { + SV_MvdInit(); + } +#endif + + Cvar_ClampInteger( sv_reserved_slots, 0, sv_maxclients->integer - 1 ); + +#if USE_ZLIB + svs.z.zalloc = SV_Zalloc; + svs.z.zfree = SV_Zfree; + if( deflateInit2( &svs.z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + -MAX_WBITS, 9, Z_DEFAULT_STRATEGY ) != Z_OK ) + { + Com_Error( ERR_FATAL, "%s: deflateInit2() failed", __func__ ); + } +#endif + + // init game +#if USE_MVD_CLIENT + if( ismvd ) { + if( ge ) { + SV_ShutdownGameProgs(); + } + ge = &mvd_ge; + ge->Init(); + } else +#endif + SV_InitGameProgs(); + + // send heartbeat very soon + svs.last_heartbeat = -(HEARTBEAT_SECONDS-5)*1000; + + List_Init( &svs.udp_client_list ); + + for( i = 0; i < sv_maxclients->integer; i++ ) { + client = svs.udp_client_pool + i; + entnum = i + 1; + ent = EDICT_NUM( entnum ); + ent->s.number = entnum; + client->edict = ent; + client->number = i; + } + +#if USE_AC_SERVER + AC_Connect( ismvd ); +#endif + + 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 (const char *levelstring, qboolean restart) { + char level[MAX_QPATH]; + char spawnpoint[MAX_QPATH]; + char expanded[MAX_QPATH]; + char *ch; + cm_t cm; + qerror_t ret; + size_t len; + + // skip the end-of-unit flag if necessary + if( *levelstring == '*' ) { + levelstring++; + } + + // save levelstring as it typically points to cmd_argv + len = Q_strlcpy( level, levelstring, sizeof( level ) ); + if( len >= sizeof( level ) ) { + Com_Printf( "Refusing to process oversize level string.\n" ); + return; + } + + // if there is a + in the map, set nextserver to the remainder + ch = strchr(level, '+'); + if (ch) { + *ch = 0; + Cvar_Set ("nextserver", va("gamemap \"%s\"", ch+1)); + } else { + Cvar_Set ("nextserver", ""); + } + + // if there is a $, use the remainder as a spawnpoint + ch = strchr( level, '$' ); + if( ch ) { + *ch = 0; + strcpy( spawnpoint, ch + 1 ); + } else { + spawnpoint[0] = 0; + } + + len = Q_concat( expanded, sizeof( expanded ), "maps/", level, ".bsp", NULL ); + if( len >= sizeof( expanded ) ) { + ret = Q_ERR_NAMETOOLONG; + } else { + ret = CM_LoadMap( &cm, expanded ); + } + if( ret ) { + Com_Printf( "Couldn't load %s: %s\n", expanded, Q_ErrorString( ret ) ); + return; + } + + if( sv.state != ss_game || restart ) { + SV_InitGame( qfalse ); // the game is just starting + } + + // change state to loading + if( sv.state > ss_loading ) { + sv.state = ss_loading; + } + +#if USE_CLIENT + SCR_BeginLoadingPlaque(); // for local system +#endif + SV_BroadcastCommand( "changing map=%s\n", level ); + SV_SendClientMessages(); + SV_SendAsyncPackets(); + SV_SpawnServer( &cm, level, spawnpoint ); + + SV_BroadcastCommand( "reconnect\n" ); +} + |