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 #include "mvd.h"
24
25 game_export_t *ge;
26
27
28 /*
29 ===============
30 PF_Unicast
31
32 Sends the contents of the mutlicast buffer to a single client.
33 Archived in MVD stream.
34 ===============
35 */
PF_Unicast(edict_t * ent,qboolean reliable)36 void PF_Unicast( edict_t *ent, qboolean reliable ) {
37 int p;
38 client_t *client;
39 int flags;
40 int clientNum;
41
42 if( !ent )
43 return;
44
45 p = NUM_FOR_EDICT( ent );
46 if( p < 1 || p > sv_maxclients->integer )
47 return;
48
49 clientNum = p - 1;
50 client = svs.clientpool + clientNum;
51
52 #if 0
53 // HACK: fixes 'anti-votekick' exploit
54 if( msg_write.data[0] == svc_disconnect ) {
55 SV_RemoveClient( client );
56 }
57 #endif
58
59 flags = 0;
60 if( reliable ) {
61 flags |= MSG_RELIABLE;
62 }
63
64 clientNum = ( reliable << 7 ) | ( clientNum & 0x7F );
65
66 if( client->protocol == PROTOCOL_VERSION_MVD ) {
67 if( msg_write.data[0] == svc_stufftext ) {
68 SV_ClientAddMessage( client, flags );
69 } else {
70 client->AddUnicast( client, clientNum );
71 SZ_Clear( &msg_write );
72 }
73 return;
74 }
75
76 SV_ClientAddMessage( client, flags );
77
78 /* dump to demofile */
79 MVD_RecUnicast( clientNum );
80
81 /* send to all MVD clients */
82 FOR_EACH_CLIENT( client ) {
83 if( client->protocol != PROTOCOL_VERSION_MVD ) {
84 continue;
85 }
86 if( client->state < cs_connected ) {
87 continue;
88 }
89 client->AddUnicast( client, clientNum );
90 }
91
92 SZ_Clear( &msg_write );
93
94 }
95
96 /*
97 =================
98 PF_bprintf
99
100 Sends text to all active clients.
101 Archived in MVD stream.
102 =================
103 */
PF_bprintf(int level,const char * fmt,...)104 void PF_bprintf( int level, const char *fmt, ... ) {
105 va_list argptr;
106 char string[MAX_STRING_CHARS];
107 client_t *client;
108 int i;
109
110 va_start( argptr, fmt );
111 Q_vsnprintf( string, sizeof( string ), fmt, argptr );
112 va_end( argptr );
113
114 MSG_WriteByte( svc_print );
115 MSG_WriteByte( level );
116 MSG_WriteString( string );
117
118 // echo to console
119 if( dedicated->integer ) {
120 // mask off high bits
121 for( i = 0; string[i]; i++ )
122 string[i] &= 127;
123 Com_Printf( "%s", string );
124 }
125
126 FOR_EACH_CLIENT( client ) {
127 if( client->state != cs_spawned )
128 continue;
129 if( client->protocol == PROTOCOL_VERSION_MVD ) {
130 client->AddMulticast( client, NULL, MULTICAST_ALL_R );
131 continue;
132 }
133 if( level >= client->messagelevel ) {
134 SV_ClientAddMessage( client, MSG_RELIABLE );
135 }
136 }
137
138 MVD_RecMulticast( vec3_origin, MULTICAST_ALL_R );
139
140 SZ_Clear( &msg_write );
141 }
142
143
144 /*
145 ===============
146 PF_dprintf
147
148 Debug print to server console.
149 ===============
150 */
PF_dprintf(const char * fmt,...)151 void PF_dprintf( const char *fmt, ... ) {
152 char msg[MAXPRINTMSG];
153 va_list argptr;
154
155 va_start( argptr, fmt );
156 Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
157 va_end( argptr );
158
159 Com_Printf( "%s", msg );
160 }
161
162
163 /*
164 ===============
165 PF_cprintf
166
167 Print to a single client if the level passes.
168 Archived in MVD stream.
169 ===============
170 */
PF_cprintf(edict_t * ent,int level,const char * fmt,...)171 void PF_cprintf( edict_t *ent, int level, const char *fmt, ... ) {
172 char msg[MAX_STRING_CHARS];
173 va_list argptr;
174 int clientNum;
175 client_t *client;
176
177 va_start( argptr, fmt );
178 Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
179 va_end( argptr );
180
181 if( !ent ) {
182 Com_Printf( "%s", msg );
183 return;
184 }
185
186 clientNum = NUM_FOR_EDICT( ent ) - 1;
187 if( clientNum < 0 || clientNum >= sv_maxclients->integer )
188 Com_Error( ERR_DROP, "cprintf to a non-client" );
189
190 client = svs.clientpool + clientNum;
191
192 MSG_WriteByte( svc_print );
193 MSG_WriteByte( level );
194 MSG_WriteString( msg );
195
196 clientNum = ( 1 << 7 ) | ( clientNum & 0x7F );
197
198 if( client->protocol == PROTOCOL_VERSION_MVD ) {
199 client->AddUnicast( client, clientNum );
200 SZ_Clear( &msg_write );
201 return;
202 }
203
204 if( level >= client->messagelevel ) {
205 SV_ClientAddMessage( client, MSG_RELIABLE );
206 }
207
208 /* dump to demofile */
209 MVD_RecUnicast( clientNum );
210
211 /* send to all MVD clients */
212 FOR_EACH_CLIENT( client ) {
213 if( client->protocol != PROTOCOL_VERSION_MVD ) {
214 continue;
215 }
216 if( client->state < cs_connected ) {
217 continue;
218 }
219 client->AddUnicast( client, clientNum );
220 }
221
222 SZ_Clear( &msg_write );
223 }
224
225
226 /*
227 ===============
228 PF_centerprintf
229
230 Centerprint to a single client.
231 Archived in MVD stream.
232 ===============
233 */
PF_centerprintf(edict_t * ent,const char * fmt,...)234 void PF_centerprintf (edict_t *ent, const char *fmt, ...)
235 {
236 char msg[MAX_STRING_CHARS];
237 va_list argptr;
238 int n;
239
240 n = NUM_FOR_EDICT(ent);
241 if (n < 1 || n > sv_maxclients->integer)
242 return;
243
244 va_start (argptr,fmt);
245 Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
246 va_end (argptr);
247
248 MSG_WriteByte (svc_centerprint);
249 MSG_WriteString (msg);
250
251 PF_Unicast (ent, qtrue);
252 }
253
254
255 /*
256 ===============
257 PF_error
258
259 Abort the server with a game error
260 ===============
261 */
262 void PF_error (const char *fmt, ...) __attribute__(( noreturn ));
263
PF_error(const char * fmt,...)264 void PF_error (const char *fmt, ...)
265 {
266 char msg[MAXPRINTMSG];
267 va_list argptr;
268
269 va_start (argptr,fmt);
270 Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
271 va_end (argptr);
272
273 Com_Error (ERR_DROP, "Game Error: %s", msg);
274 }
275
276
277 /*
278 =================
279 PF_setmodel
280
281 Also sets mins and maxs for inline bmodels
282 =================
283 */
PF_setmodel(edict_t * ent,const char * name)284 void PF_setmodel (edict_t *ent, const char *name)
285 {
286 int i;
287 cmodel_t *mod;
288
289 if (!name)
290 Com_Error (ERR_DROP, "PF_setmodel: NULL");
291
292 i = SV_ModelIndex (name);
293
294 ent->s.modelindex = i;
295
296 // if it is an inline model, get the size information for it
297 if( name[0] == '*' ) {
298 mod = CM_InlineModel (&sv.cm, name);
299 VectorCopy (mod->mins, ent->mins);
300 VectorCopy (mod->maxs, ent->maxs);
301 SV_LinkEdict (ent);
302 }
303
304 }
305
306 /*
307 ===============
308 PF_Configstring
309
310 If game is actively running, broadcasts configstring change.
311 Archived in MVD stream.
312 ===============
313 */
PF_Configstring(int index,const char * val)314 void PF_Configstring( int index, const char *val ) {
315 int length, maxlength;
316 client_t *client;
317
318 if( index < 0 || index >= MAX_CONFIGSTRINGS )
319 Com_Error( ERR_DROP, "PF_Configstring: bad index %i\n", index );
320
321 if( !val )
322 val = "";
323
324 length = strlen( val );
325 maxlength = sizeof( sv.configstrings ) -
326 sizeof( sv.configstrings[0] ) * index - 1;
327 if( length > maxlength ) {
328 Com_Error( ERR_DROP, "PF_Configstring: oversize configstring "
329 "%d: %d > %d\n", index, length, maxlength );
330 }
331
332 if( !strcmp( sv.configstrings[index], val ) ) {
333 return;
334 }
335
336 // change the string in sv
337 strcpy( sv.configstrings[index], val );
338
339 if( sv.state == ss_loading ) {
340 return;
341 }
342
343 // dump to demofile
344 MVD_RecConfigstring( index, val );
345
346 // send the update to everyone
347 MSG_WriteByte( svc_configstring );
348 MSG_WriteShort( index );
349 MSG_WriteString( val );
350
351 FOR_EACH_CLIENT( client ) {
352 if( client->state < cs_connected ) {
353 continue;
354 }
355 SV_ClientAddMessage( client, MSG_RELIABLE );
356 }
357
358 SZ_Clear( &msg_write );
359 }
360
PF_WriteFloat(float f)361 void PF_WriteFloat( float f ) {
362 Com_Error( ERR_DROP, "PF_WriteFloat not implemented" );
363 }
364
365
366
367 /*
368 =================
369 PF_inPVS
370
371 Also checks portalareas so that doors block sight
372 =================
373 */
PF_inPVS(vec3_t p1,vec3_t p2)374 qboolean PF_inPVS (vec3_t p1, vec3_t p2)
375 {
376 cleaf_t *leaf;
377 int cluster;
378 int area1, area2;
379 byte *mask;
380
381 if( !sv.cm.cache ) {
382 Com_Error( ERR_DROP, "PF_inPVS: no map loaded" );
383 }
384
385 leaf = CM_PointLeaf (&sv.cm, p1);
386 cluster = CM_LeafCluster (leaf);
387 area1 = CM_LeafArea (leaf);
388 mask = CM_ClusterPVS (&sv.cm, cluster);
389
390 leaf = CM_PointLeaf (&sv.cm, p2);
391 cluster = CM_LeafCluster (leaf);
392 area2 = CM_LeafArea (leaf);
393 if ( !Q_IsBitSet( mask, cluster ) )
394 return qfalse;
395 if (!CM_AreasConnected (&sv.cm, area1, area2))
396 return qfalse; // a door blocks sight
397 return qtrue;
398 }
399
400
401 /*
402 =================
403 PF_inPHS
404
405 Also checks portalareas so that doors block sound
406 =================
407 */
PF_inPHS(vec3_t p1,vec3_t p2)408 qboolean PF_inPHS (vec3_t p1, vec3_t p2)
409 {
410 cleaf_t *leaf;
411 int cluster;
412 int area1, area2;
413 byte *mask;
414
415 if( !sv.cm.cache ) {
416 Com_Error( ERR_DROP, "PF_inPHS: no map loaded" );
417 }
418
419 leaf = CM_PointLeaf (&sv.cm, p1);
420 cluster = CM_LeafCluster (leaf);
421 area1 = CM_LeafArea (leaf);
422 mask = CM_ClusterPHS (&sv.cm, cluster);
423
424 leaf = CM_PointLeaf (&sv.cm, p2);
425 cluster = CM_LeafCluster (leaf);
426 area2 = CM_LeafArea (leaf);
427 if( !Q_IsBitSet( mask, cluster ) )
428 return qfalse; // more than one bounce away
429 if (!CM_AreasConnected (&sv.cm, area1, area2))
430 return qfalse; // a door blocks hearing
431
432 return qtrue;
433 }
434
PF_StartSound(edict_t * entity,int channel,int sound_num,float volume,float attenuation,float timeofs)435 void PF_StartSound (edict_t *entity, int channel, int sound_num, float volume,
436 float attenuation, float timeofs)
437 {
438 if (!entity)
439 return;
440 SV_StartSound (NULL, entity, channel, sound_num, volume, attenuation, timeofs);
441 }
442
PF_Pmove(pmove_t * pm)443 void PF_Pmove( pmove_t *pm ) {
444 if( !sv_client ) {
445 //Pmove( pm ); // TODO
446 return;
447 }
448
449 Pmove( pm, &sv_client->pmp );
450 }
451
PF_cvar(const char * name,const char * value,int flags)452 cvar_t *PF_cvar( const char *name, const char *value, int flags ) {
453 cvar_t *var;
454
455 if( flags & CVAR_EXTENDED_MASK ) {
456 Com_WPrintf( "Game DLL attemped to set extended flags on variable '%s', cleared.\n", name );
457 flags &= ~CVAR_EXTENDED_MASK;
458 }
459
460 var = Cvar_Get( name, value, flags );
461 if( !var->subsystem ) {
462 var->subsystem = CVAR_SYSTEM_GAME;
463 }
464
465 return var;
466 }
467
PF_AddCommandString(const char * string)468 void PF_AddCommandString( const char *string ) {
469 Cbuf_AddTextEx( &cmd_buffer, string );
470 }
471
PF_SetAreaPortalState(int portalnum,qboolean open)472 void PF_SetAreaPortalState( int portalnum, qboolean open ) {
473 if( !sv.cm.cache ) {
474 Com_Error( ERR_DROP, "PF_SetAreaPortalState: no map loaded" );
475 }
476 CM_SetAreaPortalState( &sv.cm, portalnum, open );
477 }
478
PF_AreasConnected(int area1,int area2)479 qboolean PF_AreasConnected( int area1, int area2 ) {
480 if( !sv.cm.cache ) {
481 Com_Error( ERR_DROP, "PF_AreasConnected: no map loaded" );
482 }
483 return CM_AreasConnected( &sv.cm, area1, area2 );
484 }
485
PF_TagMalloc(int size,memtag_t tag)486 void *PF_TagMalloc( int size, memtag_t tag ) {
487 void *ptr;
488
489 if( !size ) {
490 return NULL;
491 }
492
493 ptr = Z_TagMalloc( size, tag );
494 memset( ptr, 0, size );
495
496 return ptr;
497 }
498
499 //==============================================
500
501 /*
502 ===============
503 SV_ShutdownGameProgs
504
505 Called when either the entire server is being killed, or
506 it is changing to a different game directory.
507 ===============
508 */
SV_ShutdownGameProgs(void)509 void SV_ShutdownGameProgs (void)
510 {
511 if (!ge)
512 return;
513 ge->Shutdown ();
514 if( ge != &mvd_ge ) {
515 Sys_FreeGameLibrary();
516 }
517 ge = NULL;
518 }
519
520 /*
521 ===============
522 SV_InitGameProgs
523
524 Init the game subsystem for a new map
525 ===============
526 */
527 void SCR_DebugGraph (float value, int color);
528
SV_InitGameProgs(void)529 void SV_InitGameProgs ( void )
530 {
531 game_import_t import;
532
533 // unload anything we have now
534 if (ge)
535 SV_ShutdownGameProgs ();
536
537 // load a new game dll
538 import.multicast = SV_Multicast;
539 import.unicast = PF_Unicast;
540 import.bprintf = PF_bprintf;
541 import.dprintf = PF_dprintf;
542 import.cprintf = PF_cprintf;
543 import.centerprintf = PF_centerprintf;
544 import.error = PF_error;
545
546 import.linkentity = SV_LinkEdict;
547 import.unlinkentity = SV_UnlinkEdict;
548 import.BoxEdicts = SV_AreaEdicts;
549 #ifdef _WIN32
550 #ifdef __GNUC__
551 import.trace = ( sv_trace_t )SV_Trace_Old;
552 #else
553 import.trace = SV_Trace;
554 #endif
555 #else /* _WIN32 */
556 if( sv_oldgame_hack->integer ) {
557 import.trace = ( sv_trace_t )SV_Trace_Old;
558 } else {
559 import.trace = SV_Trace;
560 }
561 #endif /* !_WIN32 */
562 import.pointcontents = SV_PointContents;
563 import.setmodel = PF_setmodel;
564 import.inPVS = PF_inPVS;
565 import.inPHS = PF_inPHS;
566 import.Pmove = PF_Pmove;
567
568 import.modelindex = SV_ModelIndex;
569 import.soundindex = SV_SoundIndex;
570 import.imageindex = SV_ImageIndex;
571
572 import.configstring = PF_Configstring;
573 import.sound = PF_StartSound;
574 import.positioned_sound = SV_StartSound;
575
576 import.WriteChar = MSG_WriteChar;
577 import.WriteByte = MSG_WriteByte;
578 import.WriteShort = MSG_WriteShort;
579 import.WriteLong = MSG_WriteLong;
580 import.WriteFloat = PF_WriteFloat;
581 import.WriteString = MSG_WriteString;
582 import.WritePosition = MSG_WritePos;
583 import.WriteDir = MSG_WriteDir;
584 import.WriteAngle = MSG_WriteAngle;
585
586 import.TagMalloc = PF_TagMalloc;
587 import.TagFree = Z_Free;
588 import.FreeTags = Z_FreeTags;
589
590 import.cvar = PF_cvar;
591 import.cvar_set = Cvar_UserSet;
592 import.cvar_forceset = Cvar_Set;
593
594 import.argc = Cmd_Argc;
595 import.argv = Cmd_Argv;
596 import.args = Cmd_Args;
597 import.AddCommandString = PF_AddCommandString;
598
599 import.DebugGraph = SCR_DebugGraph;
600 import.SetAreaPortalState = PF_SetAreaPortalState;
601 import.AreasConnected = PF_AreasConnected;
602
603 ge = (game_export_t *)Sys_LoadGameLibrary( &import );
604
605 if (!ge)
606 Com_Error (ERR_DROP, "failed to load game DLL");
607 if (ge->apiversion != GAME_API_VERSION)
608 Com_Error (ERR_DROP, "game is version %i, not %i", ge->apiversion,
609 GAME_API_VERSION);
610
611 ge->Init ();
612 }
613
614