1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21 
22 #include "server.h"
23 
24 server_static_t svs;  // persistant server info
25 server_t sv;  // local server
26 
27 /*
28 SV_FindIndex
29 
30 */
SV_FindIndex(char * name,int start,int max,qboolean create)31 int SV_FindIndex(char *name, int start, int max, qboolean create){
32 	int i;
33 
34 	if(!name || !name[0])
35 		return 0;
36 
37 	for(i = 1; i < max && sv.configstrings[start + i][0]; i++)
38 		if(!strcmp(sv.configstrings[start + i], name))
39 			return i;
40 
41 	if(!create)
42 		return 0;
43 
44 	if(i == max)
45 		Com_Error(ERR_DROP, "*Index: overflow");
46 
47 	strncpy(sv.configstrings[start + i], name, sizeof(sv.configstrings[i]));
48 
49 	if(sv.state != ss_loading){  // send the update to everyone
50 		SZ_Clear(&sv.multicast);
51 		MSG_WriteChar(&sv.multicast, svc_configstring);
52 		MSG_WriteShort(&sv.multicast, start + i);
53 		MSG_WriteString(&sv.multicast, name);
54 		SV_Multicast(vec3_origin, MULTICAST_ALL_R);
55 	}
56 
57 	return i;
58 }
59 
60 
SV_ModelIndex(char * name)61 int SV_ModelIndex(char *name){
62 	return SV_FindIndex(name, CS_MODELS, MAX_MODELS, true);
63 }
64 
SV_SoundIndex(char * name)65 int SV_SoundIndex(char *name){
66 	return SV_FindIndex(name, CS_SOUNDS, MAX_SOUNDS, true);
67 }
68 
SV_ImageIndex(char * name)69 int SV_ImageIndex(char *name){
70 	return SV_FindIndex(name, CS_IMAGES, MAX_IMAGES, true);
71 }
72 
73 
74 /*
75 SV_CreateBaseline
76 
77 Entity baselines are used to compress the update messages
78 to the clients -- only the fields that differ from the
79 baseline will be transmitted
80 */
SV_CreateBaseline(void)81 void SV_CreateBaseline(void){
82 	edict_t *svent;
83 	int entnum;
84 
85 	for(entnum = 1; entnum < ge->num_edicts; entnum++){
86 		svent = EDICT_NUM(entnum);
87 		if(!svent->inuse)
88 			continue;
89 		if(!svent->s.modelindex && !svent->s.sound && !svent->s.effects)
90 			continue;
91 		svent->s.number = entnum;
92 
93 		// take current state as baseline
94 		VectorCopy(svent->s.origin, svent->s.old_origin);
95 		sv.baselines[entnum] = svent->s;
96 	}
97 }
98 
99 
100 /*
101 SV_CheckMap
102 
103 Ensures that map exists before attempting to spawn a server to it.
104 Returns -1 if the map does not exist, PROTOCOL_34 otherwise.
105 */
SV_CheckMap(char * name)106 int SV_CheckMap(char *name){
107 	char map[MAX_OSPATH];
108 	FILE *f;
109 
110 	Com_sprintf(map, sizeof(map), "maps/%s.bsp", name);
111 	FS_FOpenFile(map, &f);
112 
113 	if(!f){
114 		Com_Printf("Couldn't open %s\n", map);
115 		return -1;
116 	}
117 
118 	FS_FCloseFile(f);
119 	return PROTOCOL_34;
120 }
121 
122 
123 /*
124 SV_CheckDemo
125 
126 Attempts to open and peek into the specified demo file.  Returns
127 the protocol version of the file if it exists.  File is closed.
128 */
SV_CheckDemo(char * name)129 int SV_CheckDemo(char *name){
130 	char demo[MAX_OSPATH];
131 	byte buff[MAX_MSGLEN];
132 	int msglen, protocol;
133 	FILE *f;
134 
135 	Com_sprintf(demo, sizeof(demo), "demos/%s", name);
136 	FS_FOpenFile(demo, &f);
137 	if(!f){
138 		Com_Printf("Couldn't open %s\n", demo);
139 		return -1;
140 	}
141 
142 	fread(&msglen, 4, 1, f);
143 	msglen = LittleLong(msglen);
144 
145 	if(msglen == -1 || msglen > MAX_MSGLEN){
146 		Com_Printf("%s is not a demo\n", demo);
147 		return -1;
148 	}
149 
150 	fread(buff, msglen, 1, f);
151 	memcpy(&protocol, buff + 1, 4);
152 
153 	FS_FCloseFile(f);
154 	return LittleLong(protocol);
155 }
156 
157 
158 /*
159 SV_InitGame
160 
161 A brand new game has been started
162 */
SV_InitGame()163 void SV_InitGame(){
164 	int i;
165 	edict_t *ent;
166 	char idmaster[32];
167 
168 	svs.initialized = true;
169 
170 	if(coop->value && deathmatch->value){
171 		Com_Printf("Deathmatch and Coop both set, disabling Coop.\n");
172 		Cvar_FullSet("coop", "0", CVAR_SERVERINFO | CVAR_LATCH);
173 	}
174 
175 	// dedicated servers can't be single player and are usually dm
176 	// so unless they explicity set coop, force it to deathmatch
177 	if(dedicated->value && !coop->value){
178 		Cvar_FullSet("deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH);
179 	}
180 
181 	// init clients
182 	if(deathmatch->value){
183 		if(maxclients->value <= 1)
184 			Cvar_FullSet("maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH);
185 		else if(maxclients->value > MAX_CLIENTS)
186 			Cvar_FullSet("maxclients", va("%i", MAX_CLIENTS), CVAR_SERVERINFO | CVAR_LATCH);
187 	} else if(coop->value){
188 		if(maxclients->value <= 1 || maxclients->value > 4)
189 			Cvar_FullSet("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
190 	} else {  // non-deathmatch, non-coop is one player
191 		Cvar_FullSet("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
192 	}
193 
194 	svs.spawncount = rand();
195 	svs.clients = Z_Malloc(sizeof(client_t) * maxclients->value);
196 	svs.num_client_entities = maxclients->value * UPDATE_BACKUP * 64;
197 	svs.client_entities = Z_Malloc(sizeof(entity_state_t) * svs.num_client_entities);
198 
199 	// init network stuff
200 	NET_Config((maxclients->value > 1));
201 
202 	// heartbeats will always be sent to the id master
203 	svs.last_heartbeat = -99999;  // send immediately
204 	Com_sprintf(idmaster, sizeof(idmaster), "192.246.40.37:%i", PORT_MASTER);
205 	NET_StringToAdr(idmaster, &master_adr[0]);
206 
207 	SV_InitGameProgs();
208 
209 	for(i = 0; i < maxclients->value; i++){
210 		ent = EDICT_NUM(i + 1);
211 		ent->s.number = i + 1;
212 		svs.clients[i].edict = ent;
213 		memset(&svs.clients[i].lastcmd, 0, sizeof(svs.clients[i].lastcmd));
214 	}
215 }
216 
217 
218 /*
219 SV_SpawnServer
220 
221 Change the server to a new map, taking all connected clients along with it.
222 The serverstate parameter must either be ss_game or ss_demo.  See SV_Map.
223 */
SV_SpawnServer(char * server,char * spawnpoint,server_state_t serverstate,int protocol)224 void SV_SpawnServer(char *server, char *spawnpoint,
225 		server_state_t serverstate, int protocol){
226 
227 	int i;
228 	unsigned checksum;
229 	qboolean hardreconnect, initgame, changing;
230 
231 	Com_Printf("Server initialization..\n");
232 
233 	hardreconnect = protocol != sv.protocol;
234 	initgame = changing = false;
235 
236 	if(!svs.initialized || Cvar_UpdateLatchedVars()){  // need reconnect
237 		SV_Shutdown("Server restarting..\n", true, hardreconnect);
238 		SV_InitGame();  // init clients, game progs, etc
239 		initgame = true;
240 	}
241 	else {  // server was initialized and no latched changes
242 		if(hardreconnect)  // protocol has changed
243 			SV_BroadcastCommand("reconnect\n");
244 		else {
245 			SV_BroadcastCommand("changing\n");
246 			changing = true;
247 		}
248 		SV_SendClientMessages();
249 	}
250 
251 	if(sv.demofile)
252 		fclose(sv.demofile);
253 
254 	svs.spawncount++;  // any partially connected client will be restarted
255 	svs.realtime = 0;
256 
257 	sv.state = ss_dead;
258 	Com_SetServerState(sv.state);
259 
260 	// wipe the entire per-level structure
261 	memset(&sv, 0, sizeof(sv));
262 
263 	if(serverstate == ss_demo || deathmatch->value)
264 		Cvar_Set("paused", "0");
265 
266 	if(deathmatch->value){
267 		sprintf(sv.configstrings[CS_AIRACCEL], "%g", sv_airaccelerate->value);
268 		pm_airaccelerate = sv_airaccelerate->value;
269 	} else {
270 		strcpy(sv.configstrings[CS_AIRACCEL], "0");
271 		pm_airaccelerate = 0;
272 	}
273 
274 	SZ_Init(&sv.multicast, sv.multicast_buf, sizeof(sv.multicast_buf));
275 
276 	// leave slots at start for clients only
277 	for(i = 0; i < maxclients->value; i++){
278 		// needs to reconnect
279 		if(svs.clients[i].state > cs_connected)
280 			svs.clients[i].state = cs_connected;
281 		svs.clients[i].lastframe = -1;
282 	}
283 
284 	sv.time = 1000;  // set time
285 
286 	sv.protocol = protocol;  // force update cvar
287 	Cvar_ForceSet("protocol", va("%d", sv.protocol));
288 
289 	// save name for levels that don't set message
290 	strcpy(sv.name, server);
291 	strcpy(sv.configstrings[CS_NAME], server);
292 
293 	if(serverstate == ss_demo){  // playing a demo, no map on the server
294 		sv.models[1] = CM_LoadMap("", false, &checksum);
295 	} else {  // playing a game
296 		Com_sprintf(sv.configstrings[CS_MODELS + 1], MAX_QPATH, "maps/%s.bsp", server);
297 		sv.models[1] = CM_LoadMap(sv.configstrings[CS_MODELS + 1], false, &checksum);
298 	}
299 	Com_sprintf(sv.configstrings[CS_MAPCHECKSUM], MAX_QPATH, "%i", checksum);
300 
301 	// clear physics interaction links
302 	SV_ClearWorld();
303 
304 	for(i = 1; i < CM_NumInlineModels(); i++){
305 		Com_sprintf(sv.configstrings[CS_MODELS + 1 + i], MAX_QPATH, "*%i", i);
306 		sv.models[i + 1] = CM_InlineModel(sv.configstrings[CS_MODELS + 1 + i]);
307 	}
308 
309 	if(serverstate == ss_game){  // load and spawn all other entities
310 
311 		sv.state = ss_loading;
312 		Com_SetServerState(sv.state);
313 
314 		ge->SpawnEntities(sv.name, CM_EntityString(), spawnpoint);
315 
316 		ge->RunFrame();  // run two frames to allow everything to settle
317 		ge->RunFrame();
318 
319 		// create a baseline for more efficient communications
320 		SV_CreateBaseline();
321 	}
322 
323 	// all precaches are complete
324 	sv.state = serverstate;
325 	Com_SetServerState(sv.state);
326 
327 	// set serverinfo variable
328 	Cvar_FullSet("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET);
329 
330 	if(!initgame && hardreconnect){  // zombie clients so they may reconnect
331 		for(i = 0; i < maxclients->value; i++){
332 			if(svs.clients[i].state){
333 				svs.clients[i].state = cs_zombie;
334 			}
335 		}
336 	}
337 	else if(changing){  // tell clients to reconnect
338 		SV_BroadcastCommand("reconnect\n");
339 		SV_SendClientMessages();
340 	}
341 
342 	Com_Printf("Server initialized.\n");
343 }
344 
345 
346 /*
347 SV_Map
348 
349   To be backwards compatible, Quetoo supports the full syntax:
350 
351   map [*]<map>$<spawnpoint>+<nextserver>
352 
353   However, if a +nextserver is present, it will be immediately
354   skipped to, as Quetoo does not support cinematics.  String
355   may also specify a .dm2.
356 
357 */
SV_Map(char * levelstring)358 void SV_Map(char *levelstring){
359 	char level[MAX_QPATH];
360 	char spawnpoint[MAX_QPATH];
361 	server_state_t state;
362 	int i, protocol;
363 	char *ch;
364 
365 	strncpy(level, levelstring, sizeof(level));  // normally this is adequate, but..
366 
367 	// if there is a + in the map, skip the antecedent and jump to .bsp
368 	if((ch = strstr(levelstring, "+")))
369 		strncpy(level, ch + 1, sizeof(level));
370 
371 	// if there is a $, use the remainder as a spawnpoint
372 	if((ch = strstr(level, "$"))){
373 		strcpy(spawnpoint, ch + 1);
374 		*ch = 0;  // and truncate level
375 	} else
376 		spawnpoint[0] = 0;
377 
378 	// skip the end-of-unit flag if necessary
379 	if(level[0] == '*')
380 		strcpy(level, level + 1);
381 
382 	// hack for end game screen in coop mode
383 	if(coop->value && !strcmp(levelstring, "victory.pcx"))
384 		strcpy(level, "base1");
385 
386 	i = strlen(level);
387 	state = i > 4 && !strcasecmp(level + i - 4, ".dm2") ? ss_demo : ss_game;
388 
389 	if(state == ss_demo){  // resolve next protocol to determine reconnect
390 		protocol = SV_CheckDemo(level);
391 	} else protocol = SV_CheckMap(level);
392 
393 	if(protocol == -1)  // demo or map file didn't exist
394 		return;
395 
396 	CL_Drop();  // make sure local client drops
397 
398 	Cbuf_CopyToDefer();
399 	SV_SpawnServer(level, spawnpoint, state, protocol);
400 }
401