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