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