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 Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) );
343 return qtrue;
344 }
345
346 // the clientLevelShot command is used during development
347 // to generate 128*128 screenshots from the intermission
348 // point of levels for the menu system to use
349 // we pass it along to the cgame to make apropriate adjustments,
350 // but we also clear the console and notify lines here
351 if ( !strcmp( cmd, "clientLevelShot" ) ) {
352 // don't do it if we aren't running the server locally,
353 // otherwise malicious remote servers could overwrite
354 // the existing thumbnails
355 if ( !com_sv_running->integer ) {
356 return qfalse;
357 }
358 // close the console
359 Con_Close();
360 // take a special screenshot next frame
361 Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" );
362 return qtrue;
363 }
364
365 // we may want to put a "connect to other server" command here
366
367 // cgame can now act on the command
368 return qtrue;
369 }
370
371
372 /*
373 ====================
374 CL_CM_LoadMap
375
376 Just adds default parameters that cgame doesn't need to know about
377 ====================
378 */
CL_CM_LoadMap(const char * mapname)379 void CL_CM_LoadMap( const char *mapname ) {
380 int checksum;
381
382 CM_LoadMap( mapname, qtrue, &checksum );
383 }
384
385 /*
386 ====================
387 CL_ShutdonwCGame
388
389 ====================
390 */
CL_ShutdownCGame(void)391 void CL_ShutdownCGame( void ) {
392 Key_SetCatcher( Key_GetCatcher( ) & ~KEYCATCH_CGAME );
393 cls.cgameStarted = qfalse;
394 if ( !cgvm ) {
395 return;
396 }
397 VM_Call( cgvm, CG_SHUTDOWN );
398 VM_Free( cgvm );
399 cgvm = NULL;
400 }
401
FloatAsInt(float f)402 static int FloatAsInt( float f ) {
403 int temp;
404
405 *(float *)&temp = f;
406
407 return temp;
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) );
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 #if USE_VOIP
921 if (!clc.speexInitialized) {
922 int i;
923 speex_bits_init(&clc.speexEncoderBits);
924 speex_bits_reset(&clc.speexEncoderBits);
925 clc.speexEncoder = speex_encoder_init(&speex_nb_mode);
926 for (i = 0; i < MAX_CLIENTS; i++) {
927 speex_bits_init(&clc.speexDecoderBits[i]);
928 speex_bits_reset(&clc.speexDecoderBits[i]);
929 clc.speexDecoder[i] = speex_decoder_init(&speex_nb_mode);
930 clc.voipIgnore[i] = qfalse;
931 clc.voipGain[i] = 1.0f;
932 }
933 speex_encoder_ctl(clc.speexEncoder, SPEEX_GET_FRAME_SIZE,
934 &clc.speexFrameSize);
935 clc.speexInitialized = qtrue;
936 clc.voipMuteAll = qfalse;
937 Cmd_AddCommand ("voip", CL_Voip_f);
938 Cvar_Set("cl_voipSendTarget", "all");
939 clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0x7FFFFFFF;
940 }
941 #endif
942 }
943
944 /*
945 ==================
946 CL_SetCGameTime
947 ==================
948 */
CL_SetCGameTime(void)949 void CL_SetCGameTime( void ) {
950 // getting a valid frame message ends the connection process
951 if ( cls.state != CA_ACTIVE ) {
952 if ( cls.state != CA_PRIMED ) {
953 return;
954 }
955 if ( clc.demoplaying ) {
956 // we shouldn't get the first snapshot on the same frame
957 // as the gamestate, because it causes a bad time skip
958 if ( !clc.firstDemoFrameSkipped ) {
959 clc.firstDemoFrameSkipped = qtrue;
960 return;
961 }
962 CL_ReadDemoMessage();
963 }
964 if ( cl.newSnapshots ) {
965 cl.newSnapshots = qfalse;
966 CL_FirstSnapshot();
967 }
968 if ( cls.state != CA_ACTIVE ) {
969 return;
970 }
971 }
972
973 // if we have gotten to this point, cl.snap is guaranteed to be valid
974 if ( !cl.snap.valid ) {
975 Com_Error( ERR_DROP, "CL_SetCGameTime: !cl.snap.valid" );
976 }
977
978 // allow pause in single player
979 if ( sv_paused->integer && CL_CheckPaused() && com_sv_running->integer ) {
980 // paused
981 return;
982 }
983
984 if ( cl.snap.serverTime < cl.oldFrameServerTime ) {
985 Com_Error( ERR_DROP, "cl.snap.serverTime < cl.oldFrameServerTime" );
986 }
987 cl.oldFrameServerTime = cl.snap.serverTime;
988
989
990 // get our current view of time
991
992 if ( clc.demoplaying && cl_freezeDemo->integer ) {
993 // cl_freezeDemo is used to lock a demo in place for single frame advances
994
995 } else {
996 // cl_timeNudge is a user adjustable cvar that allows more
997 // or less latency to be added in the interest of better
998 // smoothness or better responsiveness.
999 int tn;
1000
1001 tn = cl_timeNudge->integer;
1002 if (tn<-30) {
1003 tn = -30;
1004 } else if (tn>30) {
1005 tn = 30;
1006 }
1007
1008 cl.serverTime = cls.realtime + cl.serverTimeDelta - tn;
1009
1010 // guarantee that time will never flow backwards, even if
1011 // serverTimeDelta made an adjustment or cl_timeNudge was changed
1012 if ( cl.serverTime < cl.oldServerTime ) {
1013 cl.serverTime = cl.oldServerTime;
1014 }
1015 cl.oldServerTime = cl.serverTime;
1016
1017 // note if we are almost past the latest frame (without timeNudge),
1018 // so we will try and adjust back a bit when the next snapshot arrives
1019 if ( cls.realtime + cl.serverTimeDelta >= cl.snap.serverTime - 5 ) {
1020 cl.extrapolatedSnapshot = qtrue;
1021 }
1022 }
1023
1024 // if we have gotten new snapshots, drift serverTimeDelta
1025 // don't do this every frame, or a period of packet loss would
1026 // make a huge adjustment
1027 if ( cl.newSnapshots ) {
1028 CL_AdjustTimeDelta();
1029 }
1030
1031 if ( !clc.demoplaying ) {
1032 return;
1033 }
1034
1035 // if we are playing a demo back, we can just keep reading
1036 // messages from the demo file until the cgame definately
1037 // has valid snapshots to interpolate between
1038
1039 // a timedemo will always use a deterministic set of time samples
1040 // no matter what speed machine it is run on,
1041 // while a normal demo may have different time samples
1042 // each time it is played back
1043 if ( cl_timedemo->integer ) {
1044 int now = Sys_Milliseconds( );
1045 int frameDuration;
1046
1047 if (!clc.timeDemoStart) {
1048 clc.timeDemoStart = clc.timeDemoLastFrame = now;
1049 clc.timeDemoMinDuration = INT_MAX;
1050 clc.timeDemoMaxDuration = 0;
1051 }
1052
1053 frameDuration = now - clc.timeDemoLastFrame;
1054 clc.timeDemoLastFrame = now;
1055
1056 // Ignore the first measurement as it'll always be 0
1057 if( clc.timeDemoFrames > 0 )
1058 {
1059 if( frameDuration > clc.timeDemoMaxDuration )
1060 clc.timeDemoMaxDuration = frameDuration;
1061
1062 if( frameDuration < clc.timeDemoMinDuration )
1063 clc.timeDemoMinDuration = frameDuration;
1064
1065 // 255 ms = about 4fps
1066 if( frameDuration > UCHAR_MAX )
1067 frameDuration = UCHAR_MAX;
1068
1069 clc.timeDemoDurations[ ( clc.timeDemoFrames - 1 ) %
1070 MAX_TIMEDEMO_DURATIONS ] = frameDuration;
1071 }
1072
1073 clc.timeDemoFrames++;
1074 cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50;
1075 }
1076
1077 while ( cl.serverTime >= cl.snap.serverTime ) {
1078 // feed another messag, which should change
1079 // the contents of cl.snap
1080 CL_ReadDemoMessage();
1081 if ( cls.state != CA_ACTIVE ) {
1082 return; // end of demo
1083 }
1084 }
1085
1086 }
1087
1088
1089
1090