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 */
SV_FindIndex(char * name,int start,int max,qboolean create)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 
SV_ModelIndex(char * name)64 int SV_ModelIndex (char *name)
65 {
66 	return SV_FindIndex (name, CS_MODELS, MAX_MODELS, true);
67 }
68 
SV_SoundIndex(char * name)69 int SV_SoundIndex (char *name)
70 {
71 	return SV_FindIndex (name, CS_SOUNDS, MAX_SOUNDS, true);
72 }
73 
SV_ImageIndex(char * name)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 */
SV_CreateBaseline(void)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 */
SV_CheckForSavegame(void)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 */
SV_SpawnServer(char * server,char * spawnpoint,server_state_t serverstate,qboolean attractloop,qboolean loadgame)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 */
SV_InitGame(void)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 */
SV_Map(qboolean attractloop,char * levelstring,qboolean loadgame)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 }
466