1 /*
2  * Copyright (C) 1997-2001 Id Software, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * 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 along with
16  * this program; if not, write to the Free Software Foundation, Inc., 59
17  * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  */
20 /* sv_game.c -- interface to the game dll */
21 
22 #include "server.h"
23 
24 game_export_t  *ge;
25 
26 
27 /*
28  * =============== PF_Unicast
29  *
30  * Sends the contents of the mutlicast buffer to a single client ===============
31  */
32 void
PF_Unicast(edict_t * ent,qboolean reliable)33 PF_Unicast(edict_t * ent, qboolean reliable)
34 {
35 	int		p;
36 	client_t       *client;
37 
38 	if (!ent)
39 		return;
40 
41 	p = NUM_FOR_EDICT(ent);
42 	if (p < 1 || p > maxclients->value)
43 		return;
44 
45 	client = svs.clients + (p - 1);
46 
47 	if (reliable)
48 		SZ_Write(&client->netchan.message, sv.multicast.data, sv.multicast.cursize);
49 	else
50 		SZ_Write(&client->datagram, sv.multicast.data, sv.multicast.cursize);
51 
52 	SZ_Clear(&sv.multicast);
53 }
54 
55 
56 /*
57  * =============== PF_dprintf
58  *
59  * Debug print to server console ===============
60  */
61 void
PF_dprintf(char * fmt,...)62 PF_dprintf(char *fmt,...)
63 {
64 	char		msg[1024];
65 	va_list		argptr;
66 
67 	va_start(argptr, fmt);
68 	vsnprintf(msg, sizeof(msg), fmt, argptr);
69 	va_end(argptr);
70 
71 	Com_Printf("%s", msg);
72 }
73 
74 
75 /*
76  * =============== PF_cprintf
77  *
78  * Print to a single client ===============
79  */
80 void
PF_cprintf(edict_t * ent,int level,char * fmt,...)81 PF_cprintf(edict_t * ent, int level, char *fmt,...)
82 {
83 	char		msg[1024];
84 	va_list		argptr;
85 	int		n;
86 
87 	n = 0;
88 	if (ent) {
89 		n = NUM_FOR_EDICT(ent);
90 		if (n < 1 || n > maxclients->value)
91 			Com_Error(ERR_DROP, "cprintf to a non-client");
92 	}
93 	va_start(argptr, fmt);
94 	vsnprintf(msg, sizeof(msg), fmt, argptr);
95 	va_end(argptr);
96 
97 	if (ent)
98 		SV_ClientPrintf(svs.clients + (n - 1), level, "%s", msg);
99 	else
100 		Com_Printf("%s", msg);
101 }
102 
103 
104 /*
105  * =============== PF_centerprintf
106  *
107  * centerprint to a single client ===============
108  */
109 void
PF_centerprintf(edict_t * ent,char * fmt,...)110 PF_centerprintf(edict_t * ent, char *fmt,...)
111 {
112 	char		msg[1024];
113 	va_list		argptr;
114 	int		n;
115 
116 	n = NUM_FOR_EDICT(ent);
117 	if (n < 1 || n > maxclients->value)
118 		return;		/* Com_Error (ERR_DROP, "centerprintf to a
119 				 * non-client"); */
120 
121 	va_start(argptr, fmt);
122 	vsnprintf(msg, sizeof(msg), fmt, argptr);
123 	va_end(argptr);
124 
125 	MSG_WriteByte(&sv.multicast, svc_centerprint);
126 	MSG_WriteString(&sv.multicast, msg);
127 	PF_Unicast(ent, true);
128 }
129 
130 
131 /*
132  * =============== PF_error
133  *
134  * Abort the server with a game error ===============
135  */
136 void
PF_error(char * fmt,...)137 PF_error(char *fmt,...)
138 {
139 	char		msg[1024];
140 	va_list		argptr;
141 
142 	va_start(argptr, fmt);
143 	vsnprintf(msg, sizeof(msg), fmt, argptr);
144 	va_end(argptr);
145 
146 	Com_Error(ERR_DROP, "Game Error: %s", msg);
147 }
148 
149 
150 /*
151  * ================= PF_setmodel
152  *
153  * Also sets mins and maxs for inline bmodels =================
154  */
155 void
PF_setmodel(edict_t * ent,char * name)156 PF_setmodel(edict_t * ent, char *name)
157 {
158 	int		i;
159 	cmodel_t       *mod;
160 
161 	if (!name)
162 		Com_Error(ERR_DROP, "PF_setmodel: NULL");
163 
164 	i = SV_ModelIndex(name);
165 
166 	/* ent->model = name; */
167 	ent->s.modelindex = i;
168 
169 	/* if it is an inline model, get the size information for it */
170 	if (name[0] == '*') {
171 		mod = CM_InlineModel(name);
172 		VectorCopy(mod->mins, ent->mins);
173 		VectorCopy(mod->maxs, ent->maxs);
174 		SV_LinkEdict(ent);
175 	}
176 }
177 
178 /*
179  * ===============
180  * PF_Configstring
181  * ===============
182  */
183 void
PF_Configstring(int pos,char * val)184 PF_Configstring(int pos, char *val)
185 {
186 	if (pos < 0 || pos >= MAX_CONFIGSTRINGS)
187 		Com_Error(ERR_DROP, "configstring: bad index %i\n", pos);
188 
189 	if (!val)
190 		val = "";
191 
192 	/* change the string in sv */
193 	strcpy(sv.configstrings[pos], val);
194 
195 
196 	if (sv.state != ss_loading) {	/* send the update to everyone */
197 		SZ_Clear(&sv.multicast);
198 		MSG_WriteChar(&sv.multicast, svc_configstring);
199 		MSG_WriteShort(&sv.multicast, pos);
200 		MSG_WriteString(&sv.multicast, val);
201 
202 		SV_Multicast(vec3_origin, MULTICAST_ALL_R);
203 	}
204 }
205 
206 void
PF_WriteChar(int c)207 PF_WriteChar(int c){
208 	MSG_WriteChar(&sv.multicast, c);
209 }
210 
211 void
PF_WriteByte(int c)212 PF_WriteByte(int c){
213 	MSG_WriteByte(&sv.multicast, c);
214 }
215 
216 void
PF_WriteShort(int c)217 PF_WriteShort(int c){
218 	MSG_WriteShort(&sv.multicast, c);
219 }
220 
221 void
PF_WriteLong(int c)222 PF_WriteLong(int c){
223 	MSG_WriteLong(&sv.multicast, c);
224 }
225 
226 void
PF_WriteFloat(float f)227 PF_WriteFloat(float f){
228 	MSG_WriteFloat(&sv.multicast, f);
229 }
230 
231 void
PF_WriteString(char * s)232 PF_WriteString(char *s){
233 	MSG_WriteString(&sv.multicast, s);
234 }
235 
236 void
PF_WritePos(vec3_t pos)237 PF_WritePos(vec3_t pos) {
238 	MSG_WritePos(&sv.multicast, pos);
239 }
240 
241 void
PF_WriteDir(vec3_t dir)242 PF_WriteDir(vec3_t dir) {
243 	MSG_WriteDir(&sv.multicast, dir);
244 }
245 
246 void
PF_WriteAngle(float f)247 PF_WriteAngle(float f){
248 	MSG_WriteAngle(&sv.multicast, f);
249 }
250 
251 
252 /*
253  * ================= PF_inPVS
254  *
255  * Also checks portalareas so that doors block sight =================
256  */
257 qboolean
PF_inPVS(vec3_t p1,vec3_t p2)258 PF_inPVS(vec3_t p1, vec3_t p2)
259 {
260 	int		leafnum;
261 	int		cluster;
262 	int		area1, area2;
263 	byte           *mask;
264 
265 	leafnum = CM_PointLeafnum(p1);
266 	cluster = CM_LeafCluster(leafnum);
267 	area1 = CM_LeafArea(leafnum);
268 	mask = CM_ClusterPVS(cluster);
269 
270 	leafnum = CM_PointLeafnum(p2);
271 	cluster = CM_LeafCluster(leafnum);
272 	area2 = CM_LeafArea(leafnum);
273 	if (mask && (!(mask[cluster >> 3] & (1 << (cluster & 7)))))
274 		return false;
275 	if (!CM_AreasConnected(area1, area2))
276 		return false;	/* a door blocks sight */
277 	return true;
278 }
279 
280 
281 /*
282  * ================= PF_inPHS
283  *
284  * Also checks portalareas so that doors block sound =================
285  */
286 qboolean
PF_inPHS(vec3_t p1,vec3_t p2)287 PF_inPHS(vec3_t p1, vec3_t p2)
288 {
289 	int		leafnum;
290 	int		cluster;
291 	int		area1, area2;
292 	byte           *mask;
293 
294 	leafnum = CM_PointLeafnum(p1);
295 	cluster = CM_LeafCluster(leafnum);
296 	area1 = CM_LeafArea(leafnum);
297 	mask = CM_ClusterPHS(cluster);
298 
299 	leafnum = CM_PointLeafnum(p2);
300 	cluster = CM_LeafCluster(leafnum);
301 	area2 = CM_LeafArea(leafnum);
302 	if (mask && (!(mask[cluster >> 3] & (1 << (cluster & 7)))))
303 		return false;	/* more than one bounce away */
304 	if (!CM_AreasConnected(area1, area2))
305 		return false;	/* a door blocks hearing */
306 
307 	return true;
308 }
309 
310 void
PF_StartSound(edict_t * entity,int channel,int sound_num,float volume,float attenuation,float timeofs)311 PF_StartSound(edict_t * entity, int channel, int sound_num, float volume,
312     float attenuation, float timeofs)
313 {
314 	if (!entity)
315 		return;
316 	SV_StartSound(NULL, entity, channel, sound_num, volume, attenuation, timeofs);
317 }
318 
319 /* ============================================== */
320 
321 /*
322  * =============== SV_ShutdownGameProgs
323  *
324  * Called when either the entire server is being killed, or it is changing to a
325  * different game directory. ===============
326  */
327 void
SV_ShutdownGameProgs(void)328 SV_ShutdownGameProgs(void)
329 {
330 	if (!ge)
331 		return;
332 	ge->Shutdown();
333 	Sys_UnloadGame();
334 	ge = NULL;
335 }
336 
337 /*
338  * ===============
339  * SV_InitGameProgs
340  *
341  * Init the game subsystem for a new map.
342  * ===============
343  */
344 void
SV_InitGameProgs(void)345 SV_InitGameProgs(void)
346 {
347 	game_import_t	import;
348 
349 	/* unload anything we have now */
350 	if (ge)
351 		SV_ShutdownGameProgs();
352 
353 
354 	/* load a new game dll */
355 	import.multicast = SV_Multicast;
356 	import.unicast = PF_Unicast;
357 	import.bprintf = SV_BroadcastPrintf;
358 	import.dprintf = PF_dprintf;
359 	import.cprintf = PF_cprintf;
360 	import.centerprintf = PF_centerprintf;
361 	import.error = PF_error;
362 
363 	import.linkentity = SV_LinkEdict;
364 	import.unlinkentity = SV_UnlinkEdict;
365 	import.BoxEdicts = SV_AreaEdicts;
366 	import.trace = SV_Trace;
367 	import.pointcontents = SV_PointContents;
368 	import.setmodel = PF_setmodel;
369 	import.inPVS = PF_inPVS;
370 	import.inPHS = PF_inPHS;
371 	import.Pmove = Pmove;
372 
373 	import.modelindex = SV_ModelIndex;
374 	import.soundindex = SV_SoundIndex;
375 	import.imageindex = SV_ImageIndex;
376 
377 	import.configstring = PF_Configstring;
378 	import.sound = PF_StartSound;
379 	import.positioned_sound = SV_StartSound;
380 
381 	import.WriteChar = PF_WriteChar;
382 	import.WriteByte = PF_WriteByte;
383 	import.WriteShort = PF_WriteShort;
384 	import.WriteLong = PF_WriteLong;
385 	import.WriteFloat = PF_WriteFloat;
386 	import.WriteString = PF_WriteString;
387 	import.WritePosition = PF_WritePos;
388 	import.WriteDir = PF_WriteDir;
389 	import.WriteAngle = PF_WriteAngle;
390 
391 	import.TagMalloc = Z_TagMalloc;
392 	import.TagFree = Z_Free;
393 	import.FreeTags = Z_FreeTags;
394 
395 	import.cvar = Cvar_Get;
396 	import.cvar_set = Cvar_Set;
397 	import.cvar_forceset = Cvar_ForceSet;
398 
399 	import.argc = Cmd_Argc;
400 	import.argv = Cmd_Argv;
401 	import.args = Cmd_Args;
402 	import.AddCommandString = Cbuf_AddText;
403 
404 	import.DebugGraph = SCR_DebugGraph;
405 	import.SetAreaPortalState = CM_SetAreaPortalState;
406 	import.AreasConnected = CM_AreasConnected;
407 
408 	ge = (game_export_t *) Sys_GetGameAPI(&import);
409 
410 	if (!ge)
411 		Com_Error(ERR_DROP, "failed to load game DLL");
412 	if (ge->apiversion != GAME_API_VERSION)
413 		Com_Error(ERR_DROP, "game is version %i, not %i", ge->apiversion,
414 		    GAME_API_VERSION);
415 
416 	ge->Init();
417 }
418