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