1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 // cl_cgame.c  -- client system interaction with client game
30 
31 #include "client.h"
32 
33 #include "../botlib/botlib.h"
34 
35 #ifdef USE_MUMBLE
36 #include "libmumblelink.h"
37 #endif
38 
39 extern botlib_export_t *botlib_export;
40 
41 extern qboolean loadCamera( int camNum, const char *name );
42 extern void startCamera( int camNum, int time );
43 extern qboolean getCameraInfo( int camNum, int time, vec3_t *origin, vec3_t *angles, float *fov );
44 
45 // RF, this is only used when running a local server
46 extern void SV_SendMoveSpeedsToGame( int entnum, char *text );
47 
48 // NERVE - SMF
49 void Key_GetBindingBuf( int keynum, char *buf, int buflen );
50 void Key_KeynumToStringBuf( int keynum, char *buf, int buflen );
51 // -NERVE - SMF
52 
53 /*
54 ====================
55 CL_GetGameState
56 ====================
57 */
CL_GetGameState(gameState_t * gs)58 void CL_GetGameState( gameState_t *gs ) {
59 	*gs = cl.gameState;
60 }
61 
62 /*
63 ====================
64 CL_GetGlconfig
65 ====================
66 */
CL_GetGlconfig(glconfig_t * glconfig)67 void CL_GetGlconfig( glconfig_t *glconfig ) {
68 	*glconfig = cls.glconfig;
69 }
70 
71 
72 /*
73 ====================
74 CL_GetUserCmd
75 ====================
76 */
CL_GetUserCmd(int cmdNumber,usercmd_t * ucmd)77 qboolean CL_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) {
78 	// cmds[cmdNumber] is the last properly generated command
79 
80 	// can't return anything that we haven't created yet
81 	if ( cmdNumber > cl.cmdNumber ) {
82 		Com_Error( ERR_DROP, "CL_GetUserCmd: %i >= %i", cmdNumber, cl.cmdNumber );
83 	}
84 
85 	// the usercmd has been overwritten in the wrapping
86 	// buffer because it is too far out of date
87 	if ( cmdNumber <= cl.cmdNumber - CMD_BACKUP ) {
88 		return qfalse;
89 	}
90 
91 	*ucmd = cl.cmds[ cmdNumber & CMD_MASK ];
92 
93 	return qtrue;
94 }
95 
CL_GetCurrentCmdNumber(void)96 int CL_GetCurrentCmdNumber( void ) {
97 	return cl.cmdNumber;
98 }
99 
100 
101 /*
102 ====================
103 CL_GetParseEntityState
104 ====================
105 */
CL_GetParseEntityState(int parseEntityNumber,entityState_t * state)106 qboolean    CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) {
107 	// can't return anything that hasn't been parsed yet
108 	if ( parseEntityNumber >= cl.parseEntitiesNum ) {
109 		Com_Error( ERR_DROP, "CL_GetParseEntityState: %i >= %i",
110 				   parseEntityNumber, cl.parseEntitiesNum );
111 	}
112 
113 	// can't return anything that has been overwritten in the circular buffer
114 	if ( parseEntityNumber <= cl.parseEntitiesNum - MAX_PARSE_ENTITIES ) {
115 		return qfalse;
116 	}
117 
118 	*state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ];
119 	return qtrue;
120 }
121 
122 /*
123 ====================
124 CL_GetCurrentSnapshotNumber
125 ====================
126 */
CL_GetCurrentSnapshotNumber(int * snapshotNumber,int * serverTime)127 void    CL_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) {
128 	*snapshotNumber = cl.snap.messageNum;
129 	*serverTime = cl.snap.serverTime;
130 }
131 
132 /*
133 ====================
134 CL_GetSnapshot
135 ====================
136 */
CL_GetSnapshot(int snapshotNumber,snapshot_t * snapshot)137 qboolean    CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) {
138 	clSnapshot_t    *clSnap;
139 	int i, count;
140 
141 	if ( snapshotNumber > cl.snap.messageNum ) {
142 		Com_Error( ERR_DROP, "CL_GetSnapshot: snapshotNumber > cl.snapshot.messageNum" );
143 	}
144 
145 	// if the frame has fallen out of the circular buffer, we can't return it
146 	if ( cl.snap.messageNum - snapshotNumber >= PACKET_BACKUP ) {
147 		return qfalse;
148 	}
149 
150 	// if the frame is not valid, we can't return it
151 	clSnap = &cl.snapshots[snapshotNumber & PACKET_MASK];
152 	if ( !clSnap->valid ) {
153 		return qfalse;
154 	}
155 
156 	// if the entities in the frame have fallen out of their
157 	// circular buffer, we can't return it
158 	if ( cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES ) {
159 		return qfalse;
160 	}
161 
162 	// write the snapshot
163 	snapshot->snapFlags = clSnap->snapFlags;
164 	snapshot->serverCommandSequence = clSnap->serverCommandNum;
165 	snapshot->ping = clSnap->ping;
166 	snapshot->serverTime = clSnap->serverTime;
167 	memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) );
168 	snapshot->ps = clSnap->ps;
169 	count = clSnap->numEntities;
170 	if ( count > MAX_ENTITIES_IN_SNAPSHOT ) {
171 		Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT );
172 		count = MAX_ENTITIES_IN_SNAPSHOT;
173 	}
174 	snapshot->numEntities = count;
175 	for ( i = 0 ; i < count ; i++ ) {
176 		snapshot->entities[i] =
177 			cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & ( MAX_PARSE_ENTITIES - 1 ) ];
178 	}
179 
180 	// FIXME: configstring changes and server commands!!!
181 
182 	return qtrue;
183 }
184 
185 /*
186 ==============
187 CL_SetUserCmdValue
188 ==============
189 */
CL_SetUserCmdValue(int userCmdValue,int holdableValue,float sensitivityScale,int mpSetup,int mpIdentClient)190 void CL_SetUserCmdValue( int userCmdValue, int holdableValue, float sensitivityScale, int mpSetup, int mpIdentClient ) {
191 	cl.cgameUserCmdValue        = userCmdValue;
192 	cl.cgameUserHoldableValue   = holdableValue;
193 	cl.cgameSensitivity         = sensitivityScale;
194 	cl.cgameMpSetup             = mpSetup;              // NERVE - SMF
195 	cl.cgameMpIdentClient       = mpIdentClient;        // NERVE - SMF
196 }
197 
198 /*
199 ==================
200 CL_SetClientLerpOrigin
201 ==================
202 */
CL_SetClientLerpOrigin(float x,float y,float z)203 void CL_SetClientLerpOrigin( float x, float y, float z ) {
204 	cl.cgameClientLerpOrigin[0] = x;
205 	cl.cgameClientLerpOrigin[1] = y;
206 	cl.cgameClientLerpOrigin[2] = z;
207 }
208 
209 /*
210 ==============
211 CL_AddCgameCommand
212 ==============
213 */
CL_AddCgameCommand(const char * cmdName)214 void CL_AddCgameCommand( const char *cmdName ) {
215 	Cmd_AddCommand( cmdName, NULL );
216 }
217 
218 /*
219 =====================
220 CL_ConfigstringModified
221 =====================
222 */
CL_ConfigstringModified(void)223 void CL_ConfigstringModified( void ) {
224 	char        *old, *s;
225 	int i, index;
226 	char        *dup;
227 	gameState_t oldGs;
228 	int len;
229 
230 	index = atoi( Cmd_Argv( 1 ) );
231 	if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
232 		Com_Error( ERR_DROP, "CL_ConfigstringModified: bad index %i", index );
233 	}
234 //	s = Cmd_Argv(2);
235 	// get everything after "cs <num>"
236 	s = Cmd_ArgsFrom( 2 );
237 
238 	old = cl.gameState.stringData + cl.gameState.stringOffsets[ index ];
239 	if ( !strcmp( old, s ) ) {
240 		return;     // unchanged
241 	}
242 
243 	// build the new gameState_t
244 	oldGs = cl.gameState;
245 
246 	memset( &cl.gameState, 0, sizeof( cl.gameState ) );
247 
248 	// leave the first 0 for uninitialized strings
249 	cl.gameState.dataCount = 1;
250 
251 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
252 		if ( i == index ) {
253 			dup = s;
254 		} else {
255 			dup = oldGs.stringData + oldGs.stringOffsets[ i ];
256 		}
257 		if ( !dup[0] ) {
258 			continue;       // leave with the default empty string
259 		}
260 
261 		len = strlen( dup );
262 
263 		if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
264 			Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
265 		}
266 
267 		// append it to the gameState string buffer
268 		cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
269 		memcpy( cl.gameState.stringData + cl.gameState.dataCount, dup, len + 1 );
270 		cl.gameState.dataCount += len + 1;
271 	}
272 
273 	if ( index == CS_SYSTEMINFO ) {
274 		// parse serverId and other cvars
275 		CL_SystemInfoChanged();
276 	}
277 
278 }
279 
280 
281 /*
282 ===================
283 CL_GetServerCommand
284 
285 Set up argc/argv for the given command
286 ===================
287 */
CL_GetServerCommand(int serverCommandNumber)288 qboolean CL_GetServerCommand( int serverCommandNumber ) {
289 	char    *s;
290 	char    *cmd;
291 	static char bigConfigString[BIG_INFO_STRING];
292 	int argc;
293 
294 	// if we have irretrievably lost a reliable command, drop the connection
295 	if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) {
296 		// when a demo record was started after the client got a whole bunch of
297 		// reliable commands then the client never got those first reliable commands
298 		if ( clc.demoplaying ) {
299 			return qfalse;
300 		}
301 		Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" );
302 		return qfalse;
303 	}
304 
305 	if ( serverCommandNumber > clc.serverCommandSequence ) {
306 		Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" );
307 		return qfalse;
308 	}
309 
310 	s = clc.serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ];
311 	clc.lastExecutedServerCommand = serverCommandNumber;
312 
313 	if ( cl_showServerCommands->integer ) {         // NERVE - SMF
314 		Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s );
315 	}
316 
317 rescan:
318 	Cmd_TokenizeString( s );
319 	cmd = Cmd_Argv( 0 );
320 	argc = Cmd_Argc();
321 
322 	if ( !strcmp( cmd, "disconnect" ) ) {
323 		// NERVE - SMF - allow server to indicate why they were disconnected
324 		if ( argc >= 2 ) {
325 			Com_Error( ERR_SERVERDISCONNECT, "Server disconnected - %s", Cmd_Argv( 1 ) );
326 		} else {
327 			Com_Error( ERR_SERVERDISCONNECT, "Server disconnected" );
328 		}
329 	}
330 
331 	if ( !strcmp( cmd, "bcs0" ) ) {
332 		Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv( 1 ), Cmd_Argv( 2 ) );
333 		return qfalse;
334 	}
335 
336 	if ( !strcmp( cmd, "bcs1" ) ) {
337 		s = Cmd_Argv( 2 );
338 		if ( strlen( bigConfigString ) + strlen( s ) >= BIG_INFO_STRING ) {
339 			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
340 		}
341 		strcat( bigConfigString, s );
342 		return qfalse;
343 	}
344 
345 	if ( !strcmp( cmd, "bcs2" ) ) {
346 		s = Cmd_Argv( 2 );
347 		if ( strlen( bigConfigString ) + strlen( s ) + 1 >= BIG_INFO_STRING ) {
348 			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
349 		}
350 		strcat( bigConfigString, s );
351 		strcat( bigConfigString, "\"" );
352 		s = bigConfigString;
353 		goto rescan;
354 	}
355 
356 	if ( !strcmp( cmd, "cs" ) ) {
357 		CL_ConfigstringModified();
358 		// reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString()
359 		Cmd_TokenizeString( s );
360 		return qtrue;
361 	}
362 
363 	if ( !strcmp( cmd, "map_restart" ) ) {
364 		// clear notify lines and outgoing commands before passing
365 		// the restart to the cgame
366 		Con_ClearNotify();
367 		// reparse the string, because Con_ClearNotify() may have done another Cmd_TokenizeString()
368 		Cmd_TokenizeString( s );
369 		memset( cl.cmds, 0, sizeof( cl.cmds ) );
370 		return qtrue;
371 	}
372 
373 	if ( !strcmp( cmd, "popup" ) ) { // direct server to client popup request, bypassing cgame
374 //		trap_UI_Popup(Cmd_Argv(1));
375 //		if ( cls.state == CA_ACTIVE && !clc.demoplaying ) {
376 //			VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_CLIPBOARD);
377 //			Menus_OpenByName(Cmd_Argv(1));
378 //		}
379 		return qfalse;
380 	}
381 
382 
383 	// the clientLevelShot command is used during development
384 	// to generate 128*128 screenshots from the intermission
385 	// point of levels for the menu system to use
386 	// we pass it along to the cgame to make apropriate adjustments,
387 	// but we also clear the console and notify lines here
388 	if ( !strcmp( cmd, "clientLevelShot" ) ) {
389 		// don't do it if we aren't running the server locally,
390 		// otherwise malicious remote servers could overwrite
391 		// the existing thumbnails
392 		if ( !com_sv_running->integer ) {
393 			return qfalse;
394 		}
395 		// close the console
396 		Con_Close();
397 		// take a special screenshot next frame
398 		Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" );
399 		return qtrue;
400 	}
401 
402 	// we may want to put a "connect to other server" command here
403 
404 	// cgame can now act on the command
405 	return qtrue;
406 }
407 
408 // DHM - Nerve :: Copied from server to here
409 /*
410 ====================
411 CL_SetExpectedHunkUsage
412 
413   Sets com_expectedhunkusage, so the client knows how to draw the percentage bar
414 ====================
415 */
CL_SetExpectedHunkUsage(const char * mapname)416 void CL_SetExpectedHunkUsage( const char *mapname ) {
417 	int handle;
418 	char *memlistfile = "hunkusage.dat";
419 	char *buf;
420 	char *buftrav;
421 	char *token;
422 	int len;
423 
424 	len = FS_FOpenFileByMode( memlistfile, &handle, FS_READ );
425 	if ( len >= 0 ) { // the file exists, so read it in, strip out the current entry for this map, and save it out, so we can append the new value
426 
427 		buf = (char *)Z_Malloc( len + 1 );
428 		memset( buf, 0, len + 1 );
429 
430 		FS_Read( (void *)buf, len, handle );
431 		FS_FCloseFile( handle );
432 
433 		// now parse the file, filtering out the current map
434 		buftrav = buf;
435 		while ( ( token = COM_Parse( &buftrav ) ) && token[0] ) {
436 			if ( !Q_strcasecmp( token, (char *)mapname ) ) {
437 				// found a match
438 				token = COM_Parse( &buftrav );  // read the size
439 				if ( token && token[0] ) {
440 					// this is the usage
441 					Cvar_Set( "com_expectedhunkusage", token );
442 					Z_Free( buf );
443 					return;
444 				}
445 			}
446 		}
447 
448 		Z_Free( buf );
449 	}
450 	// just set it to a negative number,so the cgame knows not to draw the percent bar
451 	Cvar_Set( "com_expectedhunkusage", "-1" );
452 }
453 
454 // dhm - nerve
455 
456 /*
457 ====================
458 CL_CM_LoadMap
459 
460 Just adds default parameters that cgame doesn't need to know about
461 ====================
462 */
CL_CM_LoadMap(const char * mapname)463 void CL_CM_LoadMap( const char *mapname ) {
464 	int checksum;
465 
466 	// DHM - Nerve :: If we are not running the server, then set expected usage here
467 	if ( !com_sv_running->integer ) {
468 		CL_SetExpectedHunkUsage( mapname );
469 	} else
470 	{
471 		// TTimo
472 		// catch here when a local server is started to avoid outdated com_errorDiagnoseIP
473 		Cvar_Set( "com_errorDiagnoseIP", "" );
474 	}
475 
476 	CM_LoadMap( mapname, qtrue, &checksum );
477 }
478 
479 /*
480 ====================
481 CL_ShutdonwCGame
482 
483 ====================
484 */
CL_ShutdownCGame(void)485 void CL_ShutdownCGame( void ) {
486 	Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CGAME );
487 	cls.cgameStarted = qfalse;
488 	if ( !cgvm ) {
489 		return;
490 	}
491 	VM_Call( cgvm, CG_SHUTDOWN );
492 	VM_Free( cgvm );
493 	cgvm = NULL;
494 }
495 
FloatAsInt(float f)496 static int  FloatAsInt( float f ) {
497 	floatint_t fi;
498 	fi.f = f;
499 	return fi.i;
500 }
501 
502 /*
503 ====================
504 CL_CgameSystemCalls
505 
506 The cgame module is making a system call
507 ====================
508 */
CL_CgameSystemCalls(intptr_t * args)509 intptr_t CL_CgameSystemCalls( intptr_t *args ) {
510 	switch ( args[0] ) {
511 	case CG_PRINT:
512 		Com_Printf( "%s", (const char*)VMA(1) );
513 		return 0;
514 	case CG_ERROR:
515 		Com_Error( ERR_DROP, "%s", (const char*)VMA(1) );
516 		return 0;
517 	case CG_MILLISECONDS:
518 		return Sys_Milliseconds();
519 	case CG_CVAR_REGISTER:
520 		Cvar_Register( VMA( 1 ), VMA( 2 ), VMA( 3 ), args[4] );
521 		return 0;
522 	case CG_CVAR_UPDATE:
523 		Cvar_Update( VMA( 1 ) );
524 		return 0;
525 	case CG_CVAR_SET:
526 		Cvar_SetSafe( VMA(1), VMA(2) );
527 		return 0;
528 	case CG_CVAR_VARIABLESTRINGBUFFER:
529 		Cvar_VariableStringBuffer( VMA( 1 ), VMA( 2 ), args[3] );
530 		return 0;
531 	case CG_ARGC:
532 		return Cmd_Argc();
533 	case CG_ARGV:
534 		Cmd_ArgvBuffer( args[1], VMA( 2 ), args[3] );
535 		return 0;
536 	case CG_ARGS:
537 		Cmd_ArgsBuffer( VMA( 1 ), args[2] );
538 		return 0;
539 	case CG_FS_FOPENFILE:
540 		return FS_FOpenFileByMode( VMA( 1 ), VMA( 2 ), args[3] );
541 	case CG_FS_READ:
542 		FS_Read( VMA( 1 ), args[2], args[3] );
543 		return 0;
544 	case CG_FS_WRITE:
545 		return FS_Write( VMA( 1 ), args[2], args[3] );
546 	case CG_FS_FCLOSEFILE:
547 		FS_FCloseFile( args[1] );
548 		return 0;
549 	case CG_SENDCONSOLECOMMAND:
550 		Cbuf_AddText( VMA( 1 ) );
551 		return 0;
552 	case CG_ADDCOMMAND:
553 		CL_AddCgameCommand( VMA( 1 ) );
554 		return 0;
555 	case CG_REMOVECOMMAND:
556 		Cmd_RemoveCommandSafe( VMA(1) );
557 		return 0;
558 	case CG_SENDCLIENTCOMMAND:
559 		CL_AddReliableCommand(VMA(1), qfalse);
560 		return 0;
561 	case CG_UPDATESCREEN:
562 		// this is used during lengthy level loading, so pump message loop
563 		// Com_EventLoop();	// FIXME: if a server restarts here, BAD THINGS HAPPEN!
564 		// We can't call Com_EventLoop here, a restart will crash and this _does_ happen
565 		// if there is a map change while we are downloading a pk3.
566 		// ZOID
567 		SCR_UpdateScreen();
568 		return 0;
569 	case CG_CM_LOADMAP:
570 		CL_CM_LoadMap( VMA( 1 ) );
571 		return 0;
572 	case CG_CM_NUMINLINEMODELS:
573 		return CM_NumInlineModels();
574 	case CG_CM_INLINEMODEL:
575 		return CM_InlineModel( args[1] );
576 	case CG_CM_TEMPBOXMODEL:
577 		return CM_TempBoxModel( VMA( 1 ), VMA( 2 ), qfalse );
578 	case CG_CM_TEMPCAPSULEMODEL:
579 		return CM_TempBoxModel( VMA( 1 ), VMA( 2 ), qtrue );
580 	case CG_CM_POINTCONTENTS:
581 		return CM_PointContents( VMA( 1 ), args[2] );
582 	case CG_CM_TRANSFORMEDPOINTCONTENTS:
583 		return CM_TransformedPointContents( VMA( 1 ), args[2], VMA( 3 ), VMA( 4 ) );
584 	case CG_CM_BOXTRACE:
585 		CM_BoxTrace( VMA( 1 ), VMA( 2 ), VMA( 3 ), VMA( 4 ), VMA( 5 ), args[6], args[7], /*int capsule*/ qfalse );
586 		return 0;
587 	case CG_CM_TRANSFORMEDBOXTRACE:
588 		CM_TransformedBoxTrace( VMA( 1 ), VMA( 2 ), VMA( 3 ), VMA( 4 ), VMA( 5 ), args[6], args[7], VMA( 8 ), VMA( 9 ), /*int capsule*/ qfalse );
589 		return 0;
590 	case CG_CM_CAPSULETRACE:
591 		CM_BoxTrace( VMA( 1 ), VMA( 2 ), VMA( 3 ), VMA( 4 ), VMA( 5 ), args[6], args[7], /*int capsule*/ qtrue );
592 		return 0;
593 	case CG_CM_TRANSFORMEDCAPSULETRACE:
594 		CM_TransformedBoxTrace( VMA( 1 ), VMA( 2 ), VMA( 3 ), VMA( 4 ), VMA( 5 ), args[6], args[7], VMA( 8 ), VMA( 9 ), /*int capsule*/ qtrue );
595 		return 0;
596 	case CG_CM_MARKFRAGMENTS:
597 		return re.MarkFragments( args[1], VMA( 2 ), VMA( 3 ), args[4], VMA( 5 ), args[6], VMA( 7 ) );
598 	case CG_S_STARTSOUND:
599 		S_StartSound( VMA( 1 ), args[2], args[3], args[4] );
600 		return 0;
601 //----(SA)	added
602 	case CG_S_STARTSOUNDEX:
603 		S_StartSoundEx( VMA( 1 ), args[2], args[3], args[4], args[5] );
604 		return 0;
605 //----(SA)	end
606 	case CG_S_STARTLOCALSOUND:
607 		S_StartLocalSound( args[1], args[2] );
608 		return 0;
609 	case CG_S_CLEARLOOPINGSOUNDS:
610 		S_ClearLoopingSounds( args[1] ); // (SA) modified so no_pvs sounds can function
611 		return 0;
612 	case CG_S_ADDLOOPINGSOUND:
613 		// FIXME MrE: handling of looping sounds changed
614 		S_AddLoopingSound( args[1], VMA( 2 ), VMA( 3 ), args[4], args[5], args[6] );
615 		return 0;
616 	case CG_S_ADDREALLOOPINGSOUND:
617 		S_AddLoopingSound( args[1], VMA( 2 ), VMA( 3 ), args[4], args[5], args[6] );
618 		//S_AddRealLoopingSound( args[1], VMA(2), VMA(3), args[4], args[5] );
619 		return 0;
620 	case CG_S_STOPLOOPINGSOUND:
621 		// RF, not functional anymore, since we reverted to old looping code
622 		//S_StopLoopingSound( args[1] );
623 		return 0;
624 	case CG_S_UPDATEENTITYPOSITION:
625 		S_UpdateEntityPosition( args[1], VMA( 2 ) );
626 		return 0;
627 // Ridah, talking animations
628 	case CG_S_GETVOICEAMPLITUDE:
629 		return S_GetVoiceAmplitude( args[1] );
630 // done.
631 	case CG_S_RESPATIALIZE:
632 		S_Respatialize( args[1], VMA( 2 ), VMA( 3 ), args[4] );
633 		return 0;
634 	case CG_S_REGISTERSOUND:
635 		return S_RegisterSound( VMA( 1 ), qfalse );
636 	case CG_S_STARTBACKGROUNDTRACK:
637 		S_StartBackgroundTrack( VMA( 1 ), VMA( 2 ) );
638 		return 0;
639 	case CG_S_STARTSTREAMINGSOUND:
640 		S_StartStreamingSound( VMA( 1 ), VMA( 2 ), args[3], args[4], args[5] );
641 		return 0;
642 	case CG_R_LOADWORLDMAP:
643 		re.LoadWorld( VMA( 1 ) );
644 		return 0;
645 	case CG_R_REGISTERMODEL:
646 		return re.RegisterModel( VMA( 1 ) );
647 	case CG_R_REGISTERSKIN:
648 		return re.RegisterSkin( VMA( 1 ) );
649 
650 		//----(SA)	added
651 	case CG_R_GETSKINMODEL:
652 		return re.GetSkinModel( args[1], VMA( 2 ), VMA( 3 ) );
653 	case CG_R_GETMODELSHADER:
654 		return re.GetShaderFromModel( args[1], args[2], args[3] );
655 		//----(SA)	end
656 
657 	case CG_R_REGISTERSHADER:
658 		return re.RegisterShader( VMA( 1 ) );
659 	case CG_R_REGISTERFONT:
660 		re.RegisterFont( VMA( 1 ), args[2], VMA( 3 ) );
661 		return 0;
662 	case CG_R_REGISTERSHADERNOMIP:
663 		return re.RegisterShaderNoMip( VMA( 1 ) );
664 	case CG_R_CLEARSCENE:
665 		re.ClearScene();
666 		return 0;
667 	case CG_R_ADDREFENTITYTOSCENE:
668 		re.AddRefEntityToScene( VMA( 1 ) );
669 		return 0;
670 	case CG_R_ADDPOLYTOSCENE:
671 		re.AddPolyToScene( args[1], args[2], VMA( 3 ) );
672 		return 0;
673 		// Ridah
674 	case CG_R_ADDPOLYSTOSCENE:
675 		re.AddPolysToScene( args[1], args[2], VMA( 3 ), args[4] );
676 		return 0;
677 		// done.
678 //	case CG_R_LIGHTFORPOINT:
679 //		return re.LightForPoint( VMA(1), VMA(2), VMA(3), VMA(4) );
680 	case CG_R_ADDLIGHTTOSCENE:
681 		re.AddLightToScene( VMA( 1 ), VMF( 2 ), VMF( 3 ), VMF( 4 ), VMF( 5 ), args[6] );
682 		return 0;
683 //	case CG_R_ADDADDITIVELIGHTTOSCENE:
684 //		re.AddAdditiveLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
685 //		return 0;
686 	case CG_R_ADDCORONATOSCENE:
687 		re.AddCoronaToScene( VMA( 1 ), VMF( 2 ), VMF( 3 ), VMF( 4 ), VMF( 5 ), args[6], args[7] );
688 		return 0;
689 	case CG_R_SETFOG:
690 		re.SetFog( args[1], args[2], args[3], VMF( 4 ), VMF( 5 ), VMF( 6 ), VMF( 7 ) );
691 		return 0;
692 	case CG_R_RENDERSCENE:
693 		re.RenderScene( VMA( 1 ) );
694 		return 0;
695 	case CG_R_SETCOLOR:
696 		re.SetColor( VMA( 1 ) );
697 		return 0;
698 	case CG_R_DRAWSTRETCHPIC:
699 		re.DrawStretchPic( VMF( 1 ), VMF( 2 ), VMF( 3 ), VMF( 4 ), VMF( 5 ), VMF( 6 ), VMF( 7 ), VMF( 8 ), args[9] );
700 		return 0;
701 	case CG_R_DRAWROTATEDPIC:
702 		re.DrawRotatedPic( VMF( 1 ), VMF( 2 ), VMF( 3 ), VMF( 4 ), VMF( 5 ), VMF( 6 ), VMF( 7 ), VMF( 8 ), args[9], VMF( 10 ) );
703 		return 0;
704 	case CG_R_DRAWSTRETCHPIC_GRADIENT:
705 		re.DrawStretchPicGradient( VMF( 1 ), VMF( 2 ), VMF( 3 ), VMF( 4 ), VMF( 5 ), VMF( 6 ), VMF( 7 ), VMF( 8 ), args[9], VMA( 10 ), args[11] );
706 		return 0;
707 	case CG_R_MODELBOUNDS:
708 		re.ModelBounds( args[1], VMA( 2 ), VMA( 3 ) );
709 		return 0;
710 	case CG_R_LERPTAG:
711 		return re.LerpTag( VMA( 1 ), VMA( 2 ), VMA( 3 ), args[4] );
712 	case CG_GETGLCONFIG:
713 		CL_GetGlconfig( VMA( 1 ) );
714 		return 0;
715 	case CG_GETGAMESTATE:
716 		CL_GetGameState( VMA( 1 ) );
717 		return 0;
718 	case CG_GETCURRENTSNAPSHOTNUMBER:
719 		CL_GetCurrentSnapshotNumber( VMA( 1 ), VMA( 2 ) );
720 		return 0;
721 	case CG_GETSNAPSHOT:
722 		return CL_GetSnapshot( args[1], VMA( 2 ) );
723 	case CG_GETSERVERCOMMAND:
724 		return CL_GetServerCommand( args[1] );
725 	case CG_GETCURRENTCMDNUMBER:
726 		return CL_GetCurrentCmdNumber();
727 	case CG_GETUSERCMD:
728 		return CL_GetUserCmd( args[1], VMA( 2 ) );
729 	case CG_SETUSERCMDVALUE:
730 		CL_SetUserCmdValue( args[1], args[2], VMF( 3 ), args[4], args[5] );
731 		return 0;
732 	case CG_SETCLIENTLERPORIGIN:
733 		CL_SetClientLerpOrigin( VMF( 1 ), VMF( 2 ), VMF( 3 ) );
734 		return 0;
735 	case CG_MEMORY_REMAINING:
736 		return Hunk_MemoryRemaining();
737 	case CG_KEY_ISDOWN:
738 		return Key_IsDown( args[1] );
739 	case CG_KEY_GETCATCHER:
740 		return Key_GetCatcher();
741 	case CG_KEY_SETCATCHER:
742 		// Don't allow the cgame module to close the console
743 		Key_SetCatcher( args[1] | ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) );
744 		return 0;
745 	case CG_KEY_GETKEY:
746 		return Key_GetKey( VMA( 1 ) );
747 
748 
749 
750 	case CG_MEMSET:
751 		Com_Memset( VMA(1), args[2], args[3] );
752 		return args[1];
753 	case CG_MEMCPY:
754 		Com_Memcpy( VMA(1), VMA(2), args[3] );
755 		return args[1];
756 	case CG_STRNCPY:
757 		strncpy( VMA(1), VMA(2), args[3] );
758 		return args[1];
759 	case CG_SIN:
760 		return FloatAsInt( sin( VMF( 1 ) ) );
761 	case CG_COS:
762 		return FloatAsInt( cos( VMF( 1 ) ) );
763 	case CG_ATAN2:
764 		return FloatAsInt( atan2( VMF( 1 ), VMF( 2 ) ) );
765 	case CG_SQRT:
766 		return FloatAsInt( sqrt( VMF( 1 ) ) );
767 	case CG_FLOOR:
768 		return FloatAsInt( floor( VMF( 1 ) ) );
769 	case CG_CEIL:
770 		return FloatAsInt( ceil( VMF( 1 ) ) );
771 	case CG_ACOS:
772 		return FloatAsInt( Q_acos( VMF( 1 ) ) );
773 
774 	case CG_PC_ADD_GLOBAL_DEFINE:
775 		return botlib_export->PC_AddGlobalDefine( VMA( 1 ) );
776 	case CG_PC_LOAD_SOURCE:
777 		return botlib_export->PC_LoadSourceHandle( VMA( 1 ) );
778 	case CG_PC_FREE_SOURCE:
779 		return botlib_export->PC_FreeSourceHandle( args[1] );
780 	case CG_PC_READ_TOKEN:
781 		return botlib_export->PC_ReadTokenHandle( args[1], VMA( 2 ) );
782 	case CG_PC_SOURCE_FILE_AND_LINE:
783 		return botlib_export->PC_SourceFileAndLine( args[1], VMA( 2 ), VMA( 3 ) );
784 
785 	case CG_S_STOPBACKGROUNDTRACK:
786 		S_StopBackgroundTrack();
787 		return 0;
788 
789 	case CG_REAL_TIME:
790 		return Com_RealTime( VMA( 1 ) );
791 	case CG_SNAPVECTOR:
792 		Q_SnapVector(VMA(1));
793 		return 0;
794 
795 	case CG_SENDMOVESPEEDSTOGAME:
796 		SV_SendMoveSpeedsToGame( args[1], VMA( 2 ) );
797 		return 0;
798 
799 	case CG_CIN_PLAYCINEMATIC:
800 		return CIN_PlayCinematic( VMA( 1 ), args[2], args[3], args[4], args[5], args[6] );
801 
802 	case CG_CIN_STOPCINEMATIC:
803 		return CIN_StopCinematic( args[1] );
804 
805 	case CG_CIN_RUNCINEMATIC:
806 		return CIN_RunCinematic( args[1] );
807 
808 	case CG_CIN_DRAWCINEMATIC:
809 		CIN_DrawCinematic( args[1] );
810 		return 0;
811 
812 	case CG_CIN_SETEXTENTS:
813 		CIN_SetExtents( args[1], args[2], args[3], args[4], args[5] );
814 		return 0;
815 
816 	case CG_R_REMAP_SHADER:
817 		re.RemapShader( VMA( 1 ), VMA( 2 ), VMA( 3 ) );
818 		return 0;
819 
820 	case CG_TESTPRINTINT:
821 //		Com_Printf( "%s%i\n", (const char*)VMA( 1 ), args[2] );
822 		return 0;
823 	case CG_TESTPRINTFLOAT:
824 //		Com_Printf( "%s%f\n", (const char*)VMA( 1 ), VMF( 2 ) );
825 		return 0;
826 
827 	case CG_LOADCAMERA:
828 		return loadCamera( args[1], VMA( 2 ) );
829 
830 	case CG_STARTCAMERA:
831 		startCamera( args[1], args[2] );
832 		return 0;
833 
834 	case CG_GETCAMERAINFO:
835 		return getCameraInfo( args[1], args[2], VMA( 3 ), VMA( 4 ), VMA( 5 ) );
836 
837 	case CG_GET_ENTITY_TOKEN:
838 		return re.GetEntityToken( VMA( 1 ), args[2] );
839 
840 	case CG_INGAME_POPUP:
841 		if ( clc.state == CA_ACTIVE && !clc.demoplaying ) {
842 			// NERVE - SMF
843 			if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "UIMENU_WM_PICKTEAM" ) ) {
844 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_PICKTEAM );
845 			} else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "UIMENU_WM_PICKPLAYER" ) )    {
846 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_PICKPLAYER );
847 			} else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "UIMENU_WM_QUICKMESSAGE" ) )    {
848 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_QUICKMESSAGE );
849 			} else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "UIMENU_WM_QUICKMESSAGEALT" ) )    {
850 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_QUICKMESSAGEALT );
851 			} else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "UIMENU_WM_LIMBO" ) )    {
852 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_LIMBO );
853 			} else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "UIMENU_WM_AUTOUPDATE" ) )    {
854 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_AUTOUPDATE );
855 			}
856 			// -NERVE - SMF
857 			else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "hbook1" ) ) {   //----(SA)
858 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_BOOK1 );
859 			} else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "hbook2" ) )    { //----(SA)
860 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_BOOK2 );
861 			} else if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "hbook3" ) )    { //----(SA)
862 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_BOOK3 );
863 			} else {
864 				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_CLIPBOARD );
865 			}
866 		}
867 		return 0;
868 
869 		// NERVE - SMF
870 	case CG_INGAME_CLOSEPOPUP:
871 		// if popup menu is up, then close it
872 		if ( VMA( 1 ) && !Q_stricmp( VMA( 1 ), "UIMENU_WM_LIMBO" ) ) {
873 			if ( VM_Call( uivm, UI_GET_ACTIVE_MENU ) == UIMENU_WM_LIMBO ) {
874 				VM_Call( uivm, UI_KEY_EVENT, K_ESCAPE, qtrue );
875 				VM_Call( uivm, UI_KEY_EVENT, K_ESCAPE, qtrue );
876 			}
877 		}
878 		return 0;
879 
880 	case CG_LIMBOCHAT:
881 		if ( VMA( 1 ) ) {
882 			CL_AddToLimboChat( VMA( 1 ) );
883 		}
884 		return 0;
885 
886 	case CG_KEY_GETBINDINGBUF:
887 		Key_GetBindingBuf( args[1], VMA( 2 ), args[3] );
888 		return 0;
889 
890 	case CG_KEY_SETBINDING:
891 		Key_SetBinding( args[1], VMA( 2 ) );
892 		return 0;
893 
894 	case CG_KEY_KEYNUMTOSTRINGBUF:
895 		Key_KeynumToStringBuf( args[1], VMA( 2 ), args[3] );
896 		return 0;
897 
898 	case CG_TRANSLATE_STRING:
899 		CL_TranslateString( VMA( 1 ), VMA( 2 ) );
900 		return 0;
901 		// - NERVE - SMF
902 	default:
903 		Com_Error( ERR_DROP, "Bad cgame system trap: %ld", (long int) args[0] );
904 	}
905 	return 0;
906 }
907 
908 /*
909 ====================
910 CL_UpdateLevelHunkUsage
911 
912   This updates the "hunkusage.dat" file with the current map and it's hunk usage count
913 
914   This is used for level loading, so we can show a percentage bar dependant on the amount
915   of hunk memory allocated so far
916 
917   This will be slightly inaccurate if some settings like sound quality are changed, but these
918   things should only account for a small variation (hopefully)
919 ====================
920 */
CL_UpdateLevelHunkUsage(void)921 void CL_UpdateLevelHunkUsage( void ) {
922 	int handle;
923 	char *memlistfile = "hunkusage.dat";
924 	char *buf, *outbuf;
925 	char *buftrav, *outbuftrav;
926 	char *token;
927 	char outstr[256];
928 	int len, memusage;
929 
930 	memusage = Cvar_VariableIntegerValue( "com_hunkused" ) + Cvar_VariableIntegerValue( "hunk_soundadjust" );
931 
932 	len = FS_FOpenFileByMode( memlistfile, &handle, FS_READ );
933 	if ( len >= 0 ) { // the file exists, so read it in, strip out the current entry for this map, and save it out, so we can append the new value
934 
935 		buf = (char *)Z_Malloc( len + 1 );
936 		memset( buf, 0, len + 1 );
937 		outbuf = (char *)Z_Malloc( len + 1 );
938 		memset( outbuf, 0, len + 1 );
939 
940 		FS_Read( (void *)buf, len, handle );
941 		FS_FCloseFile( handle );
942 
943 		// now parse the file, filtering out the current map
944 		buftrav = buf;
945 		outbuftrav = outbuf;
946 		outbuftrav[0] = '\0';
947 		while ( ( token = COM_Parse( &buftrav ) ) && token[0] ) {
948 			if ( !Q_strcasecmp( token, cl.mapname ) ) {
949 				// found a match
950 				token = COM_Parse( &buftrav );  // read the size
951 				if ( token && token[0] ) {
952 					if ( atoi( token ) == memusage ) {  // if it is the same, abort this process
953 						Z_Free( buf );
954 						Z_Free( outbuf );
955 						return;
956 					}
957 				}
958 			} else {    // send it to the outbuf
959 				Q_strcat( outbuftrav, len + 1, token );
960 				Q_strcat( outbuftrav, len + 1, " " );
961 				token = COM_Parse( &buftrav );  // read the size
962 				if ( token && token[0] ) {
963 					Q_strcat( outbuftrav, len + 1, token );
964 					Q_strcat( outbuftrav, len + 1, "\n" );
965 				} else {
966 					Com_Error( ERR_DROP, "hunkusage.dat file is corrupt\n" );
967 				}
968 			}
969 		}
970 
971 #ifdef __MACOS__    //DAJ MacOS file typing
972 		{
973 			extern _MSL_IMP_EXP_C long _fcreator, _ftype;
974 			_ftype = 'TEXT';
975 			_fcreator = 'WlfS';
976 		}
977 #endif
978 		handle = FS_FOpenFileWrite( memlistfile );
979 		if ( handle < 0 ) {
980 			Com_Error( ERR_DROP, "cannot create %s\n", memlistfile );
981 		}
982 		// input file is parsed, now output to the new file
983 		len = strlen( outbuf );
984 		if ( FS_Write( (void *)outbuf, len, handle ) != len ) {
985 			Com_Error( ERR_DROP, "cannot write to %s\n", memlistfile );
986 		}
987 		FS_FCloseFile( handle );
988 
989 		Z_Free( buf );
990 		Z_Free( outbuf );
991 	}
992 	// now append the current map to the current file
993 	FS_FOpenFileByMode( memlistfile, &handle, FS_APPEND );
994 	if ( handle < 0 ) {
995 		Com_Error( ERR_DROP, "cannot write to hunkusage.dat, check disk full\n" );
996 	}
997 	Com_sprintf( outstr, sizeof( outstr ), "%s %i\n", cl.mapname, memusage );
998 	FS_Write( outstr, strlen( outstr ), handle );
999 	FS_FCloseFile( handle );
1000 
1001 	// now just open it and close it, so it gets copied to the pak dir
1002 	len = FS_FOpenFileByMode( memlistfile, &handle, FS_READ );
1003 	if ( len >= 0 ) {
1004 		FS_FCloseFile( handle );
1005 	}
1006 }
1007 
1008 /*
1009 ====================
1010 CL_InitCGame
1011 
1012 Should only by called by CL_StartHunkUsers
1013 ====================
1014 */
CL_InitCGame(void)1015 void CL_InitCGame( void ) {
1016 	const char          *info;
1017 	const char          *mapname;
1018 	int t1, t2;
1019 
1020 	t1 = Sys_Milliseconds();
1021 
1022 	// put away the console
1023 	Con_Close();
1024 
1025 	// find the current mapname
1026 	info = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SERVERINFO ];
1027 	mapname = Info_ValueForKey( info, "mapname" );
1028 	Com_sprintf( cl.mapname, sizeof( cl.mapname ), "maps/%s.bsp", mapname );
1029 
1030 	// load the dll or bytecode
1031 	cgvm = VM_Create( "cgame", CL_CgameSystemCalls, Cvar_VariableValue("vm_cgame") );
1032 	if ( !cgvm ) {
1033 		Com_Error( ERR_DROP, "VM_Create on cgame failed" );
1034 	}
1035 	clc.state = CA_LOADING;
1036 
1037 	// init for this gamestate
1038 	// use the lastExecutedServerCommand instead of the serverCommandSequence
1039 	// otherwise server commands sent just before a gamestate are dropped
1040 	VM_Call( cgvm, CG_INIT, clc.serverMessageSequence, clc.lastExecutedServerCommand, clc.clientNum );
1041 
1042 	// reset any CVAR_CHEAT cvars registered by cgame
1043 	if ( !clc.demoplaying && !cl_connectedToCheatServer )
1044 		Cvar_SetCheatState();
1045 
1046 	// we will send a usercmd this frame, which
1047 	// will cause the server to send us the first snapshot
1048 	clc.state = CA_PRIMED;
1049 
1050 	t2 = Sys_Milliseconds();
1051 
1052 	Com_Printf( "CL_InitCGame: %5.2f seconds\n", ( t2 - t1 ) / 1000.0 );
1053 
1054 	// have the renderer touch all its images, so they are present
1055 	// on the card even if the driver does deferred loading
1056 	re.EndRegistration();
1057 
1058 	// make sure everything is paged in
1059 	if ( !Sys_LowPhysicalMemory() ) {
1060 		Com_TouchMemory();
1061 	}
1062 
1063 	// clear anything that got printed
1064 	Con_ClearNotify();
1065 
1066 	// Ridah, update the memory usage file
1067 	CL_UpdateLevelHunkUsage();
1068 }
1069 
1070 
1071 /*
1072 ====================
1073 CL_GameCommand
1074 
1075 See if the current console command is claimed by the cgame
1076 ====================
1077 */
CL_GameCommand(void)1078 qboolean CL_GameCommand( void ) {
1079 	if ( !cgvm ) {
1080 		return qfalse;
1081 	}
1082 
1083 	return VM_Call( cgvm, CG_CONSOLE_COMMAND );
1084 }
1085 
1086 
1087 
1088 /*
1089 =====================
1090 CL_CGameRendering
1091 =====================
1092 */
CL_CGameRendering(stereoFrame_t stereo)1093 void CL_CGameRendering( stereoFrame_t stereo ) {
1094 	VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl.serverTime, stereo, clc.demoplaying );
1095 	VM_Debug( 0 );
1096 }
1097 
1098 
1099 /*
1100 =================
1101 CL_AdjustTimeDelta
1102 
1103 Adjust the clients view of server time.
1104 
1105 We attempt to have cl.serverTime exactly equal the server's view
1106 of time plus the timeNudge, but with variable latencies over
1107 the internet it will often need to drift a bit to match conditions.
1108 
1109 Our ideal time would be to have the adjusted time approach, but not pass,
1110 the very latest snapshot.
1111 
1112 Adjustments are only made when a new snapshot arrives with a rational
1113 latency, which keeps the adjustment process framerate independent and
1114 prevents massive overadjustment during times of significant packet loss
1115 or bursted delayed packets.
1116 =================
1117 */
1118 
1119 #define RESET_TIME  500
1120 
CL_AdjustTimeDelta(void)1121 void CL_AdjustTimeDelta( void ) {
1122 	int newDelta;
1123 	int deltaDelta;
1124 
1125 	cl.newSnapshots = qfalse;
1126 
1127 	// the delta never drifts when replaying a demo
1128 	if ( clc.demoplaying ) {
1129 		return;
1130 	}
1131 
1132 	newDelta = cl.snap.serverTime - cls.realtime;
1133 	deltaDelta = abs( newDelta - cl.serverTimeDelta );
1134 
1135 	if ( deltaDelta > RESET_TIME ) {
1136 		cl.serverTimeDelta = newDelta;
1137 		cl.oldServerTime = cl.snap.serverTime;  // FIXME: is this a problem for cgame?
1138 		cl.serverTime = cl.snap.serverTime;
1139 		if ( cl_showTimeDelta->integer ) {
1140 			Com_Printf( "<RESET> " );
1141 		}
1142 	} else if ( deltaDelta > 100 ) {
1143 		// fast adjust, cut the difference in half
1144 		if ( cl_showTimeDelta->integer ) {
1145 			Com_Printf( "<FAST> " );
1146 		}
1147 		cl.serverTimeDelta = ( cl.serverTimeDelta + newDelta ) >> 1;
1148 	} else {
1149 		// slow drift adjust, only move 1 or 2 msec
1150 
1151 		// if any of the frames between this and the previous snapshot
1152 		// had to be extrapolated, nudge our sense of time back a little
1153 		// the granularity of +1 / -2 is too high for timescale modified frametimes
1154 		if ( com_timescale->value == 0 || com_timescale->value == 1 ) {
1155 			if ( cl.extrapolatedSnapshot ) {
1156 				cl.extrapolatedSnapshot = qfalse;
1157 				cl.serverTimeDelta -= 2;
1158 			} else {
1159 				// otherwise, move our sense of time forward to minimize total latency
1160 				cl.serverTimeDelta++;
1161 			}
1162 		}
1163 	}
1164 
1165 	if ( cl_showTimeDelta->integer ) {
1166 		Com_Printf( "%i ", cl.serverTimeDelta );
1167 	}
1168 }
1169 
1170 
1171 /*
1172 ==================
1173 CL_FirstSnapshot
1174 ==================
1175 */
CL_FirstSnapshot(void)1176 void CL_FirstSnapshot( void ) {
1177 	// ignore snapshots that don't have entities
1178 	if ( cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE ) {
1179 		return;
1180 	}
1181 	clc.state = CA_ACTIVE;
1182 
1183 	// set the timedelta so we are exactly on this first frame
1184 	cl.serverTimeDelta = cl.snap.serverTime - cls.realtime;
1185 	cl.oldServerTime = cl.snap.serverTime;
1186 
1187 	clc.timeDemoBaseTime = cl.snap.serverTime;
1188 
1189 	// if this is the first frame of active play,
1190 	// execute the contents of activeAction now
1191 	// this is to allow scripting a timedemo to start right
1192 	// after loading
1193 	if ( cl_activeAction->string[0] ) {
1194 		Cbuf_AddText( cl_activeAction->string );
1195 		Cvar_Set( "activeAction", "" );
1196 	}
1197 
1198 #ifdef USE_MUMBLE
1199 	if ((cl_useMumble->integer) && !mumble_islinked()) {
1200 		int ret = mumble_link(CLIENT_WINDOW_TITLE);
1201 		Com_Printf("Mumble: Linking to Mumble application %s\n", ret==0?"ok":"failed");
1202 	}
1203 #endif
1204 
1205 #ifdef USE_VOIP
1206 	if (!clc.voipCodecInitialized) {
1207 		int i;
1208 		int error;
1209 
1210 		clc.opusEncoder = opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, &error);
1211 
1212 		if ( error ) {
1213 			Com_DPrintf("VoIP: Error opus_encoder_create %d\n", error);
1214 			return;
1215 		}
1216 
1217 		for (i = 0; i < MAX_CLIENTS; i++) {
1218 			clc.opusDecoder[i] = opus_decoder_create(48000, 1, &error);
1219 			if ( error ) {
1220 				Com_DPrintf("VoIP: Error opus_decoder_create(%d) %d\n", i, error);
1221 				return;
1222 			}
1223 			clc.voipIgnore[i] = qfalse;
1224 			clc.voipGain[i] = 1.0f;
1225 		}
1226 		clc.voipCodecInitialized = qtrue;
1227 		clc.voipMuteAll = qfalse;
1228 		Cmd_AddCommand ("voip", CL_Voip_f);
1229 		Cvar_Set("cl_voipSendTarget", "spatial");
1230 		Com_Memset(clc.voipTargets, ~0, sizeof(clc.voipTargets));
1231 	}
1232 #endif
1233 }
1234 
1235 /*
1236 ==================
1237 CL_SetCGameTime
1238 ==================
1239 */
CL_SetCGameTime(void)1240 void CL_SetCGameTime( void ) {
1241 	// getting a valid frame message ends the connection process
1242 	if ( clc.state != CA_ACTIVE ) {
1243 		if ( clc.state != CA_PRIMED ) {
1244 			return;
1245 		}
1246 		if ( clc.demoplaying ) {
1247 			// we shouldn't get the first snapshot on the same frame
1248 			// as the gamestate, because it causes a bad time skip
1249 			if ( !clc.firstDemoFrameSkipped ) {
1250 				clc.firstDemoFrameSkipped = qtrue;
1251 				return;
1252 			}
1253 			CL_ReadDemoMessage();
1254 		}
1255 		if ( cl.newSnapshots ) {
1256 			cl.newSnapshots = qfalse;
1257 			CL_FirstSnapshot();
1258 		}
1259 		if ( clc.state != CA_ACTIVE ) {
1260 			return;
1261 		}
1262 	}
1263 
1264 	// if we have gotten to this point, cl.snap is guaranteed to be valid
1265 	if ( !cl.snap.valid ) {
1266 		Com_Error( ERR_DROP, "CL_SetCGameTime: !cl.snap.valid" );
1267 	}
1268 
1269 	// allow pause in single player
1270 	if ( sv_paused->integer && CL_CheckPaused() && com_sv_running->integer ) {
1271 		// paused
1272 		return;
1273 	}
1274 
1275 	if ( cl.snap.serverTime < cl.oldFrameServerTime ) {
1276 		// Ridah, if this is a localhost, then we are probably loading a savegame
1277 		if ( !Q_stricmp( clc.servername, "localhost" ) ) {
1278 			// do nothing?
1279 			CL_FirstSnapshot();
1280 		} else {
1281 			Com_Error( ERR_DROP, "cl.snap.serverTime < cl.oldFrameServerTime" );
1282 		}
1283 	}
1284 	cl.oldFrameServerTime = cl.snap.serverTime;
1285 
1286 
1287 	// get our current view of time
1288 
1289 	if ( clc.demoplaying && cl_freezeDemo->integer ) {
1290 		// cl_freezeDemo is used to lock a demo in place for single frame advances
1291 
1292 	} else {
1293 		// cl_timeNudge is a user adjustable cvar that allows more
1294 		// or less latency to be added in the interest of better
1295 		// smoothness or better responsiveness.
1296 		int tn;
1297 
1298 		tn = cl_timeNudge->integer;
1299 		if ( tn < -30 ) {
1300 			tn = -30;
1301 		} else if ( tn > 0 ) {
1302 			tn = 0;
1303 		}
1304 
1305 		cl.serverTime = cls.realtime + cl.serverTimeDelta - tn;
1306 
1307 		// guarantee that time will never flow backwards, even if
1308 		// serverTimeDelta made an adjustment or cl_timeNudge was changed
1309 		if ( cl.serverTime < cl.oldServerTime ) {
1310 			cl.serverTime = cl.oldServerTime;
1311 		}
1312 		cl.oldServerTime = cl.serverTime;
1313 
1314 		// note if we are almost past the latest frame (without timeNudge),
1315 		// so we will try and adjust back a bit when the next snapshot arrives
1316 		if ( cls.realtime + cl.serverTimeDelta >= cl.snap.serverTime - 5 ) {
1317 			cl.extrapolatedSnapshot = qtrue;
1318 		}
1319 	}
1320 
1321 	// if we have gotten new snapshots, drift serverTimeDelta
1322 	// don't do this every frame, or a period of packet loss would
1323 	// make a huge adjustment
1324 	if ( cl.newSnapshots ) {
1325 		CL_AdjustTimeDelta();
1326 	}
1327 
1328 	if ( !clc.demoplaying ) {
1329 		return;
1330 	}
1331 
1332 	// if we are playing a demo back, we can just keep reading
1333 	// messages from the demo file until the cgame definately
1334 	// has valid snapshots to interpolate between
1335 
1336 	// a timedemo will always use a deterministic set of time samples
1337 	// no matter what speed machine it is run on,
1338 	// while a normal demo may have different time samples
1339 	// each time it is played back
1340 	if ( cl_timedemo->integer ) {
1341 		int now = Sys_Milliseconds( );
1342 		int frameDuration;
1343 
1344 		if ( !clc.timeDemoStart ) {
1345 			clc.timeDemoStart = clc.timeDemoLastFrame = now;
1346 			clc.timeDemoMinDuration = INT_MAX;
1347 			clc.timeDemoMaxDuration = 0;
1348 		}
1349 
1350 		frameDuration = now - clc.timeDemoLastFrame;
1351 		clc.timeDemoLastFrame = now;
1352 
1353 		// Ignore the first measurement as it'll always be 0
1354 		if( clc.timeDemoFrames > 0 )
1355 		{
1356 			if( frameDuration > clc.timeDemoMaxDuration )
1357 				clc.timeDemoMaxDuration = frameDuration;
1358 
1359 			if( frameDuration < clc.timeDemoMinDuration )
1360 				clc.timeDemoMinDuration = frameDuration;
1361 
1362 			// 255 ms = about 4fps
1363 			if( frameDuration > UCHAR_MAX )
1364 				frameDuration = UCHAR_MAX;
1365 
1366 			clc.timeDemoDurations[ ( clc.timeDemoFrames - 1 ) %
1367 				MAX_TIMEDEMO_DURATIONS ] = frameDuration;
1368 		}
1369 
1370 		clc.timeDemoFrames++;
1371 		cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50;
1372 	}
1373 
1374 	while ( cl.serverTime >= cl.snap.serverTime ) {
1375 		// feed another messag, which should change
1376 		// the contents of cl.snap
1377 		CL_ReadDemoMessage();
1378 		if ( clc.state != CA_ACTIVE ) {
1379 			return;     // end of demo
1380 		}
1381 	}
1382 
1383 }
1384 
1385 /*
1386 ====================
1387 CL_GetTag
1388 ====================
1389 */
CL_GetTag(int clientNum,char * tagname,orientation_t * or)1390 qboolean CL_GetTag( int clientNum, char *tagname, orientation_t *or ) {
1391 	if ( !cgvm ) {
1392 		return qfalse;
1393 	}
1394 
1395 	if ( VM_IsNative( cgvm ) ) {
1396 		return VM_Call( cgvm, CG_GET_TAG, clientNum, tagname, or );
1397 	} else {
1398 		qboolean foundTag;
1399 		unsigned cgOr;
1400 		unsigned cgTagname;
1401 		int tagnameSize;
1402 
1403 		tagnameSize = strlen( tagname ) + 1;
1404 
1405 		// alloc data on cgame hunk and copy data to it
1406 		cgOr = VM_GetTempMemory( cgvm, sizeof (orientation_t), or );
1407 		cgTagname = VM_GetTempMemory( cgvm, tagnameSize, tagname );
1408 
1409 		if ( !cgOr || !cgTagname ) {
1410 			Com_Printf("WARNING: CL_GetTag: Not enough cgame QVM memory (increase vm_minQvmHunkMegs cvar).\n");
1411 			return qfalse;
1412 		}
1413 
1414 		foundTag = VM_Call( cgvm, CG_GET_TAG, clientNum, cgTagname, cgOr );
1415 
1416 		// copy result back to game memory and free temp in reverse order
1417 		// tagname isn't copied back because it might be a static string.
1418 		VM_FreeTempMemory( cgvm, cgTagname, tagnameSize, NULL );
1419 		VM_FreeTempMemory( cgvm, cgOr, sizeof (orientation_t), or );
1420 
1421 		return foundTag;
1422 	}
1423 }
1424