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