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