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