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