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