yquake2remaster/src/server/sv_init.c

506 lines
11 KiB
C
Raw Normal View History

/*
2010-11-26 07:22:46 +00:00
* 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.
*
* =======================================================================
*
* Server startup.
*
* =======================================================================
*/
#include "header/server.h"
2010-11-26 07:22:46 +00:00
server_static_t svs; /* persistant server info */
server_t sv; /* local server */
2010-11-26 07:22:46 +00:00
int
SV_FindIndex ( char *name, int start, int max, qboolean create )
{
2010-11-26 07:22:46 +00:00
int i;
if ( !name || !name [ 0 ] )
{
return ( 0 );
}
2010-11-26 07:22:46 +00:00
for ( i = 1; i < max && sv.configstrings [ start + i ] [ 0 ]; i++ )
{
if ( !strcmp( sv.configstrings [ start + i ], name ) )
{
return ( i );
}
}
2010-11-26 07:22:46 +00:00
if ( !create )
{
return ( 0 );
}
2010-11-26 07:22:46 +00:00
if ( i == max )
{
Com_Error( ERR_DROP, "*Index: overflow" );
}
2010-11-26 07:22:46 +00:00
strncpy( sv.configstrings [ start + i ], name, sizeof ( sv.configstrings [ i ] ) );
2010-11-26 07:22:46 +00:00
if ( sv.state != ss_loading )
{
/* send the update to everyone */
MSG_WriteChar( &sv.multicast, svc_configstring );
MSG_WriteShort( &sv.multicast, start + i );
MSG_WriteString( &sv.multicast, name );
SV_Multicast( vec3_origin, MULTICAST_ALL_R );
}
2010-11-26 07:22:46 +00:00
return ( i );
}
2010-11-26 07:22:46 +00:00
int
SV_ModelIndex ( char *name )
{
2010-11-26 07:22:46 +00:00
return ( SV_FindIndex( name, CS_MODELS, MAX_MODELS, true ) );
}
2010-11-26 07:22:46 +00:00
int
SV_SoundIndex ( char *name )
{
2010-11-26 07:22:46 +00:00
return ( SV_FindIndex( name, CS_SOUNDS, MAX_SOUNDS, true ) );
}
2010-11-26 07:22:46 +00:00
int
SV_ImageIndex ( char *name )
{
2010-11-26 07:22:46 +00:00
return ( SV_FindIndex( name, CS_IMAGES, MAX_IMAGES, true ) );
}
/*
2010-11-26 07:22:46 +00:00
* Entity baselines are used to compress the update messages
* to the clients -- only the fields that differ from the
* baseline will be transmitted
*/
void
SV_CreateBaseline ( void )
{
2010-11-26 07:22:46 +00:00
edict_t *svent;
int entnum;
2010-11-26 07:22:46 +00:00
for ( entnum = 1; entnum < ge->num_edicts; entnum++ )
{
2010-11-26 07:22:46 +00:00
svent = EDICT_NUM( entnum );
if ( !svent->inuse )
{
continue;
2010-11-26 07:22:46 +00:00
}
if ( !svent->s.modelindex && !svent->s.sound && !svent->s.effects )
{
continue;
2010-11-26 07:22:46 +00:00
}
svent->s.number = entnum;
2010-11-26 07:22:46 +00:00
/* take current state as baseline */
VectorCopy( svent->s.origin, svent->s.old_origin );
sv.baselines [ entnum ] = svent->s;
}
}
2010-11-26 07:22:46 +00:00
void
SV_CheckForSavegame ( void )
{
2010-11-26 07:22:46 +00:00
char name [ MAX_OSPATH ];
FILE *f;
int i;
2010-11-26 07:22:46 +00:00
if ( sv_noreload->value )
{
return;
2010-11-26 07:22:46 +00:00
}
2010-11-26 07:22:46 +00:00
if ( Cvar_VariableValue( "deathmatch" ) )
{
return;
2010-11-26 07:22:46 +00:00
}
2010-11-26 07:22:46 +00:00
Com_sprintf( name, sizeof ( name ), "%s/save/current/%s.sav", FS_Gamedir(), sv.name );
f = fopen( name, "rb" );
2010-11-26 07:22:46 +00:00
if ( !f )
{
return; /* no savegame */
}
fclose( f );
2010-11-26 07:22:46 +00:00
SV_ClearWorld();
2010-11-26 07:22:46 +00:00
/* get configstrings and areaportals */
SV_ReadLevelFile();
2010-11-26 07:22:46 +00:00
if ( !sv.loadgame )
{
/* coming back to a level after being in a different
level, so run it for ten seconds */
2010-11-26 07:22:46 +00:00
server_state_t previousState;
2010-11-26 07:22:46 +00:00
previousState = sv.state;
sv.state = ss_loading;
2010-11-26 07:22:46 +00:00
for ( i = 0; i < 100; i++ )
{
ge->RunFrame();
}
sv.state = previousState;
}
}
/*
2010-11-26 07:22:46 +00:00
* 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, qboolean attractloop, qboolean loadgame )
{
2010-11-26 07:22:46 +00:00
int i;
unsigned checksum;
if ( attractloop )
{
Cvar_Set( "paused", "0" );
}
2010-11-26 07:22:46 +00:00
Com_Printf( "------- server initialization ------\n" );
2010-11-26 07:22:46 +00:00
Com_DPrintf( "SpawnServer: %s\n", server );
2010-11-26 07:22:46 +00:00
if ( sv.demofile )
{
FS_FCloseFile( (size_t) sv.demofile );
}
2010-11-26 07:22:46 +00:00
svs.spawncount++; /* any partially connected client will be restarted */
sv.state = ss_dead;
2010-11-26 07:22:46 +00:00
Com_SetServerState( sv.state );
2010-11-26 07:22:46 +00:00
/* wipe the entire per-level structure */
memset( &sv, 0, sizeof ( sv ) );
svs.realtime = 0;
sv.loadgame = loadgame;
sv.attractloop = attractloop;
2010-11-26 07:22:46 +00:00
/* save name for levels that don't set message */
strcpy( sv.configstrings [ CS_NAME ], server );
if ( Cvar_VariableValue( "deathmatch" ) )
{
2010-11-26 07:22:46 +00:00
sprintf( sv.configstrings [ CS_AIRACCEL ], "%g", sv_airaccelerate->value );
pm_airaccelerate = sv_airaccelerate->value;
}
else
{
2010-11-26 07:22:46 +00:00
strcpy( sv.configstrings [ CS_AIRACCEL ], "0" );
pm_airaccelerate = 0;
}
2010-11-26 07:22:46 +00:00
SZ_Init( &sv.multicast, sv.multicast_buf, sizeof ( sv.multicast_buf ) );
2010-11-26 07:22:46 +00:00
strcpy( sv.name, server );
2010-11-26 07:22:46 +00:00
/* leave slots at start for clients only */
for ( i = 0; i < maxclients->value; i++ )
{
2010-11-26 07:22:46 +00:00
/* needs to reconnect */
if ( svs.clients [ i ].state > cs_connected )
{
svs.clients [ i ].state = cs_connected;
}
svs.clients [ i ].lastframe = -1;
}
sv.time = 1000;
2010-11-26 07:22:46 +00:00
strcpy( sv.name, server );
strcpy( sv.configstrings [ CS_NAME ], server );
if ( serverstate != ss_game )
{
2010-11-26 07:22:46 +00:00
sv.models [ 1 ] = CM_LoadMap( "", false, &checksum ); /* no real map */
}
else
{
2010-11-26 07:22:46 +00:00
Com_sprintf( sv.configstrings [ CS_MODELS + 1 ],
sizeof ( sv.configstrings [ CS_MODELS + 1 ] ), "maps/%s.bsp", server );
sv.models [ 1 ] = CM_LoadMap( sv.configstrings [ CS_MODELS + 1 ], false, &checksum );
}
2010-11-26 07:22:46 +00:00
Com_sprintf( sv.configstrings [ CS_MAPCHECKSUM ], sizeof ( sv.configstrings [ CS_MAPCHECKSUM ] ),
"%i", checksum );
/* clear physics interaction links */
SV_ClearWorld();
for ( i = 1; i < CM_NumInlineModels(); i++ )
{
2010-11-26 07:22:46 +00:00
Com_sprintf( sv.configstrings [ CS_MODELS + 1 + i ], sizeof ( sv.configstrings [ CS_MODELS + 1 + i ] ),
"*%i", i );
sv.models [ i + 1 ] = CM_InlineModel( sv.configstrings [ CS_MODELS + 1 + i ] );
}
2010-11-26 07:22:46 +00:00
/* spawn the rest of the entities on the map */
sv.state = ss_loading;
2010-11-26 07:22:46 +00:00
Com_SetServerState( sv.state );
2010-11-26 07:22:46 +00:00
/* load and spawn all other entities */
ge->SpawnEntities( sv.name, CM_EntityString(), spawnpoint );
2010-11-26 07:22:46 +00:00
/* run two frames to allow everything to settle */
ge->RunFrame();
ge->RunFrame();
2010-11-26 07:22:46 +00:00
/* verify game didn't clobber important stuff */
if ( (int) checksum != atoi( sv.configstrings [ CS_MAPCHECKSUM ] ) )
{
Com_Error( ERR_DROP, "Game DLL corrupted server configstrings" );
}
2009-03-04 11:13:16 +00:00
2010-11-26 07:22:46 +00:00
/* all precaches are complete */
sv.state = serverstate;
2010-11-26 07:22:46 +00:00
Com_SetServerState( sv.state );
/* create a baseline for more efficient communications */
SV_CreateBaseline();
2010-11-26 07:22:46 +00:00
/* check for a savegame */
SV_CheckForSavegame();
2010-11-26 07:22:46 +00:00
/* set serverinfo variable */
Cvar_FullSet( "mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET );
Cvar_SetValue( "windowed_mouse", 1 );
2010-11-26 07:22:46 +00:00
Com_Printf( "------------------------------------\n\n" );
}
/*
2010-11-26 07:22:46 +00:00
* A brand new game has been started
*/
void
SV_InitGame ( void )
{
2010-11-26 07:22:46 +00:00
int i;
edict_t *ent;
char idmaster [ 32 ];
2010-11-26 07:22:46 +00:00
if ( svs.initialized )
{
2010-11-26 07:22:46 +00:00
/* cause any connected clients to reconnect */
SV_Shutdown( "Server restarted\n", true );
}
2010-11-26 07:22:46 +00:00
2009-03-04 11:13:16 +00:00
#ifndef DEDICATED_ONLY
else
{
2010-11-26 07:22:46 +00:00
/* make sure the client is down */
CL_Drop();
SCR_BeginLoadingPlaque();
}
2009-03-04 11:13:16 +00:00
#endif
2010-11-26 07:22:46 +00:00
/* get any latched variable changes (maxclients, etc) */
Cvar_GetLatchedVars();
svs.initialized = true;
2010-11-26 07:22:46 +00:00
if ( Cvar_VariableValue( "coop" ) && Cvar_VariableValue( "deathmatch" ) )
{
2010-11-26 07:22:46 +00:00
Com_Printf( "Deathmatch and Coop both set, disabling Coop\n" );
Cvar_FullSet( "coop", "0", CVAR_SERVERINFO | CVAR_LATCH );
}
2010-11-26 07:22:46 +00:00
/* dedicated servers can't be single player and are usually DM
so unless they explicity set coop, force it to deathmatch */
if ( dedicated->value )
{
2010-11-26 07:22:46 +00:00
if ( !Cvar_VariableValue( "coop" ) )
{
Cvar_FullSet( "deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH );
}
}
2010-11-26 07:22:46 +00:00
/* init clients */
if ( Cvar_VariableValue( "deathmatch" ) )
{
2010-11-26 07:22:46 +00:00
if ( maxclients->value <= 1 )
{
Cvar_FullSet( "maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH );
}
else if ( maxclients->value > MAX_CLIENTS )
{
Cvar_FullSet( "maxclients", va( "%i", MAX_CLIENTS ), CVAR_SERVERINFO | CVAR_LATCH );
}
}
2010-11-26 07:22:46 +00:00
else if ( Cvar_VariableValue( "coop" ) )
{
2010-11-26 07:22:46 +00:00
if ( ( maxclients->value <= 1 ) || ( maxclients->value > 4 ) )
{
Cvar_FullSet( "maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH );
}
}
2010-11-26 07:22:46 +00:00
else /* non-deathmatch, non-coop is one player */
{
2010-11-26 07:22:46 +00:00
Cvar_FullSet( "maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH );
}
svs.spawncount = rand();
2010-11-26 07:22:46 +00:00
svs.clients = Z_Malloc( sizeof ( client_t ) * maxclients->value );
svs.num_client_entities = maxclients->value * UPDATE_BACKUP * 64;
svs.client_entities = Z_Malloc( sizeof ( entity_state_t ) * svs.num_client_entities );
/* init network stuff */
NET_Config( ( maxclients->value > 1 ) );
2010-11-26 07:22:46 +00:00
/* heartbeats will always be sent to the id master */
svs.last_heartbeat = -99999; /* send immediately */
Com_sprintf( idmaster, sizeof ( idmaster ), "192.246.40.37:%i", PORT_MASTER );
NET_StringToAdr( idmaster, &master_adr [ 0 ] );
2010-11-26 07:22:46 +00:00
/* init game */
SV_InitGameProgs();
2010-11-26 07:22:46 +00:00
for ( i = 0; i < maxclients->value; i++ )
{
2010-11-26 07:22:46 +00:00
ent = EDICT_NUM( i + 1 );
ent->s.number = i + 1;
svs.clients [ i ].edict = ent;
memset( &svs.clients [ i ].lastcmd, 0, sizeof ( svs.clients [ i ].lastcmd ) );
}
}
/*
2010-11-26 07:22:46 +00:00
* 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 ( qboolean attractloop, char *levelstring, qboolean loadgame )
{
2010-11-26 07:22:46 +00:00
char level [ MAX_QPATH ];
char *ch;
int l;
char spawnpoint [ MAX_QPATH ];
sv.loadgame = loadgame;
sv.attractloop = attractloop;
2010-11-26 07:22:46 +00:00
if ( ( sv.state == ss_dead ) && !sv.loadgame )
{
SV_InitGame(); /* the game is just starting */
}
2010-11-26 07:22:46 +00:00
strcpy( level, levelstring );
2010-11-26 07:22:46 +00:00
/* if there is a + in the map, set nextserver to the remainder */
ch = strstr( level, "+" );
if ( ch )
{
*ch = 0;
2010-11-26 07:22:46 +00:00
Cvar_Set( "nextserver", va( "gamemap \"%s\"", ch + 1 ) );
}
else
2010-11-26 07:22:46 +00:00
{
Cvar_Set( "nextserver", "" );
}
/* hack for end game screen in coop mode */
if ( Cvar_VariableValue( "coop" ) && !Q_stricmp( level, "victory.pcx" ) )
{
Cvar_Set( "nextserver", "gamemap \"*base1\"" );
}
2010-11-26 07:22:46 +00:00
/* if there is a $, use the remainder as a spawnpoint */
ch = strstr( level, "$" );
2010-11-26 07:22:46 +00:00
if ( ch )
{
*ch = 0;
2010-11-26 07:22:46 +00:00
strcpy( spawnpoint, ch + 1 );
}
else
2010-11-26 07:22:46 +00:00
{
spawnpoint [ 0 ] = 0;
}
2010-11-26 07:22:46 +00:00
/* skip the end-of-unit flag if necessary */
if ( level [ 0 ] == '*' )
{
memmove( level, level + 1, strlen( level ) + 1 );
}
l = strlen( level );
2010-11-26 07:22:46 +00:00
if ( ( l > 4 ) && !strcmp( level + l - 4, ".cin" ) )
{
2010-11-26 07:22:46 +00:00
#ifndef DEDICATED_ONLY
SCR_BeginLoadingPlaque(); /* for local system */
2009-03-04 11:13:16 +00:00
#endif
2010-11-26 07:22:46 +00:00
SV_BroadcastCommand( "changing\n" );
SV_SpawnServer( level, spawnpoint, ss_cinematic, attractloop, loadgame );
}
2010-11-26 07:22:46 +00:00
else if ( ( l > 4 ) && !strcmp( level + l - 4, ".dm2" ) )
{
2009-03-04 11:13:16 +00:00
#ifndef DEDICATED_ONLY
2010-11-26 07:22:46 +00:00
SCR_BeginLoadingPlaque(); /* for local system */
2009-03-04 11:13:16 +00:00
#endif
2010-11-26 07:22:46 +00:00
SV_BroadcastCommand( "changing\n" );
SV_SpawnServer( level, spawnpoint, ss_demo, attractloop, loadgame );
}
2010-11-26 07:22:46 +00:00
else if ( ( l > 4 ) && !strcmp( level + l - 4, ".pcx" ) )
{
2009-03-04 11:13:16 +00:00
#ifndef DEDICATED_ONLY
2010-11-26 07:22:46 +00:00
SCR_BeginLoadingPlaque(); /* for local system */
2009-03-04 11:13:16 +00:00
#endif
2010-11-26 07:22:46 +00:00
SV_BroadcastCommand( "changing\n" );
SV_SpawnServer( level, spawnpoint, ss_pic, attractloop, loadgame );
}
else
{
2009-03-04 11:13:16 +00:00
#ifndef DEDICATED_ONLY
2010-11-26 07:22:46 +00:00
SCR_BeginLoadingPlaque(); /* for local system */
2009-03-04 11:13:16 +00:00
#endif
2010-11-26 07:22:46 +00:00
SV_BroadcastCommand( "changing\n" );
SV_SendClientMessages();
SV_SpawnServer( level, spawnpoint, ss_game, attractloop, loadgame );
Cbuf_CopyToDefer();
}
2010-11-26 07:22:46 +00:00
SV_BroadcastCommand( "reconnect\n" );
}
2010-11-26 07:22:46 +00:00