1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 // cl_cgame.c  -- client system interaction with client game
23 
24 #include "client.h"
25 
26 #include "../botlib/botlib.h"
27 
28 #include "libmumblelink.h"
29 
30 extern	botlib_export_t	*botlib_export;
31 
32 extern qboolean loadCamera(const char *name);
33 extern void startCamera(int time);
34 extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles);
35 
36 /*
37 ====================
38 CL_GetGameState
39 ====================
40 */
CL_GetGameState(gameState_t * gs)41 void CL_GetGameState( gameState_t *gs ) {
42 	*gs = cl.gameState;
43 }
44 
45 /*
46 ====================
47 CL_GetGlconfig
48 ====================
49 */
CL_GetGlconfig(glconfig_t * glconfig)50 void CL_GetGlconfig( glconfig_t *glconfig ) {
51 	*glconfig = cls.glconfig;
52 }
53 
54 
55 /*
56 ====================
57 CL_GetUserCmd
58 ====================
59 */
CL_GetUserCmd(int cmdNumber,usercmd_t * ucmd)60 qboolean CL_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) {
61 	// cmds[cmdNumber] is the last properly generated command
62 
63 	// can't return anything that we haven't created yet
64 	if ( cmdNumber > cl.cmdNumber ) {
65 		Com_Error( ERR_DROP, "CL_GetUserCmd: %i >= %i", cmdNumber, cl.cmdNumber );
66 	}
67 
68 	// the usercmd has been overwritten in the wrapping
69 	// buffer because it is too far out of date
70 	if ( cmdNumber <= cl.cmdNumber - CMD_BACKUP ) {
71 		return qfalse;
72 	}
73 
74 	*ucmd = cl.cmds[ cmdNumber & CMD_MASK ];
75 
76 	return qtrue;
77 }
78 
CL_GetCurrentCmdNumber(void)79 int CL_GetCurrentCmdNumber( void ) {
80 	return cl.cmdNumber;
81 }
82 
83 
84 /*
85 ====================
86 CL_GetParseEntityState
87 ====================
88 */
CL_GetParseEntityState(int parseEntityNumber,entityState_t * state)89 qboolean	CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) {
90 	// can't return anything that hasn't been parsed yet
91 	if ( parseEntityNumber >= cl.parseEntitiesNum ) {
92 		Com_Error( ERR_DROP, "CL_GetParseEntityState: %i >= %i",
93 			parseEntityNumber, cl.parseEntitiesNum );
94 	}
95 
96 	// can't return anything that has been overwritten in the circular buffer
97 	if ( parseEntityNumber <= cl.parseEntitiesNum - MAX_PARSE_ENTITIES ) {
98 		return qfalse;
99 	}
100 
101 	*state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ];
102 	return qtrue;
103 }
104 
105 /*
106 ====================
107 CL_GetCurrentSnapshotNumber
108 ====================
109 */
CL_GetCurrentSnapshotNumber(int * snapshotNumber,int * serverTime)110 void	CL_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) {
111 	*snapshotNumber = cl.snap.messageNum;
112 	*serverTime = cl.snap.serverTime;
113 }
114 
115 /*
116 ====================
117 CL_GetSnapshot
118 ====================
119 */
CL_GetSnapshot(int snapshotNumber,snapshot_t * snapshot)120 qboolean	CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) {
121 	clSnapshot_t	*clSnap;
122 	int				i, count;
123 
124 	if ( snapshotNumber > cl.snap.messageNum ) {
125 		Com_Error( ERR_DROP, "CL_GetSnapshot: snapshotNumber > cl.snapshot.messageNum" );
126 	}
127 
128 	// if the frame has fallen out of the circular buffer, we can't return it
129 	if ( cl.snap.messageNum - snapshotNumber >= PACKET_BACKUP ) {
130 		return qfalse;
131 	}
132 
133 	// if the frame is not valid, we can't return it
134 	clSnap = &cl.snapshots[snapshotNumber & PACKET_MASK];
135 	if ( !clSnap->valid ) {
136 		return qfalse;
137 	}
138 
139 	// if the entities in the frame have fallen out of their
140 	// circular buffer, we can't return it
141 	if ( cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES ) {
142 		return qfalse;
143 	}
144 
145 	// write the snapshot
146 	snapshot->snapFlags = clSnap->snapFlags;
147 	snapshot->serverCommandSequence = clSnap->serverCommandNum;
148 	snapshot->ping = clSnap->ping;
149 	snapshot->serverTime = clSnap->serverTime;
150 	Com_Memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) );
151 	snapshot->ps = clSnap->ps;
152 	count = clSnap->numEntities;
153 	if ( count > MAX_ENTITIES_IN_SNAPSHOT ) {
154 		Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT );
155 		count = MAX_ENTITIES_IN_SNAPSHOT;
156 	}
157 	snapshot->numEntities = count;
158 	for ( i = 0 ; i < count ; i++ ) {
159 		snapshot->entities[i] =
160 			cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ];
161 	}
162 
163 	// FIXME: configstring changes and server commands!!!
164 
165 	return qtrue;
166 }
167 
168 /*
169 =====================
170 CL_SetUserCmdValue
171 =====================
172 */
CL_SetUserCmdValue(int userCmdValue,float sensitivityScale)173 void CL_SetUserCmdValue( int userCmdValue, float sensitivityScale ) {
174 	cl.cgameUserCmdValue = userCmdValue;
175 	cl.cgameSensitivity = sensitivityScale;
176 }
177 
178 /*
179 =====================
180 CL_AddCgameCommand
181 =====================
182 */
CL_AddCgameCommand(const char * cmdName)183 void CL_AddCgameCommand( const char *cmdName ) {
184 	Cmd_AddCommand( cmdName, NULL );
185 }
186 
187 /*
188 =====================
189 CL_CgameError
190 =====================
191 */
CL_CgameError(const char * string)192 void CL_CgameError( const char *string ) {
193 	Com_Error( ERR_DROP, "%s", string );
194 }
195 
196 
197 /*
198 =====================
199 CL_ConfigstringModified
200 =====================
201 */
CL_ConfigstringModified(void)202 void CL_ConfigstringModified( void ) {
203 	char		*old, *s;
204 	int			i, index;
205 	char		*dup;
206 	gameState_t	oldGs;
207 	int			len;
208 
209 	index = atoi( Cmd_Argv(1) );
210 	if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
211 		Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
212 	}
213 	// get everything after "cs <num>"
214 	s = Cmd_ArgsFrom(2);
215 
216 	old = cl.gameState.stringData + cl.gameState.stringOffsets[ index ];
217 	if ( !strcmp( old, s ) ) {
218 		return;		// unchanged
219 	}
220 
221 	// build the new gameState_t
222 	oldGs = cl.gameState;
223 
224 	Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
225 
226 	// leave the first 0 for uninitialized strings
227 	cl.gameState.dataCount = 1;
228 
229 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
230 		if ( i == index ) {
231 			dup = s;
232 		} else {
233 			dup = oldGs.stringData + oldGs.stringOffsets[ i ];
234 		}
235 		if ( !dup[0] ) {
236 			continue;		// leave with the default empty string
237 		}
238 
239 		len = strlen( dup );
240 
241 		if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
242 			Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
243 		}
244 
245 		// append it to the gameState string buffer
246 		cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
247 		Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, dup, len + 1 );
248 		cl.gameState.dataCount += len + 1;
249 	}
250 
251 	if ( index == CS_SYSTEMINFO ) {
252 		// parse serverId and other cvars
253 		CL_SystemInfoChanged();
254 	}
255 
256 }
257 
258 
259 /*
260 ===================
261 CL_GetServerCommand
262 
263 Set up argc/argv for the given command
264 ===================
265 */
CL_GetServerCommand(int serverCommandNumber)266 qboolean CL_GetServerCommand( int serverCommandNumber ) {
267 	char	*s;
268 	char	*cmd;
269 	static char bigConfigString[BIG_INFO_STRING];
270 	int argc;
271 
272 	// if we have irretrievably lost a reliable command, drop the connection
273 	if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) {
274 		// when a demo record was started after the client got a whole bunch of
275 		// reliable commands then the client never got those first reliable commands
276 		if ( clc.demoplaying )
277 			return qfalse;
278 		Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" );
279 		return qfalse;
280 	}
281 
282 	if ( serverCommandNumber > clc.serverCommandSequence ) {
283 		Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" );
284 		return qfalse;
285 	}
286 
287 	s = clc.serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ];
288 	clc.lastExecutedServerCommand = serverCommandNumber;
289 
290 	Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s );
291 
292 rescan:
293 	Cmd_TokenizeString( s );
294 	cmd = Cmd_Argv(0);
295 	argc = Cmd_Argc();
296 
297 	if ( !strcmp( cmd, "disconnect" ) ) {
298 		// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552
299 		// allow server to indicate why they were disconnected
300 		if ( argc >= 2 )
301 			Com_Error( ERR_SERVERDISCONNECT, "Server disconnected - %s", Cmd_Argv( 1 ) );
302 		else
303 			Com_Error( ERR_SERVERDISCONNECT, "Server disconnected\n" );
304 	}
305 
306 	if ( !strcmp( cmd, "bcs0" ) ) {
307 		Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) );
308 		return qfalse;
309 	}
310 
311 	if ( !strcmp( cmd, "bcs1" ) ) {
312 		s = Cmd_Argv(2);
313 		if( strlen(bigConfigString) + strlen(s) >= BIG_INFO_STRING ) {
314 			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
315 		}
316 		strcat( bigConfigString, s );
317 		return qfalse;
318 	}
319 
320 	if ( !strcmp( cmd, "bcs2" ) ) {
321 		s = Cmd_Argv(2);
322 		if( strlen(bigConfigString) + strlen(s) + 1 >= BIG_INFO_STRING ) {
323 			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
324 		}
325 		strcat( bigConfigString, s );
326 		strcat( bigConfigString, "\"" );
327 		s = bigConfigString;
328 		goto rescan;
329 	}
330 
331 	if ( !strcmp( cmd, "cs" ) ) {
332 		CL_ConfigstringModified();
333 		// reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString()
334 		Cmd_TokenizeString( s );
335 		return qtrue;
336 	}
337 
338 	if ( !strcmp( cmd, "map_restart" ) ) {
339 		// clear notify lines and outgoing commands before passing
340 		// the restart to the cgame
341 		Con_ClearNotify();
342 		// reparse the string, because Con_ClearNotify() may have done another Cmd_TokenizeString()
343 		Cmd_TokenizeString( s );
344 		Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) );
345 		return qtrue;
346 	}
347 
348 	// the clientLevelShot command is used during development
349 	// to generate 128*128 screenshots from the intermission
350 	// point of levels for the menu system to use
351 	// we pass it along to the cgame to make apropriate adjustments,
352 	// but we also clear the console and notify lines here
353 	if ( !strcmp( cmd, "clientLevelShot" ) ) {
354 		// don't do it if we aren't running the server locally,
355 		// otherwise malicious remote servers could overwrite
356 		// the existing thumbnails
357 		if ( !com_sv_running->integer ) {
358 			return qfalse;
359 		}
360 		// close the console
361 		Con_Close();
362 		// take a special screenshot next frame
363 		Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" );
364 		return qtrue;
365 	}
366 
367 	// we may want to put a "connect to other server" command here
368 
369 	// cgame can now act on the command
370 	return qtrue;
371 }
372 
373 
374 /*
375 ====================
376 CL_CM_LoadMap
377 
378 Just adds default parameters that cgame doesn't need to know about
379 ====================
380 */
CL_CM_LoadMap(const char * mapname)381 void CL_CM_LoadMap( const char *mapname ) {
382 	int		checksum;
383 
384 	CM_LoadMap( mapname, qtrue, &checksum );
385 }
386 
387 /*
388 ====================
389 CL_ShutdonwCGame
390 
391 ====================
392 */
CL_ShutdownCGame(void)393 void CL_ShutdownCGame( void ) {
394 	Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CGAME );
395 	cls.cgameStarted = qfalse;
396 	if ( !cgvm ) {
397 		return;
398 	}
399 	VM_Call( cgvm, CG_SHUTDOWN );
400 	VM_Free( cgvm );
401 	cgvm = NULL;
402 }
403 
FloatAsInt(float f)404 static int	FloatAsInt( float f ) {
405 	floatint_t fi;
406 	fi.f = f;
407 	return fi.i;
408 }
409 
410 /*
411 ====================
412 CL_CgameSystemCalls
413 
414 The cgame module is making a system call
415 ====================
416 */
CL_CgameSystemCalls(intptr_t * args)417 intptr_t CL_CgameSystemCalls( intptr_t *args ) {
418 	switch( args[0] ) {
419 	case CG_PRINT:
420 		Com_Printf( "%s", (const char*)VMA(1) );
421 		return 0;
422 	case CG_ERROR:
423 		Com_Error( ERR_DROP, "%s", (const char*)VMA(1) );
424 		return 0;
425 	case CG_MILLISECONDS:
426 		return Sys_Milliseconds();
427 	case CG_CVAR_REGISTER:
428 		Cvar_Register( VMA(1), VMA(2), VMA(3), args[4] );
429 		return 0;
430 	case CG_CVAR_UPDATE:
431 		Cvar_Update( VMA(1) );
432 		return 0;
433 	case CG_CVAR_SET:
434 		Cvar_Set( VMA(1), VMA(2) );
435 		return 0;
436 	case CG_CVAR_VARIABLESTRINGBUFFER:
437 		Cvar_VariableStringBuffer( VMA(1), VMA(2), args[3] );
438 		return 0;
439 	case CG_ARGC:
440 		return Cmd_Argc();
441 	case CG_ARGV:
442 		Cmd_ArgvBuffer( args[1], VMA(2), args[3] );
443 		return 0;
444 	case CG_ARGS:
445 		Cmd_ArgsBuffer( VMA(1), args[2] );
446 		return 0;
447 	case CG_FS_FOPENFILE:
448 		return FS_FOpenFileByMode( VMA(1), VMA(2), args[3] );
449 	case CG_FS_READ:
450 		FS_Read2( VMA(1), args[2], args[3] );
451 		return 0;
452 	case CG_FS_WRITE:
453 		FS_Write( VMA(1), args[2], args[3] );
454 		return 0;
455 	case CG_FS_FCLOSEFILE:
456 		FS_FCloseFile( args[1] );
457 		return 0;
458 	case CG_FS_SEEK:
459 		return FS_Seek( args[1], args[2], args[3] );
460 	case CG_SENDCONSOLECOMMAND:
461 		Cbuf_AddText( VMA(1) );
462 		return 0;
463 	case CG_ADDCOMMAND:
464 		CL_AddCgameCommand( VMA(1) );
465 		return 0;
466 	case CG_REMOVECOMMAND:
467 		Cmd_RemoveCommand( VMA(1) );
468 		return 0;
469 	case CG_SENDCLIENTCOMMAND:
470 		CL_AddReliableCommand(VMA(1), qfalse);
471 		return 0;
472 	case CG_UPDATESCREEN:
473 		// this is used during lengthy level loading, so pump message loop
474 //		Com_EventLoop();	// FIXME: if a server restarts here, BAD THINGS HAPPEN!
475 // We can't call Com_EventLoop here, a restart will crash and this _does_ happen
476 // if there is a map change while we are downloading at pk3.
477 // ZOID
478 		SCR_UpdateScreen();
479 		return 0;
480 	case CG_CM_LOADMAP:
481 		CL_CM_LoadMap( VMA(1) );
482 		return 0;
483 	case CG_CM_NUMINLINEMODELS:
484 		return CM_NumInlineModels();
485 	case CG_CM_INLINEMODEL:
486 		return CM_InlineModel( args[1] );
487 	case CG_CM_TEMPBOXMODEL:
488 		return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qfalse );
489 	case CG_CM_TEMPCAPSULEMODEL:
490 		return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qtrue );
491 	case CG_CM_POINTCONTENTS:
492 		return CM_PointContents( VMA(1), args[2] );
493 	case CG_CM_TRANSFORMEDPOINTCONTENTS:
494 		return CM_TransformedPointContents( VMA(1), args[2], VMA(3), VMA(4) );
495 	case CG_CM_BOXTRACE:
496 		CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qfalse );
497 		return 0;
498 	case CG_CM_CAPSULETRACE:
499 		CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qtrue );
500 		return 0;
501 	case CG_CM_TRANSFORMEDBOXTRACE:
502 		CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qfalse );
503 		return 0;
504 	case CG_CM_TRANSFORMEDCAPSULETRACE:
505 		CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qtrue );
506 		return 0;
507 	case CG_CM_MARKFRAGMENTS:
508 		return re.MarkFragments( args[1], VMA(2), VMA(3), args[4], VMA(5), args[6], VMA(7) );
509 	case CG_S_STARTSOUND:
510 		S_StartSound( VMA(1), args[2], args[3], args[4] );
511 		return 0;
512 	case CG_S_STARTLOCALSOUND:
513 		S_StartLocalSound( args[1], args[2] );
514 		return 0;
515 	case CG_S_CLEARLOOPINGSOUNDS:
516 		S_ClearLoopingSounds(args[1]);
517 		return 0;
518 	case CG_S_ADDLOOPINGSOUND:
519 		S_AddLoopingSound( args[1], VMA(2), VMA(3), args[4] );
520 		return 0;
521 	case CG_S_ADDREALLOOPINGSOUND:
522 		S_AddRealLoopingSound( args[1], VMA(2), VMA(3), args[4] );
523 		return 0;
524 	case CG_S_STOPLOOPINGSOUND:
525 		S_StopLoopingSound( args[1] );
526 		return 0;
527 	case CG_S_UPDATEENTITYPOSITION:
528 		S_UpdateEntityPosition( args[1], VMA(2) );
529 		return 0;
530 	case CG_S_RESPATIALIZE:
531 		S_Respatialize( args[1], VMA(2), VMA(3), args[4] );
532 		return 0;
533 	case CG_S_REGISTERSOUND:
534 		return S_RegisterSound( VMA(1), args[2] );
535 	case CG_S_STARTBACKGROUNDTRACK:
536 		S_StartBackgroundTrack( VMA(1), VMA(2) );
537 		return 0;
538 	case CG_R_LOADWORLDMAP:
539 		re.LoadWorld( VMA(1) );
540 		return 0;
541 	case CG_R_REGISTERMODEL:
542 		return re.RegisterModel( VMA(1) );
543 	case CG_R_REGISTERSKIN:
544 		return re.RegisterSkin( VMA(1) );
545 	case CG_R_REGISTERSHADER:
546 		return re.RegisterShader( VMA(1) );
547 	case CG_R_REGISTERSHADERNOMIP:
548 		return re.RegisterShaderNoMip( VMA(1) );
549 	case CG_R_REGISTERFONT:
550 		re.RegisterFont( VMA(1), args[2], VMA(3));
551 	case CG_R_CLEARSCENE:
552 		re.ClearScene();
553 		return 0;
554 	case CG_R_ADDREFENTITYTOSCENE:
555 		re.AddRefEntityToScene( VMA(1) );
556 		return 0;
557 	case CG_R_ADDPOLYTOSCENE:
558 		re.AddPolyToScene( args[1], args[2], VMA(3), 1 );
559 		return 0;
560 	case CG_R_ADDPOLYSTOSCENE:
561 		re.AddPolyToScene( args[1], args[2], VMA(3), args[4] );
562 		return 0;
563 	case CG_R_LIGHTFORPOINT:
564 		return re.LightForPoint( VMA(1), VMA(2), VMA(3), VMA(4) );
565 	case CG_R_ADDLIGHTTOSCENE:
566 		re.AddLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
567 		return 0;
568 	case CG_R_ADDADDITIVELIGHTTOSCENE:
569 		re.AddAdditiveLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
570 		return 0;
571 	case CG_R_RENDERSCENE:
572 		re.RenderScene( VMA(1) );
573 		return 0;
574 	case CG_R_SETCOLOR:
575 		re.SetColor( VMA(1) );
576 		return 0;
577 	case CG_R_DRAWSTRETCHPIC:
578 		re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] );
579 		return 0;
580 	case CG_R_MODELBOUNDS:
581 		re.ModelBounds( args[1], VMA(2), VMA(3) );
582 		return 0;
583 	case CG_R_LERPTAG:
584 		return re.LerpTag( VMA(1), args[2], args[3], args[4], VMF(5), VMA(6) );
585 	case CG_GETGLCONFIG:
586 		CL_GetGlconfig( VMA(1) );
587 		return 0;
588 	case CG_GETGAMESTATE:
589 		CL_GetGameState( VMA(1) );
590 		return 0;
591 	case CG_GETCURRENTSNAPSHOTNUMBER:
592 		CL_GetCurrentSnapshotNumber( VMA(1), VMA(2) );
593 		return 0;
594 	case CG_GETSNAPSHOT:
595 		return CL_GetSnapshot( args[1], VMA(2) );
596 	case CG_GETSERVERCOMMAND:
597 		return CL_GetServerCommand( args[1] );
598 	case CG_GETCURRENTCMDNUMBER:
599 		return CL_GetCurrentCmdNumber();
600 	case CG_GETUSERCMD:
601 		return CL_GetUserCmd( args[1], VMA(2) );
602 	case CG_SETUSERCMDVALUE:
603 		CL_SetUserCmdValue( args[1], VMF(2) );
604 		return 0;
605 	case CG_MEMORY_REMAINING:
606 		return Hunk_MemoryRemaining();
607   case CG_KEY_ISDOWN:
608 		return Key_IsDown( args[1] );
609   case CG_KEY_GETCATCHER:
610 		return Key_GetCatcher();
611   case CG_KEY_SETCATCHER:
612 		// Don't allow the cgame module to close the console
613 		Key_SetCatcher( args[1] | ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) );
614     return 0;
615   case CG_KEY_GETKEY:
616 		return Key_GetKey( VMA(1) );
617 
618 
619 
620 	case CG_MEMSET:
621 		Com_Memset( VMA(1), args[2], args[3] );
622 		return 0;
623 	case CG_MEMCPY:
624 		Com_Memcpy( VMA(1), VMA(2), args[3] );
625 		return 0;
626 	case CG_STRNCPY:
627 		strncpy( VMA(1), VMA(2), args[3] );
628 		return args[1];
629 	case CG_SIN:
630 		return FloatAsInt( sin( VMF(1) ) );
631 	case CG_COS:
632 		return FloatAsInt( cos( VMF(1) ) );
633 	case CG_ATAN2:
634 		return FloatAsInt( atan2( VMF(1), VMF(2) ) );
635 	case CG_SQRT:
636 		return FloatAsInt( sqrt( VMF(1) ) );
637 	case CG_FLOOR:
638 		return FloatAsInt( floor( VMF(1) ) );
639 	case CG_CEIL:
640 		return FloatAsInt( ceil( VMF(1) ) );
641 	case CG_ACOS:
642 		return FloatAsInt( Q_acos( VMF(1) ) );
643 
644 	case CG_PC_ADD_GLOBAL_DEFINE:
645 		return botlib_export->PC_AddGlobalDefine( VMA(1) );
646 	case CG_PC_LOAD_SOURCE:
647 		return botlib_export->PC_LoadSourceHandle( VMA(1) );
648 	case CG_PC_FREE_SOURCE:
649 		return botlib_export->PC_FreeSourceHandle( args[1] );
650 	case CG_PC_READ_TOKEN:
651 		return botlib_export->PC_ReadTokenHandle( args[1], VMA(2) );
652 	case CG_PC_SOURCE_FILE_AND_LINE:
653 		return botlib_export->PC_SourceFileAndLine( args[1], VMA(2), VMA(3) );
654 
655 	case CG_S_STOPBACKGROUNDTRACK:
656 		S_StopBackgroundTrack();
657 		return 0;
658 
659 	case CG_REAL_TIME:
660 		return Com_RealTime( VMA(1) );
661 	case CG_SNAPVECTOR:
662 		Sys_SnapVector( VMA(1) );
663 		return 0;
664 
665 	case CG_CIN_PLAYCINEMATIC:
666 	  return CIN_PlayCinematic(VMA(1), args[2], args[3], args[4], args[5], args[6]);
667 
668 	case CG_CIN_STOPCINEMATIC:
669 	  return CIN_StopCinematic(args[1]);
670 
671 	case CG_CIN_RUNCINEMATIC:
672 	  return CIN_RunCinematic(args[1]);
673 
674 	case CG_CIN_DRAWCINEMATIC:
675 	  CIN_DrawCinematic(args[1]);
676 	  return 0;
677 
678 	case CG_CIN_SETEXTENTS:
679 	  CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]);
680 	  return 0;
681 
682 	case CG_R_REMAP_SHADER:
683 		re.RemapShader( VMA(1), VMA(2), VMA(3) );
684 		return 0;
685 
686 /*
687 	case CG_LOADCAMERA:
688 		return loadCamera(VMA(1));
689 
690 	case CG_STARTCAMERA:
691 		startCamera(args[1]);
692 		return 0;
693 
694 	case CG_GETCAMERAINFO:
695 		return getCameraInfo(args[1], VMA(2), VMA(3));
696 */
697 	case CG_GET_ENTITY_TOKEN:
698 		return re.GetEntityToken( VMA(1), args[2] );
699 	case CG_R_INPVS:
700 		return re.inPVS( VMA(1), VMA(2) );
701 
702 	default:
703 	        assert(0);
704 		Com_Error( ERR_DROP, "Bad cgame system trap: %ld", (long int) args[0] );
705 	}
706 	return 0;
707 }
708 
709 
710 /*
711 ====================
712 CL_InitCGame
713 
714 Should only be called by CL_StartHunkUsers
715 ====================
716 */
CL_InitCGame(void)717 void CL_InitCGame( void ) {
718 	const char			*info;
719 	const char			*mapname;
720 	int					t1, t2;
721 	vmInterpret_t		interpret;
722 
723 	t1 = Sys_Milliseconds();
724 
725 	// put away the console
726 	Con_Close();
727 
728 	// find the current mapname
729 	info = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SERVERINFO ];
730 	mapname = Info_ValueForKey( info, "mapname" );
731 	Com_sprintf( cl.mapname, sizeof( cl.mapname ), "maps/%s.bsp", mapname );
732 
733 	// load the dll or bytecode
734 	if ( cl_connectedToPureServer != 0 ) {
735 		// if sv_pure is set we only allow qvms to be loaded
736 		interpret = VMI_COMPILED;
737 	}
738 	else {
739 		interpret = Cvar_VariableValue( "vm_cgame" );
740 	}
741 	cgvm = VM_Create( "cgame", CL_CgameSystemCalls, interpret );
742 	if ( !cgvm ) {
743 		Com_Error( ERR_DROP, "VM_Create on cgame failed" );
744 	}
745 	cls.state = CA_LOADING;
746 
747 	// init for this gamestate
748 	// use the lastExecutedServerCommand instead of the serverCommandSequence
749 	// otherwise server commands sent just before a gamestate are dropped
750 	VM_Call( cgvm, CG_INIT, clc.serverMessageSequence, clc.lastExecutedServerCommand, clc.clientNum );
751 
752 	// reset any CVAR_CHEAT cvars registered by cgame
753 	if ( !clc.demoplaying && !cl_connectedToCheatServer )
754 		Cvar_SetCheatState();
755 
756 	// we will send a usercmd this frame, which
757 	// will cause the server to send us the first snapshot
758 	cls.state = CA_PRIMED;
759 
760 	t2 = Sys_Milliseconds();
761 
762 	Com_Printf( "CL_InitCGame: %5.2f seconds\n", (t2-t1)/1000.0 );
763 
764 	// have the renderer touch all its images, so they are present
765 	// on the card even if the driver does deferred loading
766 	re.EndRegistration();
767 
768 	// make sure everything is paged in
769 	if (!Sys_LowPhysicalMemory()) {
770 		Com_TouchMemory();
771 	}
772 
773 	// clear anything that got printed
774 	Con_ClearNotify ();
775 }
776 
777 
778 /*
779 ====================
780 CL_GameCommand
781 
782 See if the current console command is claimed by the cgame
783 ====================
784 */
CL_GameCommand(void)785 qboolean CL_GameCommand( void ) {
786 	if ( !cgvm ) {
787 		return qfalse;
788 	}
789 
790 	return VM_Call( cgvm, CG_CONSOLE_COMMAND );
791 }
792 
793 
794 
795 /*
796 =====================
797 CL_CGameRendering
798 =====================
799 */
CL_CGameRendering(stereoFrame_t stereo)800 void CL_CGameRendering( stereoFrame_t stereo ) {
801 	VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl.serverTime, stereo, clc.demoplaying );
802 	VM_Debug( 0 );
803 }
804 
805 
806 /*
807 =================
808 CL_AdjustTimeDelta
809 
810 Adjust the clients view of server time.
811 
812 We attempt to have cl.serverTime exactly equal the server's view
813 of time plus the timeNudge, but with variable latencies over
814 the internet it will often need to drift a bit to match conditions.
815 
816 Our ideal time would be to have the adjusted time approach, but not pass,
817 the very latest snapshot.
818 
819 Adjustments are only made when a new snapshot arrives with a rational
820 latency, which keeps the adjustment process framerate independent and
821 prevents massive overadjustment during times of significant packet loss
822 or bursted delayed packets.
823 =================
824 */
825 
826 #define	RESET_TIME	500
827 
CL_AdjustTimeDelta(void)828 void CL_AdjustTimeDelta( void ) {
829 	int		resetTime;
830 	int		newDelta;
831 	int		deltaDelta;
832 
833 	cl.newSnapshots = qfalse;
834 
835 	// the delta never drifts when replaying a demo
836 	if ( clc.demoplaying ) {
837 		return;
838 	}
839 
840 	// if the current time is WAY off, just correct to the current value
841 	if ( com_sv_running->integer ) {
842 		resetTime = 100;
843 	} else {
844 		resetTime = RESET_TIME;
845 	}
846 
847 	newDelta = cl.snap.serverTime - cls.realtime;
848 	deltaDelta = abs( newDelta - cl.serverTimeDelta );
849 
850 	if ( deltaDelta > RESET_TIME ) {
851 		cl.serverTimeDelta = newDelta;
852 		cl.oldServerTime = cl.snap.serverTime;	// FIXME: is this a problem for cgame?
853 		cl.serverTime = cl.snap.serverTime;
854 		if ( cl_showTimeDelta->integer ) {
855 			Com_Printf( "<RESET> " );
856 		}
857 	} else if ( deltaDelta > 100 ) {
858 		// fast adjust, cut the difference in half
859 		if ( cl_showTimeDelta->integer ) {
860 			Com_Printf( "<FAST> " );
861 		}
862 		cl.serverTimeDelta = ( cl.serverTimeDelta + newDelta ) >> 1;
863 	} else {
864 		// slow drift adjust, only move 1 or 2 msec
865 
866 		// if any of the frames between this and the previous snapshot
867 		// had to be extrapolated, nudge our sense of time back a little
868 		// the granularity of +1 / -2 is too high for timescale modified frametimes
869 		if ( com_timescale->value == 0 || com_timescale->value == 1 ) {
870 			if ( cl.extrapolatedSnapshot ) {
871 				cl.extrapolatedSnapshot = qfalse;
872 				cl.serverTimeDelta -= 2;
873 			} else {
874 				// otherwise, move our sense of time forward to minimize total latency
875 				cl.serverTimeDelta++;
876 			}
877 		}
878 	}
879 
880 	if ( cl_showTimeDelta->integer ) {
881 		Com_Printf( "%i ", cl.serverTimeDelta );
882 	}
883 }
884 
885 
886 /*
887 ==================
888 CL_FirstSnapshot
889 ==================
890 */
CL_FirstSnapshot(void)891 void CL_FirstSnapshot( void ) {
892 	// ignore snapshots that don't have entities
893 	if ( cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE ) {
894 		return;
895 	}
896 	cls.state = CA_ACTIVE;
897 
898 	// set the timedelta so we are exactly on this first frame
899 	cl.serverTimeDelta = cl.snap.serverTime - cls.realtime;
900 	cl.oldServerTime = cl.snap.serverTime;
901 
902 	clc.timeDemoBaseTime = cl.snap.serverTime;
903 
904 	// if this is the first frame of active play,
905 	// execute the contents of activeAction now
906 	// this is to allow scripting a timedemo to start right
907 	// after loading
908 	if ( cl_activeAction->string[0] ) {
909 		Cbuf_AddText( cl_activeAction->string );
910 		Cvar_Set( "activeAction", "" );
911 	}
912 
913 #ifdef USE_MUMBLE
914 	if ((cl_useMumble->integer) && !mumble_islinked()) {
915 		int ret = mumble_link(CLIENT_WINDOW_TITLE);
916 		Com_Printf("Mumble: Linking to Mumble application %s\n", ret==0?"ok":"failed");
917 	}
918 #endif
919 
920 #ifdef USE_VOIP
921 	if (!clc.speexInitialized) {
922 		int i;
923 		speex_bits_init(&clc.speexEncoderBits);
924 		speex_bits_reset(&clc.speexEncoderBits);
925 
926 		clc.speexEncoder = speex_encoder_init(&speex_nb_mode);
927 
928 		speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_FRAME_SIZE,
929 		                  &clc.speexFrameSize);
930 		speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_SAMPLING_RATE,
931 		                  &clc.speexSampleRate);
932 
933 		clc.speexPreprocessor = speex_preprocess_state_init(clc.speexFrameSize,
934 		                                                  clc.speexSampleRate);
935 
936 		i = 1;
937 		speex_preprocess_ctl(clc.speexPreprocessor,
938 		                     SPEEX_PREPROCESS_SET_DENOISE, &i);
939 
940 		i = 1;
941 		speex_preprocess_ctl(clc.speexPreprocessor,
942 		                     SPEEX_PREPROCESS_SET_AGC, &i);
943 
944 		for (i = 0; i < MAX_CLIENTS; i++) {
945 			speex_bits_init(&clc.speexDecoderBits[i]);
946 			speex_bits_reset(&clc.speexDecoderBits[i]);
947 			clc.speexDecoder[i] = speex_decoder_init(&speex_nb_mode);
948 			clc.voipIgnore[i] = qfalse;
949 			clc.voipGain[i] = 1.0f;
950 		}
951 		clc.speexInitialized = qtrue;
952 		clc.voipMuteAll = qfalse;
953 		Cmd_AddCommand ("voip", CL_Voip_f);
954 		Cvar_Set("cl_voipSendTarget", "all");
955 		clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0x7FFFFFFF;
956 	}
957 #endif
958 }
959 
960 /*
961 ==================
962 CL_SetCGameTime
963 ==================
964 */
CL_SetCGameTime(void)965 void CL_SetCGameTime( void ) {
966 	// getting a valid frame message ends the connection process
967 	if ( cls.state != CA_ACTIVE ) {
968 		if ( cls.state != CA_PRIMED ) {
969 			return;
970 		}
971 		if ( clc.demoplaying ) {
972 			// we shouldn't get the first snapshot on the same frame
973 			// as the gamestate, because it causes a bad time skip
974 			if ( !clc.firstDemoFrameSkipped ) {
975 				clc.firstDemoFrameSkipped = qtrue;
976 				return;
977 			}
978 			CL_ReadDemoMessage();
979 		}
980 		if ( cl.newSnapshots ) {
981 			cl.newSnapshots = qfalse;
982 			CL_FirstSnapshot();
983 		}
984 		if ( cls.state != CA_ACTIVE ) {
985 			return;
986 		}
987 	}
988 
989 	// if we have gotten to this point, cl.snap is guaranteed to be valid
990 	if ( !cl.snap.valid ) {
991 		Com_Error( ERR_DROP, "CL_SetCGameTime: !cl.snap.valid" );
992 	}
993 
994 	// allow pause in single player
995 	if ( sv_paused->integer && CL_CheckPaused() && com_sv_running->integer ) {
996 		// paused
997 		return;
998 	}
999 
1000 	if ( cl.snap.serverTime < cl.oldFrameServerTime ) {
1001 		Com_Error( ERR_DROP, "cl.snap.serverTime < cl.oldFrameServerTime" );
1002 	}
1003 	cl.oldFrameServerTime = cl.snap.serverTime;
1004 
1005 
1006 	// get our current view of time
1007 
1008 	if ( clc.demoplaying && cl_freezeDemo->integer ) {
1009 		// cl_freezeDemo is used to lock a demo in place for single frame advances
1010 
1011 	} else {
1012 		// cl_timeNudge is a user adjustable cvar that allows more
1013 		// or less latency to be added in the interest of better
1014 		// smoothness or better responsiveness.
1015 		int tn;
1016 
1017 		tn = cl_timeNudge->integer;
1018 		if (tn<-30) {
1019 			tn = -30;
1020 		} else if (tn>30) {
1021 			tn = 30;
1022 		}
1023 
1024 		cl.serverTime = cls.realtime + cl.serverTimeDelta - tn;
1025 
1026 		// guarantee that time will never flow backwards, even if
1027 		// serverTimeDelta made an adjustment or cl_timeNudge was changed
1028 		if ( cl.serverTime < cl.oldServerTime ) {
1029 			cl.serverTime = cl.oldServerTime;
1030 		}
1031 		cl.oldServerTime = cl.serverTime;
1032 
1033 		// note if we are almost past the latest frame (without timeNudge),
1034 		// so we will try and adjust back a bit when the next snapshot arrives
1035 		if ( cls.realtime + cl.serverTimeDelta >= cl.snap.serverTime - 5 ) {
1036 			cl.extrapolatedSnapshot = qtrue;
1037 		}
1038 	}
1039 
1040 	// if we have gotten new snapshots, drift serverTimeDelta
1041 	// don't do this every frame, or a period of packet loss would
1042 	// make a huge adjustment
1043 	if ( cl.newSnapshots ) {
1044 		CL_AdjustTimeDelta();
1045 	}
1046 
1047 	if ( !clc.demoplaying ) {
1048 		return;
1049 	}
1050 
1051 	// if we are playing a demo back, we can just keep reading
1052 	// messages from the demo file until the cgame definately
1053 	// has valid snapshots to interpolate between
1054 
1055 	// a timedemo will always use a deterministic set of time samples
1056 	// no matter what speed machine it is run on,
1057 	// while a normal demo may have different time samples
1058 	// each time it is played back
1059 	if ( cl_timedemo->integer ) {
1060 		int now = Sys_Milliseconds( );
1061 		int frameDuration;
1062 
1063 		if (!clc.timeDemoStart) {
1064 			clc.timeDemoStart = clc.timeDemoLastFrame = now;
1065 			clc.timeDemoMinDuration = INT_MAX;
1066 			clc.timeDemoMaxDuration = 0;
1067 		}
1068 
1069 		frameDuration = now - clc.timeDemoLastFrame;
1070 		clc.timeDemoLastFrame = now;
1071 
1072 		// Ignore the first measurement as it'll always be 0
1073 		if( clc.timeDemoFrames > 0 )
1074 		{
1075 			if( frameDuration > clc.timeDemoMaxDuration )
1076 				clc.timeDemoMaxDuration = frameDuration;
1077 
1078 			if( frameDuration < clc.timeDemoMinDuration )
1079 				clc.timeDemoMinDuration = frameDuration;
1080 
1081 			// 255 ms = about 4fps
1082 			if( frameDuration > UCHAR_MAX )
1083 				frameDuration = UCHAR_MAX;
1084 
1085 			clc.timeDemoDurations[ ( clc.timeDemoFrames - 1 ) %
1086 				MAX_TIMEDEMO_DURATIONS ] = frameDuration;
1087 		}
1088 
1089 		clc.timeDemoFrames++;
1090 		cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50;
1091 	}
1092 
1093 	while ( cl.serverTime >= cl.snap.serverTime ) {
1094 		// feed another messag, which should change
1095 		// the contents of cl.snap
1096 		CL_ReadDemoMessage();
1097 		if ( cls.state != CA_ACTIVE ) {
1098 			return;		// end of demo
1099 		}
1100 	}
1101 
1102 }
1103 
1104 
1105 
1106