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