Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

sv_init.c (11080B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 
     21 #include "server.h"
     22 
     23 server_static_t	svs;				// persistant server info
     24 server_t		sv;					// local server
     25 
     26 /*
     27 ================
     28 SV_FindIndex
     29 
     30 ================
     31 */
     32 int SV_FindIndex (char *name, int start, int max, qboolean create)
     33 {
     34 	int		i;
     35 	
     36 	if (!name || !name[0])
     37 		return 0;
     38 
     39 	for (i=1 ; i<max && sv.configstrings[start+i][0] ; i++)
     40 		if (!strcmp(sv.configstrings[start+i], name))
     41 			return i;
     42 
     43 	if (!create)
     44 		return 0;
     45 
     46 	if (i == max)
     47 		Com_Error (ERR_DROP, "*Index: overflow");
     48 
     49 	strncpy (sv.configstrings[start+i], name, sizeof(sv.configstrings[i]));
     50 
     51 	if (sv.state != ss_loading)
     52 	{	// send the update to everyone
     53 		SZ_Clear (&sv.multicast);
     54 		MSG_WriteChar (&sv.multicast, svc_configstring);
     55 		MSG_WriteShort (&sv.multicast, start+i);
     56 		MSG_WriteString (&sv.multicast, name);
     57 		SV_Multicast (vec3_origin, MULTICAST_ALL_R);
     58 	}
     59 
     60 	return i;
     61 }
     62 
     63 
     64 int SV_ModelIndex (char *name)
     65 {
     66 	return SV_FindIndex (name, CS_MODELS, MAX_MODELS, true);
     67 }
     68 
     69 int SV_SoundIndex (char *name)
     70 {
     71 	return SV_FindIndex (name, CS_SOUNDS, MAX_SOUNDS, true);
     72 }
     73 
     74 int SV_ImageIndex (char *name)
     75 {
     76 	return SV_FindIndex (name, CS_IMAGES, MAX_IMAGES, true);
     77 }
     78 
     79 
     80 /*
     81 ================
     82 SV_CreateBaseline
     83 
     84 Entity baselines are used to compress the update messages
     85 to the clients -- only the fields that differ from the
     86 baseline will be transmitted
     87 ================
     88 */
     89 void SV_CreateBaseline (void)
     90 {
     91 	edict_t			*svent;
     92 	int				entnum;	
     93 
     94 	for (entnum = 1; entnum < ge->num_edicts ; entnum++)
     95 	{
     96 		svent = EDICT_NUM(entnum);
     97 		if (!svent->inuse)
     98 			continue;
     99 		if (!svent->s.modelindex && !svent->s.sound && !svent->s.effects)
    100 			continue;
    101 		svent->s.number = entnum;
    102 
    103 		//
    104 		// take current state as baseline
    105 		//
    106 		VectorCopy (svent->s.origin, svent->s.old_origin);
    107 		sv.baselines[entnum] = svent->s;
    108 	}
    109 }
    110 
    111 
    112 /*
    113 =================
    114 SV_CheckForSavegame
    115 =================
    116 */
    117 void SV_CheckForSavegame (void)
    118 {
    119 	char		name[MAX_OSPATH];
    120 	FILE		*f;
    121 	int			i;
    122 
    123 	if (sv_noreload->value)
    124 		return;
    125 
    126 	if (Cvar_VariableValue ("deathmatch"))
    127 		return;
    128 
    129 	Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
    130 	f = fopen (name, "rb");
    131 	if (!f)
    132 		return;		// no savegame
    133 
    134 	fclose (f);
    135 
    136 	SV_ClearWorld ();
    137 
    138 	// get configstrings and areaportals
    139 	SV_ReadLevelFile ();
    140 
    141 	if (!sv.loadgame)
    142 	{	// coming back to a level after being in a different
    143 		// level, so run it for ten seconds
    144 
    145 		// rlava2 was sending too many lightstyles, and overflowing the
    146 		// reliable data. temporarily changing the server state to loading
    147 		// prevents these from being passed down.
    148 		server_state_t		previousState;		// PGM
    149 
    150 		previousState = sv.state;				// PGM
    151 		sv.state = ss_loading;					// PGM
    152 		for (i=0 ; i<100 ; i++)
    153 			ge->RunFrame ();
    154 
    155 		sv.state = previousState;				// PGM
    156 	}
    157 }
    158 
    159 
    160 /*
    161 ================
    162 SV_SpawnServer
    163 
    164 Change the server to a new map, taking all connected
    165 clients along with it.
    166 
    167 ================
    168 */
    169 void SV_SpawnServer (char *server, char *spawnpoint, server_state_t serverstate, qboolean attractloop, qboolean loadgame)
    170 {
    171 	int			i;
    172 	unsigned	checksum;
    173 
    174 	if (attractloop)
    175 		Cvar_Set ("paused", "0");
    176 
    177 	Com_Printf ("------- Server Initialization -------\n");
    178 
    179 	Com_DPrintf ("SpawnServer: %s\n",server);
    180 	if (sv.demofile)
    181 		fclose (sv.demofile);
    182 
    183 	svs.spawncount++;		// any partially connected client will be
    184 							// restarted
    185 	sv.state = ss_dead;
    186 	Com_SetServerState (sv.state);
    187 
    188 	// wipe the entire per-level structure
    189 	memset (&sv, 0, sizeof(sv));
    190 	svs.realtime = 0;
    191 	sv.loadgame = loadgame;
    192 	sv.attractloop = attractloop;
    193 
    194 	// save name for levels that don't set message
    195 	strcpy (sv.configstrings[CS_NAME], server);
    196 	if (Cvar_VariableValue ("deathmatch"))
    197 	{
    198 		sprintf(sv.configstrings[CS_AIRACCEL], "%g", sv_airaccelerate->value);
    199 		pm_airaccelerate = sv_airaccelerate->value;
    200 	}
    201 	else
    202 	{
    203 		strcpy(sv.configstrings[CS_AIRACCEL], "0");
    204 		pm_airaccelerate = 0;
    205 	}
    206 
    207 	SZ_Init (&sv.multicast, sv.multicast_buf, sizeof(sv.multicast_buf));
    208 
    209 	strcpy (sv.name, server);
    210 
    211 	// leave slots at start for clients only
    212 	for (i=0 ; i<maxclients->value ; i++)
    213 	{
    214 		// needs to reconnect
    215 		if (svs.clients[i].state > cs_connected)
    216 			svs.clients[i].state = cs_connected;
    217 		svs.clients[i].lastframe = -1;
    218 	}
    219 
    220 	sv.time = 1000;
    221 	
    222 	strcpy (sv.name, server);
    223 	strcpy (sv.configstrings[CS_NAME], server);
    224 
    225 	if (serverstate != ss_game)
    226 	{
    227 		sv.models[1] = CM_LoadMap ("", false, &checksum);	// no real map
    228 	}
    229 	else
    230 	{
    231 		Com_sprintf (sv.configstrings[CS_MODELS+1],sizeof(sv.configstrings[CS_MODELS+1]),
    232 			"maps/%s.bsp", server);
    233 		sv.models[1] = CM_LoadMap (sv.configstrings[CS_MODELS+1], false, &checksum);
    234 	}
    235 	Com_sprintf (sv.configstrings[CS_MAPCHECKSUM],sizeof(sv.configstrings[CS_MAPCHECKSUM]),
    236 		"%i", checksum);
    237 
    238 	//
    239 	// clear physics interaction links
    240 	//
    241 	SV_ClearWorld ();
    242 	
    243 	for (i=1 ; i< CM_NumInlineModels() ; i++)
    244 	{
    245 		Com_sprintf (sv.configstrings[CS_MODELS+1+i], sizeof(sv.configstrings[CS_MODELS+1+i]),
    246 			"*%i", i);
    247 		sv.models[i+1] = CM_InlineModel (sv.configstrings[CS_MODELS+1+i]);
    248 	}
    249 
    250 	//
    251 	// spawn the rest of the entities on the map
    252 	//	
    253 
    254 	// precache and static commands can be issued during
    255 	// map initialization
    256 	sv.state = ss_loading;
    257 	Com_SetServerState (sv.state);
    258 
    259 	// load and spawn all other entities
    260 	ge->SpawnEntities ( sv.name, CM_EntityString(), spawnpoint );
    261 
    262 	// run two frames to allow everything to settle
    263 	ge->RunFrame ();
    264 	ge->RunFrame ();
    265 
    266 	// all precaches are complete
    267 	sv.state = serverstate;
    268 	Com_SetServerState (sv.state);
    269 	
    270 	// create a baseline for more efficient communications
    271 	SV_CreateBaseline ();
    272 
    273 	// check for a savegame
    274 	SV_CheckForSavegame ();
    275 
    276 	// set serverinfo variable
    277 	Cvar_FullSet ("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET);
    278 
    279 	Com_Printf ("-------------------------------------\n");
    280 }
    281 
    282 /*
    283 ==============
    284 SV_InitGame
    285 
    286 A brand new game has been started
    287 ==============
    288 */
    289 void SV_InitGame (void)
    290 {
    291 	int		i;
    292 	edict_t	*ent;
    293 	char	idmaster[32];
    294 
    295 	if (svs.initialized)
    296 	{
    297 		// cause any connected clients to reconnect
    298 		SV_Shutdown ("Server restarted\n", true);
    299 	}
    300 	else
    301 	{
    302 		// make sure the client is down
    303 		CL_Drop ();
    304 		SCR_BeginLoadingPlaque ();
    305 	}
    306 
    307 	// get any latched variable changes (maxclients, etc)
    308 	Cvar_GetLatchedVars ();
    309 
    310 	svs.initialized = true;
    311 
    312 	if (Cvar_VariableValue ("coop") && Cvar_VariableValue ("deathmatch"))
    313 	{
    314 		Com_Printf("Deathmatch and Coop both set, disabling Coop\n");
    315 		Cvar_FullSet ("coop", "0",  CVAR_SERVERINFO | CVAR_LATCH);
    316 	}
    317 
    318 	// dedicated servers are can't be single player and are usually DM
    319 	// so unless they explicity set coop, force it to deathmatch
    320 	if (dedicated->value)
    321 	{
    322 		if (!Cvar_VariableValue ("coop"))
    323 			Cvar_FullSet ("deathmatch", "1",  CVAR_SERVERINFO | CVAR_LATCH);
    324 	}
    325 
    326 	// init clients
    327 	if (Cvar_VariableValue ("deathmatch"))
    328 	{
    329 		if (maxclients->value <= 1)
    330 			Cvar_FullSet ("maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH);
    331 		else if (maxclients->value > MAX_CLIENTS)
    332 			Cvar_FullSet ("maxclients", va("%i", MAX_CLIENTS), CVAR_SERVERINFO | CVAR_LATCH);
    333 	}
    334 	else if (Cvar_VariableValue ("coop"))
    335 	{
    336 		if (maxclients->value <= 1 || maxclients->value > 4)
    337 			Cvar_FullSet ("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
    338 #ifdef COPYPROTECT
    339 		if (!sv.attractloop && !dedicated->value)
    340 			Sys_CopyProtect ();
    341 #endif
    342 	}
    343 	else	// non-deathmatch, non-coop is one player
    344 	{
    345 		Cvar_FullSet ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
    346 #ifdef COPYPROTECT
    347 		if (!sv.attractloop)
    348 			Sys_CopyProtect ();
    349 #endif
    350 	}
    351 
    352 	svs.spawncount = rand();
    353 	svs.clients = Z_Malloc (sizeof(client_t)*maxclients->value);
    354 	svs.num_client_entities = maxclients->value*UPDATE_BACKUP*64;
    355 	svs.client_entities = Z_Malloc (sizeof(entity_state_t)*svs.num_client_entities);
    356 
    357 	// init network stuff
    358 	NET_Config ( (maxclients->value > 1) );
    359 
    360 	// heartbeats will always be sent to the id master
    361 	svs.last_heartbeat = -99999;		// send immediately
    362 	Com_sprintf(idmaster, sizeof(idmaster), "192.246.40.37:%i", PORT_MASTER);
    363 	NET_StringToAdr (idmaster, &master_adr[0]);
    364 
    365 	// init game
    366 	SV_InitGameProgs ();
    367 	for (i=0 ; i<maxclients->value ; i++)
    368 	{
    369 		ent = EDICT_NUM(i+1);
    370 		ent->s.number = i+1;
    371 		svs.clients[i].edict = ent;
    372 		memset (&svs.clients[i].lastcmd, 0, sizeof(svs.clients[i].lastcmd));
    373 	}
    374 }
    375 
    376 
    377 /*
    378 ======================
    379 SV_Map
    380 
    381   the full syntax is:
    382 
    383   map [*]<map>$<startspot>+<nextserver>
    384 
    385 command from the console or progs.
    386 Map can also be a.cin, .pcx, or .dm2 file
    387 Nextserver is used to allow a cinematic to play, then proceed to
    388 another level:
    389 
    390 	map tram.cin+jail_e3
    391 ======================
    392 */
    393 void SV_Map (qboolean attractloop, char *levelstring, qboolean loadgame)
    394 {
    395 	char	level[MAX_QPATH];
    396 	char	*ch;
    397 	int		l;
    398 	char	spawnpoint[MAX_QPATH];
    399 
    400 	sv.loadgame = loadgame;
    401 	sv.attractloop = attractloop;
    402 
    403 	if (sv.state == ss_dead && !sv.loadgame)
    404 		SV_InitGame ();	// the game is just starting
    405 
    406 	strcpy (level, levelstring);
    407 
    408 	// if there is a + in the map, set nextserver to the remainder
    409 	ch = strstr(level, "+");
    410 	if (ch)
    411 	{
    412 		*ch = 0;
    413 			Cvar_Set ("nextserver", va("gamemap \"%s\"", ch+1));
    414 	}
    415 	else
    416 		Cvar_Set ("nextserver", "");
    417 
    418 	//ZOID special hack for end game screen in coop mode
    419 	if (Cvar_VariableValue ("coop") && !Q_stricmp(level, "victory.pcx"))
    420 		Cvar_Set ("nextserver", "gamemap \"*base1\"");
    421 
    422 	// if there is a $, use the remainder as a spawnpoint
    423 	ch = strstr(level, "$");
    424 	if (ch)
    425 	{
    426 		*ch = 0;
    427 		strcpy (spawnpoint, ch+1);
    428 	}
    429 	else
    430 		spawnpoint[0] = 0;
    431 
    432 	// skip the end-of-unit flag if necessary
    433 	if (level[0] == '*')
    434 		strcpy (level, level+1);
    435 
    436 	l = strlen(level);
    437 	if (l > 4 && !strcmp (level+l-4, ".cin") )
    438 	{
    439 		SCR_BeginLoadingPlaque ();			// for local system
    440 		SV_BroadcastCommand ("changing\n");
    441 		SV_SpawnServer (level, spawnpoint, ss_cinematic, attractloop, loadgame);
    442 	}
    443 	else if (l > 4 && !strcmp (level+l-4, ".dm2") )
    444 	{
    445 		SCR_BeginLoadingPlaque ();			// for local system
    446 		SV_BroadcastCommand ("changing\n");
    447 		SV_SpawnServer (level, spawnpoint, ss_demo, attractloop, loadgame);
    448 	}
    449 	else if (l > 4 && !strcmp (level+l-4, ".pcx") )
    450 	{
    451 		SCR_BeginLoadingPlaque ();			// for local system
    452 		SV_BroadcastCommand ("changing\n");
    453 		SV_SpawnServer (level, spawnpoint, ss_pic, attractloop, loadgame);
    454 	}
    455 	else
    456 	{
    457 		SCR_BeginLoadingPlaque ();			// for local system
    458 		SV_BroadcastCommand ("changing\n");
    459 		SV_SendClientMessages ();
    460 		SV_SpawnServer (level, spawnpoint, ss_game, attractloop, loadgame);
    461 		Cbuf_CopyToDefer ();
    462 	}
    463 
    464 	SV_BroadcastCommand ("reconnect\n");
    465 }