client/cl_cgame.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Quake III Arena source code; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // cl_cgame.c  -- client system interaction with client game
00023 
00024 #include "client.h"
00025 
00026 #include "../botlib/botlib.h"
00027 
00028 extern  botlib_export_t *botlib_export;
00029 
00030 extern qboolean loadCamera(const char *name);
00031 extern void startCamera(int time);
00032 extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles);
00033 
00034 /*
00035 ====================
00036 CL_GetGameState
00037 ====================
00038 */
00039 void CL_GetGameState( gameState_t *gs ) {
00040   *gs = cl.gameState;
00041 }
00042 
00043 /*
00044 ====================
00045 CL_GetGlconfig
00046 ====================
00047 */
00048 void CL_GetGlconfig( glconfig_t *glconfig ) {
00049   *glconfig = cls.glconfig;
00050 }
00051 
00052 
00053 /*
00054 ====================
00055 CL_GetUserCmd
00056 ====================
00057 */
00058 qboolean CL_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) {
00059   // cmds[cmdNumber] is the last properly generated command
00060 
00061   // can't return anything that we haven't created yet
00062   if ( cmdNumber > cl.cmdNumber ) {
00063     Com_Error( ERR_DROP, "CL_GetUserCmd: %i >= %i", cmdNumber, cl.cmdNumber );
00064   }
00065 
00066   // the usercmd has been overwritten in the wrapping
00067   // buffer because it is too far out of date
00068   if ( cmdNumber <= cl.cmdNumber - CMD_BACKUP ) {
00069     return qfalse;
00070   }
00071 
00072   *ucmd = cl.cmds[ cmdNumber & CMD_MASK ];
00073 
00074   return qtrue;
00075 }
00076 
00077 int CL_GetCurrentCmdNumber( void ) {
00078   return cl.cmdNumber;
00079 }
00080 
00081 
00082 /*
00083 ====================
00084 CL_GetParseEntityState
00085 ====================
00086 */
00087 qboolean  CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) {
00088   // can't return anything that hasn't been parsed yet
00089   if ( parseEntityNumber >= cl.parseEntitiesNum ) {
00090     Com_Error( ERR_DROP, "CL_GetParseEntityState: %i >= %i",
00091       parseEntityNumber, cl.parseEntitiesNum );
00092   }
00093 
00094   // can't return anything that has been overwritten in the circular buffer
00095   if ( parseEntityNumber <= cl.parseEntitiesNum - MAX_PARSE_ENTITIES ) {
00096     return qfalse;
00097   }
00098 
00099   *state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ];
00100   return qtrue;
00101 }
00102 
00103 /*
00104 ====================
00105 CL_GetCurrentSnapshotNumber
00106 ====================
00107 */
00108 void  CL_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) {
00109   *snapshotNumber = cl.snap.messageNum;
00110   *serverTime = cl.snap.serverTime;
00111 }
00112 
00113 /*
00114 ====================
00115 CL_GetSnapshot
00116 ====================
00117 */
00118 qboolean  CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) {
00119   clSnapshot_t  *clSnap;
00120   int       i, count;
00121 
00122   if ( snapshotNumber > cl.snap.messageNum ) {
00123     Com_Error( ERR_DROP, "CL_GetSnapshot: snapshotNumber > cl.snapshot.messageNum" );
00124   }
00125 
00126   // if the frame has fallen out of the circular buffer, we can't return it
00127   if ( cl.snap.messageNum - snapshotNumber >= PACKET_BACKUP ) {
00128     return qfalse;
00129   }
00130 
00131   // if the frame is not valid, we can't return it
00132   clSnap = &cl.snapshots[snapshotNumber & PACKET_MASK];
00133   if ( !clSnap->valid ) {
00134     return qfalse;
00135   }
00136 
00137   // if the entities in the frame have fallen out of their
00138   // circular buffer, we can't return it
00139   if ( cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES ) {
00140     return qfalse;
00141   }
00142 
00143   // write the snapshot
00144   snapshot->snapFlags = clSnap->snapFlags;
00145   snapshot->serverCommandSequence = clSnap->serverCommandNum;
00146   snapshot->ping = clSnap->ping;
00147   snapshot->serverTime = clSnap->serverTime;
00148   Com_Memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) );
00149   snapshot->ps = clSnap->ps;
00150   count = clSnap->numEntities;
00151   if ( count > MAX_ENTITIES_IN_SNAPSHOT ) {
00152     Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT );
00153     count = MAX_ENTITIES_IN_SNAPSHOT;
00154   }
00155   snapshot->numEntities = count;
00156   for ( i = 0 ; i < count ; i++ ) {
00157     snapshot->entities[i] = 
00158       cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ];
00159   }
00160 
00161   // FIXME: configstring changes and server commands!!!
00162 
00163   return qtrue;
00164 }
00165 
00166 /*
00167 =====================
00168 CL_SetUserCmdValue
00169 =====================
00170 */
00171 void CL_SetUserCmdValue( int userCmdValue, float sensitivityScale ) {
00172   cl.cgameUserCmdValue = userCmdValue;
00173   cl.cgameSensitivity = sensitivityScale;
00174 }
00175 
00176 /*
00177 =====================
00178 CL_AddCgameCommand
00179 =====================
00180 */
00181 void CL_AddCgameCommand( const char *cmdName ) {
00182   Cmd_AddCommand( cmdName, NULL );
00183 }
00184 
00185 /*
00186 =====================
00187 CL_CgameError
00188 =====================
00189 */
00190 void CL_CgameError( const char *string ) {
00191   Com_Error( ERR_DROP, "%s", string );
00192 }
00193 
00194 
00195 /*
00196 =====================
00197 CL_ConfigstringModified
00198 =====================
00199 */
00200 void CL_ConfigstringModified( void ) {
00201   char    *old, *s;
00202   int     i, index;
00203   char    *dup;
00204   gameState_t oldGs;
00205   int     len;
00206 
00207   index = atoi( Cmd_Argv(1) );
00208   if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
00209     Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
00210   }
00211   // get everything after "cs <num>"
00212   s = Cmd_ArgsFrom(2);
00213 
00214   old = cl.gameState.stringData + cl.gameState.stringOffsets[ index ];
00215   if ( !strcmp( old, s ) ) {
00216     return;   // unchanged
00217   }
00218 
00219   // build the new gameState_t
00220   oldGs = cl.gameState;
00221 
00222   Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
00223 
00224   // leave the first 0 for uninitialized strings
00225   cl.gameState.dataCount = 1;
00226     
00227   for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
00228     if ( i == index ) {
00229       dup = s;
00230     } else {
00231       dup = oldGs.stringData + oldGs.stringOffsets[ i ];
00232     }
00233     if ( !dup[0] ) {
00234       continue;   // leave with the default empty string
00235     }
00236 
00237     len = strlen( dup );
00238 
00239     if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
00240       Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
00241     }
00242 
00243     // append it to the gameState string buffer
00244     cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
00245     Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, dup, len + 1 );
00246     cl.gameState.dataCount += len + 1;
00247   }
00248 
00249   if ( index == CS_SYSTEMINFO ) {
00250     // parse serverId and other cvars
00251     CL_SystemInfoChanged();
00252   }
00253 
00254 }
00255 
00256 
00257 /*
00258 ===================
00259 CL_GetServerCommand
00260 
00261 Set up argc/argv for the given command
00262 ===================
00263 */
00264 qboolean CL_GetServerCommand( int serverCommandNumber ) {
00265   char  *s;
00266   char  *cmd;
00267   static char bigConfigString[BIG_INFO_STRING];
00268   int argc;
00269 
00270   // if we have irretrievably lost a reliable command, drop the connection
00271   if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) {
00272     // when a demo record was started after the client got a whole bunch of
00273     // reliable commands then the client never got those first reliable commands
00274     if ( clc.demoplaying )
00275       return qfalse;
00276     Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" );
00277     return qfalse;
00278   }
00279 
00280   if ( serverCommandNumber > clc.serverCommandSequence ) {
00281     Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" );
00282     return qfalse;
00283   }
00284 
00285   s = clc.serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ];
00286   clc.lastExecutedServerCommand = serverCommandNumber;
00287 
00288   Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s );
00289 
00290 rescan:
00291   Cmd_TokenizeString( s );
00292   cmd = Cmd_Argv(0);
00293   argc = Cmd_Argc();
00294 
00295   if ( !strcmp( cmd, "disconnect" ) ) {
00296     // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552
00297     // allow server to indicate why they were disconnected
00298     if ( argc >= 2 )
00299       Com_Error (ERR_SERVERDISCONNECT, va( "Server Disconnected - %s", Cmd_Argv( 1 ) ) );
00300     else
00301       Com_Error (ERR_SERVERDISCONNECT,"Server disconnected\n");
00302   }
00303 
00304   if ( !strcmp( cmd, "bcs0" ) ) {
00305     Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) );
00306     return qfalse;
00307   }
00308 
00309   if ( !strcmp( cmd, "bcs1" ) ) {
00310     s = Cmd_Argv(2);
00311     if( strlen(bigConfigString) + strlen(s) >= BIG_INFO_STRING ) {
00312       Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
00313     }
00314     strcat( bigConfigString, s );
00315     return qfalse;
00316   }
00317 
00318   if ( !strcmp( cmd, "bcs2" ) ) {
00319     s = Cmd_Argv(2);
00320     if( strlen(bigConfigString) + strlen(s) + 1 >= BIG_INFO_STRING ) {
00321       Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
00322     }
00323     strcat( bigConfigString, s );
00324     strcat( bigConfigString, "\"" );
00325     s = bigConfigString;
00326     goto rescan;
00327   }
00328 
00329   if ( !strcmp( cmd, "cs" ) ) {
00330     CL_ConfigstringModified();
00331     // reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString()
00332     Cmd_TokenizeString( s );
00333     return qtrue;
00334   }
00335 
00336   if ( !strcmp( cmd, "map_restart" ) ) {
00337     // clear notify lines and outgoing commands before passing
00338     // the restart to the cgame
00339     Con_ClearNotify();
00340     Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) );
00341     return qtrue;
00342   }
00343 
00344   // the clientLevelShot command is used during development
00345   // to generate 128*128 screenshots from the intermission
00346   // point of levels for the menu system to use
00347   // we pass it along to the cgame to make apropriate adjustments,
00348   // but we also clear the console and notify lines here
00349   if ( !strcmp( cmd, "clientLevelShot" ) ) {
00350     // don't do it if we aren't running the server locally,
00351     // otherwise malicious remote servers could overwrite
00352     // the existing thumbnails
00353     if ( !com_sv_running->integer ) {
00354       return qfalse;
00355     }
00356     // close the console
00357     Con_Close();
00358     // take a special screenshot next frame
00359     Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" );
00360     return qtrue;
00361   }
00362 
00363   // we may want to put a "connect to other server" command here
00364 
00365   // cgame can now act on the command
00366   return qtrue;
00367 }
00368 
00369 
00370 /*
00371 ====================
00372 CL_CM_LoadMap
00373 
00374 Just adds default parameters that cgame doesn't need to know about
00375 ====================
00376 */
00377 void CL_CM_LoadMap( const char *mapname ) {
00378   int   checksum;
00379 
00380   CM_LoadMap( mapname, qtrue, &checksum );
00381 }
00382 
00383 /*
00384 ====================
00385 CL_ShutdonwCGame
00386 
00387 ====================
00388 */
00389 void CL_ShutdownCGame( void ) {
00390   cls.keyCatchers &= ~KEYCATCH_CGAME;
00391   cls.cgameStarted = qfalse;
00392   if ( !cgvm ) {
00393     return;
00394   }
00395   VM_Call( cgvm, CG_SHUTDOWN );
00396   VM_Free( cgvm );
00397   cgvm = NULL;
00398 }
00399 
00400 static int  FloatAsInt( float f ) {
00401   int   temp;
00402 
00403   *(float *)&temp = f;
00404 
00405   return temp;
00406 }
00407 
00408 /*
00409 ====================
00410 CL_CgameSystemCalls
00411 
00412 The cgame module is making a system call
00413 ====================
00414 */
00415 intptr_t CL_CgameSystemCalls( intptr_t *args ) {
00416   switch( args[0] ) {
00417   case CG_PRINT:
00418     Com_Printf( "%s", (const char*)VMA(1) );
00419     return 0;
00420   case CG_ERROR:
00421     Com_Error( ERR_DROP, "%s", (const char*)VMA(1) );
00422     return 0;
00423   case CG_MILLISECONDS:
00424     return Sys_Milliseconds();
00425   case CG_CVAR_REGISTER:
00426     Cvar_Register( VMA(1), VMA(2), VMA(3), args[4] ); 
00427     return 0;
00428   case CG_CVAR_UPDATE:
00429     Cvar_Update( VMA(1) );
00430     return 0;
00431   case CG_CVAR_SET:
00432     Cvar_Set( VMA(1), VMA(2) );
00433     return 0;
00434   case CG_CVAR_VARIABLESTRINGBUFFER:
00435     Cvar_VariableStringBuffer( VMA(1), VMA(2), args[3] );
00436     return 0;
00437   case CG_ARGC:
00438     return Cmd_Argc();
00439   case CG_ARGV:
00440     Cmd_ArgvBuffer( args[1], VMA(2), args[3] );
00441     return 0;
00442   case CG_ARGS:
00443     Cmd_ArgsBuffer( VMA(1), args[2] );
00444     return 0;
00445   case CG_FS_FOPENFILE:
00446     return FS_FOpenFileByMode( VMA(1), VMA(2), args[3] );
00447   case CG_FS_READ:
00448     FS_Read2( VMA(1), args[2], args[3] );
00449     return 0;
00450   case CG_FS_WRITE:
00451     FS_Write( VMA(1), args[2], args[3] );
00452     return 0;
00453   case CG_FS_FCLOSEFILE:
00454     FS_FCloseFile( args[1] );
00455     return 0;
00456   case CG_FS_SEEK:
00457     return FS_Seek( args[1], args[2], args[3] );
00458   case CG_SENDCONSOLECOMMAND:
00459     Cbuf_AddText( VMA(1) );
00460     return 0;
00461   case CG_ADDCOMMAND:
00462     CL_AddCgameCommand( VMA(1) );
00463     return 0;
00464   case CG_REMOVECOMMAND:
00465     Cmd_RemoveCommand( VMA(1) );
00466     return 0;
00467   case CG_SENDCLIENTCOMMAND:
00468     CL_AddReliableCommand( VMA(1) );
00469     return 0;
00470   case CG_UPDATESCREEN:
00471     // this is used during lengthy level loading, so pump message loop
00472 //    Com_EventLoop();  // FIXME: if a server restarts here, BAD THINGS HAPPEN!
00473 // We can't call Com_EventLoop here, a restart will crash and this _does_ happen
00474 // if there is a map change while we are downloading at pk3.
00475 // ZOID
00476     SCR_UpdateScreen();
00477     return 0;
00478   case CG_CM_LOADMAP:
00479     CL_CM_LoadMap( VMA(1) );
00480     return 0;
00481   case CG_CM_NUMINLINEMODELS:
00482     return CM_NumInlineModels();
00483   case CG_CM_INLINEMODEL:
00484     return CM_InlineModel( args[1] );
00485   case CG_CM_TEMPBOXMODEL:
00486     return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qfalse );
00487   case CG_CM_TEMPCAPSULEMODEL:
00488     return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qtrue );
00489   case CG_CM_POINTCONTENTS:
00490     return CM_PointContents( VMA(1), args[2] );
00491   case CG_CM_TRANSFORMEDPOINTCONTENTS:
00492     return CM_TransformedPointContents( VMA(1), args[2], VMA(3), VMA(4) );
00493   case CG_CM_BOXTRACE:
00494     CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qfalse );
00495     return 0;
00496   case CG_CM_CAPSULETRACE:
00497     CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qtrue );
00498     return 0;
00499   case CG_CM_TRANSFORMEDBOXTRACE:
00500     CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qfalse );
00501     return 0;
00502   case CG_CM_TRANSFORMEDCAPSULETRACE:
00503     CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qtrue );
00504     return 0;
00505   case CG_CM_MARKFRAGMENTS:
00506     return re.MarkFragments( args[1], VMA(2), VMA(3), args[4], VMA(5), args[6], VMA(7) );
00507   case CG_S_STARTSOUND:
00508     S_StartSound( VMA(1), args[2], args[3], args[4] );
00509     return 0;
00510   case CG_S_STARTLOCALSOUND:
00511     S_StartLocalSound( args[1], args[2] );
00512     return 0;
00513   case CG_S_CLEARLOOPINGSOUNDS:
00514     S_ClearLoopingSounds(args[1]);
00515     return 0;
00516   case CG_S_ADDLOOPINGSOUND:
00517     S_AddLoopingSound( args[1], VMA(2), VMA(3), args[4] );
00518     return 0;
00519   case CG_S_ADDREALLOOPINGSOUND:
00520     S_AddRealLoopingSound( args[1], VMA(2), VMA(3), args[4] );
00521     return 0;
00522   case CG_S_STOPLOOPINGSOUND:
00523     S_StopLoopingSound( args[1] );
00524     return 0;
00525   case CG_S_UPDATEENTITYPOSITION:
00526     S_UpdateEntityPosition( args[1], VMA(2) );
00527     return 0;
00528   case CG_S_RESPATIALIZE:
00529     S_Respatialize( args[1], VMA(2), VMA(3), args[4] );
00530     return 0;
00531   case CG_S_REGISTERSOUND:
00532     return S_RegisterSound( VMA(1), args[2] );
00533   case CG_S_STARTBACKGROUNDTRACK:
00534     S_StartBackgroundTrack( VMA(1), VMA(2) );
00535     return 0;
00536   case CG_R_LOADWORLDMAP:
00537     re.LoadWorld( VMA(1) );
00538     return 0; 
00539   case CG_R_REGISTERMODEL:
00540     return re.RegisterModel( VMA(1) );
00541   case CG_R_REGISTERSKIN:
00542     return re.RegisterSkin( VMA(1) );
00543   case CG_R_REGISTERSHADER:
00544     return re.RegisterShader( VMA(1) );
00545   case CG_R_REGISTERSHADERNOMIP:
00546     return re.RegisterShaderNoMip( VMA(1) );
00547   case CG_R_REGISTERFONT:
00548     re.RegisterFont( VMA(1), args[2], VMA(3));
00549   case CG_R_CLEARSCENE:
00550     re.ClearScene();
00551     return 0;
00552   case CG_R_ADDREFENTITYTOSCENE:
00553     re.AddRefEntityToScene( VMA(1) );
00554     return 0;
00555   case CG_R_ADDPOLYTOSCENE:
00556     re.AddPolyToScene( args[1], args[2], VMA(3), 1 );
00557     return 0;
00558   case CG_R_ADDPOLYSTOSCENE:
00559     re.AddPolyToScene( args[1], args[2], VMA(3), args[4] );
00560     return 0;
00561   case CG_R_LIGHTFORPOINT:
00562     return re.LightForPoint( VMA(1), VMA(2), VMA(3), VMA(4) );
00563   case CG_R_ADDLIGHTTOSCENE:
00564     re.AddLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
00565     return 0;
00566   case CG_R_ADDADDITIVELIGHTTOSCENE:
00567     re.AddAdditiveLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
00568     return 0;
00569   case CG_R_RENDERSCENE:
00570     re.RenderScene( VMA(1) );
00571     return 0;
00572   case CG_R_SETCOLOR:
00573     re.SetColor( VMA(1) );
00574     return 0;
00575   case CG_R_DRAWSTRETCHPIC:
00576     re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] );
00577     return 0;
00578   case CG_R_MODELBOUNDS:
00579     re.ModelBounds( args[1], VMA(2), VMA(3) );
00580     return 0;
00581   case CG_R_LERPTAG:
00582     return re.LerpTag( VMA(1), args[2], args[3], args[4], VMF(5), VMA(6) );
00583   case CG_GETGLCONFIG:
00584     CL_GetGlconfig( VMA(1) );
00585     return 0;
00586   case CG_GETGAMESTATE:
00587     CL_GetGameState( VMA(1) );
00588     return 0;
00589   case CG_GETCURRENTSNAPSHOTNUMBER:
00590     CL_GetCurrentSnapshotNumber( VMA(1), VMA(2) );
00591     return 0;
00592   case CG_GETSNAPSHOT:
00593     return CL_GetSnapshot( args[1], VMA(2) );
00594   case CG_GETSERVERCOMMAND:
00595     return CL_GetServerCommand( args[1] );
00596   case CG_GETCURRENTCMDNUMBER:
00597     return CL_GetCurrentCmdNumber();
00598   case CG_GETUSERCMD:
00599     return CL_GetUserCmd( args[1], VMA(2) );
00600   case CG_SETUSERCMDVALUE:
00601     CL_SetUserCmdValue( args[1], VMF(2) );
00602     return 0;
00603   case CG_MEMORY_REMAINING:
00604     return Hunk_MemoryRemaining();
00605   case CG_KEY_ISDOWN:
00606     return Key_IsDown( args[1] );
00607   case CG_KEY_GETCATCHER:
00608     return Key_GetCatcher();
00609   case CG_KEY_SETCATCHER:
00610     Key_SetCatcher( args[1] );
00611     return 0;
00612   case CG_KEY_GETKEY:
00613     return Key_GetKey( VMA(1) );
00614 
00615 
00616 
00617   case CG_MEMSET:
00618     Com_Memset( VMA(1), args[2], args[3] );
00619     return 0;
00620   case CG_MEMCPY:
00621     Com_Memcpy( VMA(1), VMA(2), args[3] );
00622     return 0;
00623   case CG_STRNCPY:
00624     strncpy( VMA(1), VMA(2), args[3] );
00625     return args[1];
00626   case CG_SIN:
00627     return FloatAsInt( sin( VMF(1) ) );
00628   case CG_COS:
00629     return FloatAsInt( cos( VMF(1) ) );
00630   case CG_ATAN2:
00631     return FloatAsInt( atan2( VMF(1), VMF(2) ) );
00632   case CG_SQRT:
00633     return FloatAsInt( sqrt( VMF(1) ) );
00634   case CG_FLOOR:
00635     return FloatAsInt( floor( VMF(1) ) );
00636   case CG_CEIL:
00637     return FloatAsInt( ceil( VMF(1) ) );
00638   case CG_ACOS:
00639     return FloatAsInt( Q_acos( VMF(1) ) );
00640 
00641   case CG_PC_ADD_GLOBAL_DEFINE:
00642     return botlib_export->PC_AddGlobalDefine( VMA(1) );
00643   case CG_PC_LOAD_SOURCE:
00644     return botlib_export->PC_LoadSourceHandle( VMA(1) );
00645   case CG_PC_FREE_SOURCE:
00646     return botlib_export->PC_FreeSourceHandle( args[1] );
00647   case CG_PC_READ_TOKEN:
00648     return botlib_export->PC_ReadTokenHandle( args[1], VMA(2) );
00649   case CG_PC_SOURCE_FILE_AND_LINE:
00650     return botlib_export->PC_SourceFileAndLine( args[1], VMA(2), VMA(3) );
00651 
00652   case CG_S_STOPBACKGROUNDTRACK:
00653     S_StopBackgroundTrack();
00654     return 0;
00655 
00656   case CG_REAL_TIME:
00657     return Com_RealTime( VMA(1) );
00658   case CG_SNAPVECTOR:
00659     Sys_SnapVector( VMA(1) );
00660     return 0;
00661 
00662   case CG_CIN_PLAYCINEMATIC:
00663     return CIN_PlayCinematic(VMA(1), args[2], args[3], args[4], args[5], args[6]);
00664 
00665   case CG_CIN_STOPCINEMATIC:
00666     return CIN_StopCinematic(args[1]);
00667 
00668   case CG_CIN_RUNCINEMATIC:
00669     return CIN_RunCinematic(args[1]);
00670 
00671   case CG_CIN_DRAWCINEMATIC:
00672     CIN_DrawCinematic(args[1]);
00673     return 0;
00674 
00675   case CG_CIN_SETEXTENTS:
00676     CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]);
00677     return 0;
00678 
00679   case CG_R_REMAP_SHADER:
00680     re.RemapShader( VMA(1), VMA(2), VMA(3) );
00681     return 0;
00682 
00683 /*
00684   case CG_LOADCAMERA:
00685     return loadCamera(VMA(1));
00686 
00687   case CG_STARTCAMERA:
00688     startCamera(args[1]);
00689     return 0;
00690 
00691   case CG_GETCAMERAINFO:
00692     return getCameraInfo(args[1], VMA(2), VMA(3));
00693 */
00694   case CG_GET_ENTITY_TOKEN:
00695     return re.GetEntityToken( VMA(1), args[2] );
00696   case CG_R_INPVS:
00697     return re.inPVS( VMA(1), VMA(2) );
00698 
00699   default:
00700           assert(0); // bk010102
00701     Com_Error( ERR_DROP, "Bad cgame system trap: %ld", (long int) args[0] );
00702   }
00703   return 0;
00704 }
00705 
00706 
00707 /*
00708 ====================
00709 CL_InitCGame
00710 
00711 Should only be called by CL_StartHunkUsers
00712 ====================
00713 */
00714 void CL_InitCGame( void ) {
00715   const char      *info;
00716   const char      *mapname;
00717   int         t1, t2;
00718   vmInterpret_t   interpret;
00719 
00720   t1 = Sys_Milliseconds();
00721 
00722   // put away the console
00723   Con_Close();
00724 
00725   // find the current mapname
00726   info = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SERVERINFO ];
00727   mapname = Info_ValueForKey( info, "mapname" );
00728   Com_sprintf( cl.mapname, sizeof( cl.mapname ), "maps/%s.bsp", mapname );
00729 
00730   // load the dll or bytecode
00731   if ( cl_connectedToPureServer != 0 ) {
00732     // if sv_pure is set we only allow qvms to be loaded
00733     interpret = VMI_COMPILED;
00734   }
00735   else {
00736     interpret = Cvar_VariableValue( "vm_cgame" );
00737   }
00738   cgvm = VM_Create( "cgame", CL_CgameSystemCalls, interpret );
00739   if ( !cgvm ) {
00740     Com_Error( ERR_DROP, "VM_Create on cgame failed" );
00741   }
00742   cls.state = CA_LOADING;
00743 
00744   // init for this gamestate
00745   // use the lastExecutedServerCommand instead of the serverCommandSequence
00746   // otherwise server commands sent just before a gamestate are dropped
00747   VM_Call( cgvm, CG_INIT, clc.serverMessageSequence, clc.lastExecutedServerCommand, clc.clientNum );
00748 
00749   // reset any CVAR_CHEAT cvars registered by cgame
00750   if ( !clc.demoplaying && !cl_connectedToCheatServer )
00751     Cvar_SetCheatState();
00752 
00753   // we will send a usercmd this frame, which
00754   // will cause the server to send us the first snapshot
00755   cls.state = CA_PRIMED;
00756 
00757   t2 = Sys_Milliseconds();
00758 
00759   Com_Printf( "CL_InitCGame: %5.2f seconds\n", (t2-t1)/1000.0 );
00760 
00761   // have the renderer touch all its images, so they are present
00762   // on the card even if the driver does deferred loading
00763   re.EndRegistration();
00764 
00765   // make sure everything is paged in
00766   if (!Sys_LowPhysicalMemory()) {
00767     Com_TouchMemory();
00768   }
00769 
00770   // clear anything that got printed
00771   Con_ClearNotify ();
00772 }
00773 
00774 
00775 /*
00776 ====================
00777 CL_GameCommand
00778 
00779 See if the current console command is claimed by the cgame
00780 ====================
00781 */
00782 qboolean CL_GameCommand( void ) {
00783   if ( !cgvm ) {
00784     return qfalse;
00785   }
00786 
00787   return VM_Call( cgvm, CG_CONSOLE_COMMAND );
00788 }
00789 
00790 
00791 
00792 /*
00793 =====================
00794 CL_CGameRendering
00795 =====================
00796 */
00797 void CL_CGameRendering( stereoFrame_t stereo ) {
00798   VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl.serverTime, stereo, clc.demoplaying );
00799   VM_Debug( 0 );
00800 }
00801 
00802 
00803 /*
00804 =================
00805 CL_AdjustTimeDelta
00806 
00807 Adjust the clients view of server time.
00808 
00809 We attempt to have cl.serverTime exactly equal the server's view
00810 of time plus the timeNudge, but with variable latencies over
00811 the internet it will often need to drift a bit to match conditions.
00812 
00813 Our ideal time would be to have the adjusted time approach, but not pass,
00814 the very latest snapshot.
00815 
00816 Adjustments are only made when a new snapshot arrives with a rational
00817 latency, which keeps the adjustment process framerate independent and
00818 prevents massive overadjustment during times of significant packet loss
00819 or bursted delayed packets.
00820 =================
00821 */
00822 
00823 #define RESET_TIME  500
00824 
00825 void CL_AdjustTimeDelta( void ) {
00826   int   resetTime;
00827   int   newDelta;
00828   int   deltaDelta;
00829 
00830   cl.newSnapshots = qfalse;
00831 
00832   // the delta never drifts when replaying a demo
00833   if ( clc.demoplaying ) {
00834     return;
00835   }
00836 
00837   // if the current time is WAY off, just correct to the current value
00838   if ( com_sv_running->integer ) {
00839     resetTime = 100;
00840   } else {
00841     resetTime = RESET_TIME;
00842   }
00843 
00844   newDelta = cl.snap.serverTime - cls.realtime;
00845   deltaDelta = abs( newDelta - cl.serverTimeDelta );
00846 
00847   if ( deltaDelta > RESET_TIME ) {
00848     cl.serverTimeDelta = newDelta;
00849     cl.oldServerTime = cl.snap.serverTime;  // FIXME: is this a problem for cgame?
00850     cl.serverTime = cl.snap.serverTime;
00851     if ( cl_showTimeDelta->integer ) {
00852       Com_Printf( "<RESET> " );
00853     }
00854   } else if ( deltaDelta > 100 ) {
00855     // fast adjust, cut the difference in half
00856     if ( cl_showTimeDelta->integer ) {
00857       Com_Printf( "<FAST> " );
00858     }
00859     cl.serverTimeDelta = ( cl.serverTimeDelta + newDelta ) >> 1;
00860   } else {
00861     // slow drift adjust, only move 1 or 2 msec
00862 
00863     // if any of the frames between this and the previous snapshot
00864     // had to be extrapolated, nudge our sense of time back a little
00865     // the granularity of +1 / -2 is too high for timescale modified frametimes
00866     if ( com_timescale->value == 0 || com_timescale->value == 1 ) {
00867       if ( cl.extrapolatedSnapshot ) {
00868         cl.extrapolatedSnapshot = qfalse;
00869         cl.serverTimeDelta -= 2;
00870       } else {
00871         // otherwise, move our sense of time forward to minimize total latency
00872         cl.serverTimeDelta++;
00873       }
00874     }
00875   }
00876 
00877   if ( cl_showTimeDelta->integer ) {
00878     Com_Printf( "%i ", cl.serverTimeDelta );
00879   }
00880 }
00881 
00882 
00883 /*
00884 ==================
00885 CL_FirstSnapshot
00886 ==================
00887 */
00888 void CL_FirstSnapshot( void ) {
00889   // ignore snapshots that don't have entities
00890   if ( cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE ) {
00891     return;
00892   }
00893   cls.state = CA_ACTIVE;
00894 
00895   // set the timedelta so we are exactly on this first frame
00896   cl.serverTimeDelta = cl.snap.serverTime - cls.realtime;
00897   cl.oldServerTime = cl.snap.serverTime;
00898 
00899   clc.timeDemoBaseTime = cl.snap.serverTime;
00900 
00901   // if this is the first frame of active play,
00902   // execute the contents of activeAction now
00903   // this is to allow scripting a timedemo to start right
00904   // after loading
00905   if ( cl_activeAction->string[0] ) {
00906     Cbuf_AddText( cl_activeAction->string );
00907     Cvar_Set( "activeAction", "" );
00908   }
00909   
00910   Sys_BeginProfiling();
00911 }
00912 
00913 /*
00914 ==================
00915 CL_SetCGameTime
00916 ==================
00917 */
00918 void CL_SetCGameTime( void ) {
00919   // getting a valid frame message ends the connection process
00920   if ( cls.state != CA_ACTIVE ) {
00921     if ( cls.state != CA_PRIMED ) {
00922       return;
00923     }
00924     if ( clc.demoplaying ) {
00925       // we shouldn't get the first snapshot on the same frame
00926       // as the gamestate, because it causes a bad time skip
00927       if ( !clc.firstDemoFrameSkipped ) {
00928         clc.firstDemoFrameSkipped = qtrue;
00929         return;
00930       }
00931       CL_ReadDemoMessage();
00932     }
00933     if ( cl.newSnapshots ) {
00934       cl.newSnapshots = qfalse;
00935       CL_FirstSnapshot();
00936     }
00937     if ( cls.state != CA_ACTIVE ) {
00938       return;
00939     }
00940   } 
00941 
00942   // if we have gotten to this point, cl.snap is guaranteed to be valid
00943   if ( !cl.snap.valid ) {
00944     Com_Error( ERR_DROP, "CL_SetCGameTime: !cl.snap.valid" );
00945   }
00946 
00947   // allow pause in single player
00948   if ( sv_paused->integer && CL_CheckPaused() && com_sv_running->integer ) {
00949     // paused
00950     return;
00951   }
00952 
00953   if ( cl.snap.serverTime < cl.oldFrameServerTime ) {
00954     Com_Error( ERR_DROP, "cl.snap.serverTime < cl.oldFrameServerTime" );
00955   }
00956   cl.oldFrameServerTime = cl.snap.serverTime;
00957 
00958 
00959   // get our current view of time
00960 
00961   if ( clc.demoplaying && cl_freezeDemo->integer ) {
00962     // cl_freezeDemo is used to lock a demo in place for single frame advances
00963 
00964   } else {
00965     // cl_timeNudge is a user adjustable cvar that allows more
00966     // or less latency to be added in the interest of better 
00967     // smoothness or better responsiveness.
00968     int tn;
00969     
00970     tn = cl_timeNudge->integer;
00971     if (tn<-30) {
00972       tn = -30;
00973     } else if (tn>30) {
00974       tn = 30;
00975     }
00976 
00977     cl.serverTime = cls.realtime + cl.serverTimeDelta - tn;
00978 
00979     // guarantee that time will never flow backwards, even if
00980     // serverTimeDelta made an adjustment or cl_timeNudge was changed
00981     if ( cl.serverTime < cl.oldServerTime ) {
00982       cl.serverTime = cl.oldServerTime;
00983     }
00984     cl.oldServerTime = cl.serverTime;
00985 
00986     // note if we are almost past the latest frame (without timeNudge),
00987     // so we will try and adjust back a bit when the next snapshot arrives
00988     if ( cls.realtime + cl.serverTimeDelta >= cl.snap.serverTime - 5 ) {
00989       cl.extrapolatedSnapshot = qtrue;
00990     }
00991   }
00992 
00993   // if we have gotten new snapshots, drift serverTimeDelta
00994   // don't do this every frame, or a period of packet loss would
00995   // make a huge adjustment
00996   if ( cl.newSnapshots ) {
00997     CL_AdjustTimeDelta();
00998   }
00999 
01000   if ( !clc.demoplaying ) {
01001     return;
01002   }
01003 
01004   // if we are playing a demo back, we can just keep reading
01005   // messages from the demo file until the cgame definately
01006   // has valid snapshots to interpolate between
01007 
01008   // a timedemo will always use a deterministic set of time samples
01009   // no matter what speed machine it is run on,
01010   // while a normal demo may have different time samples
01011   // each time it is played back
01012   if ( cl_timedemo->integer ) {
01013     if (!clc.timeDemoStart) {
01014       clc.timeDemoStart = Sys_Milliseconds();
01015     }
01016     clc.timeDemoFrames++;
01017     cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50;
01018   }
01019 
01020   while ( cl.serverTime >= cl.snap.serverTime ) {
01021     // feed another messag, which should change
01022     // the contents of cl.snap
01023     CL_ReadDemoMessage();
01024     if ( cls.state != CA_ACTIVE ) {
01025       return;   // end of demo
01026     }
01027   }
01028 
01029 }
01030 
01031 
01032 

Generated on Tue Aug 21 23:50:09 2007 for ioquake by  doxygen 1.5.3