codemp/cgame/cg_snapshot.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // cg_snapshot.c -- things that happen on snapshot transition,
00004 // not necessarily every single rendered frame
00005 
00006 #include "cg_local.h"
00007 
00008 
00009 
00010 /*
00011 ==================
00012 CG_ResetEntity
00013 ==================
00014 */
00015 static void CG_ResetEntity( centity_t *cent ) {
00016         // if the previous snapshot this entity was updated in is at least
00017         // an event window back in time then we can reset the previous event
00018         if ( cent->snapShotTime < cg.time - EVENT_VALID_MSEC ) {
00019                 cent->previousEvent = 0;
00020         }
00021 
00022         cent->trailTime = cg.snap->serverTime;
00023 
00024         VectorCopy (cent->currentState.origin, cent->lerpOrigin);
00025         VectorCopy (cent->currentState.angles, cent->lerpAngles);
00026 
00027         if (cent->currentState.eFlags & EF_G2ANIMATING)
00028         { //reset the animation state
00029                 cent->pe.torso.animationNumber = -1;
00030                 cent->pe.legs.animationNumber = -1;
00031         }
00032 
00033 #if 0
00034         if (cent->isRagging && (cent->currentState.eFlags & EF_DEAD))
00035         {
00036                 VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, cent->lerpOrigin);
00037         }
00038 #endif
00039 
00040         if ( cent->currentState.eType == ET_PLAYER || cent->currentState.eType == ET_NPC ) {
00041                 CG_ResetPlayerEntity( cent );
00042         }
00043 }
00044 
00045 /*
00046 ===============
00047 CG_TransitionEntity
00048 
00049 cent->nextState is moved to cent->currentState and events are fired
00050 ===============
00051 */
00052 static void CG_TransitionEntity( centity_t *cent ) {
00053         cent->currentState = cent->nextState;
00054         cent->currentValid = qtrue;
00055 
00056         // reset if the entity wasn't in the last frame or was teleported
00057         if ( !cent->interpolate ) {
00058                 CG_ResetEntity( cent );
00059         }
00060 
00061         // clear the next state.  if will be set by the next CG_SetNextSnap
00062         cent->interpolate = qfalse;
00063 
00064         // check for events
00065         CG_CheckEvents( cent );
00066 }
00067 
00068 
00069 /*
00070 ==================
00071 CG_SetInitialSnapshot
00072 
00073 This will only happen on the very first snapshot, or
00074 on tourney restarts.  All other times will use 
00075 CG_TransitionSnapshot instead.
00076 
00077 FIXME: Also called by map_restart?
00078 ==================
00079 */
00080 void CG_SetInitialSnapshot( snapshot_t *snap ) {
00081         int                             i;
00082         centity_t               *cent;
00083         entityState_t   *state;
00084 
00085         cg.snap = snap; 
00086 
00087         if ((cg_entities[snap->ps.clientNum].ghoul2 == NULL) && trap_G2_HaveWeGhoul2Models(cgs.clientinfo[snap->ps.clientNum].ghoul2Model))
00088         {
00089                 trap_G2API_DuplicateGhoul2Instance(cgs.clientinfo[snap->ps.clientNum].ghoul2Model, &cg_entities[snap->ps.clientNum].ghoul2);
00090                 CG_CopyG2WeaponInstance(&cg_entities[snap->ps.clientNum], FIRST_WEAPON, cg_entities[snap->ps.clientNum].ghoul2);
00091                 
00092                 if (trap_G2API_AddBolt(cg_entities[snap->ps.clientNum].ghoul2, 0, "face") == -1)
00093                 { //check now to see if we have this bone for setting anims and such
00094                         cg_entities[snap->ps.clientNum].noFace = qtrue;
00095                 }
00096         }
00097         BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
00098 
00099         // sort out solid entities
00100         CG_BuildSolidList();
00101 
00102         CG_ExecuteNewServerCommands( snap->serverCommandSequence );
00103 
00104         // set our local weapon selection pointer to
00105         // what the server has indicated the current weapon is
00106         CG_Respawn();
00107 
00108         for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
00109                 state = &cg.snap->entities[ i ];
00110                 cent = &cg_entities[ state->number ];
00111 
00112                 memcpy(&cent->currentState, state, sizeof(entityState_t));
00113                 //cent->currentState = *state;
00114                 cent->interpolate = qfalse;
00115                 cent->currentValid = qtrue;
00116 
00117                 CG_ResetEntity( cent );
00118 
00119                 // check for events
00120                 CG_CheckEvents( cent );
00121         }
00122 }
00123 
00124 
00125 /*
00126 ===================
00127 CG_TransitionSnapshot
00128 
00129 The transition point from snap to nextSnap has passed
00130 ===================
00131 */
00132 extern qboolean CG_UsingEWeb(void); //cg_predict.c
00133 static void CG_TransitionSnapshot( void ) {
00134         centity_t                       *cent;
00135         snapshot_t                      *oldFrame;
00136         int                                     i;
00137 
00138         if ( !cg.snap ) {
00139                 CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
00140         }
00141         if ( !cg.nextSnap ) {
00142                 CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
00143         }
00144 
00145         // execute any server string commands before transitioning entities
00146         CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
00147 
00148         // if we had a map_restart, set everthing with initial
00149         if ( !cg.snap ) {
00150         }
00151 
00152         // clear the currentValid flag for all entities in the existing snapshot
00153         for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
00154                 cent = &cg_entities[ cg.snap->entities[ i ].number ];
00155                 cent->currentValid = qfalse;
00156         }
00157 
00158         // move nextSnap to snap and do the transitions
00159         oldFrame = cg.snap;
00160         cg.snap = cg.nextSnap;
00161 
00162         //CG_CheckPlayerG2Weapons(&cg.snap->ps, &cg_entities[cg.snap->ps.clientNum]);
00163         //CG_CheckPlayerG2Weapons(&cg.snap->ps, &cg.predictedPlayerEntity);
00164         BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
00165         cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;
00166 
00167         for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
00168                 cent = &cg_entities[ cg.snap->entities[ i ].number ];
00169                 CG_TransitionEntity( cent );
00170 
00171                 // remember time of snapshot this entity was last updated in
00172                 cent->snapShotTime = cg.snap->serverTime;
00173         }
00174 
00175         cg.nextSnap = NULL;
00176 
00177         // check for playerstate transition events
00178         if ( oldFrame ) {
00179                 playerState_t   *ops, *ps;
00180 
00181                 ops = &oldFrame->ps;
00182                 ps = &cg.snap->ps;
00183                 // teleporting checks are irrespective of prediction
00184                 if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
00185                         cg.thisFrameTeleport = qtrue;   // will be cleared by prediction code
00186                 }
00187 
00188                 // if we are not doing client side movement prediction for any
00189                 // reason, then the client events and view changes will be issued now
00190                 if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)
00191                         || cg_nopredict.integer || cg_synchronousClients.integer || CG_UsingEWeb() ) {
00192                         CG_TransitionPlayerState( ps, ops );
00193                 }
00194         }
00195 
00196 }
00197 
00198 
00199 /*
00200 ===================
00201 CG_SetNextSnap
00202 
00203 A new snapshot has just been read in from the client system.
00204 ===================
00205 */
00206 static void CG_SetNextSnap( snapshot_t *snap ) {
00207         int                                     num;
00208         entityState_t           *es;
00209         centity_t                       *cent;
00210 
00211         cg.nextSnap = snap;
00212 
00213         //CG_CheckPlayerG2Weapons(&cg.snap->ps, &cg_entities[cg.snap->ps.clientNum]);
00214         //CG_CheckPlayerG2Weapons(&cg.snap->ps, &cg.predictedPlayerEntity);
00215         BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
00216         //cg_entities[ cg.snap->ps.clientNum ].interpolate = qtrue;
00217         //No longer want to do this, as the cg_entities[clnum] and cg.predictedPlayerEntity are one in the same.
00218 
00219         // check for extrapolation errors
00220         for ( num = 0 ; num < snap->numEntities ; num++ ) 
00221         {
00222                 es = &snap->entities[num];
00223                 cent = &cg_entities[ es->number ];
00224 
00225                 memcpy(&cent->nextState, es, sizeof(entityState_t));
00226                 //cent->nextState = *es;
00227 
00228                 // if this frame is a teleport, or the entity wasn't in the
00229                 // previous frame, don't interpolate
00230                 if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT )  ) {
00231                         cent->interpolate = qfalse;
00232                 } else {
00233                         cent->interpolate = qtrue;
00234                 }
00235         }
00236 
00237         // if the next frame is a teleport for the playerstate, we
00238         // can't interpolate during demos
00239         if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
00240                 cg.nextFrameTeleport = qtrue;
00241         } else {
00242                 cg.nextFrameTeleport = qfalse;
00243         }
00244 
00245         // if changing follow mode, don't interpolate
00246         if ( cg.nextSnap->ps.clientNum != cg.snap->ps.clientNum ) {
00247                 cg.nextFrameTeleport = qtrue;
00248         }
00249 
00250         // if changing server restarts, don't interpolate
00251         if ( ( cg.nextSnap->snapFlags ^ cg.snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) {
00252                 cg.nextFrameTeleport = qtrue;
00253         }
00254 
00255         // sort out solid entities
00256         CG_BuildSolidList();
00257 }
00258 
00259 
00260 /*
00261 ========================
00262 CG_ReadNextSnapshot
00263 
00264 This is the only place new snapshots are requested
00265 This may increment cgs.processedSnapshotNum multiple
00266 times if the client system fails to return a
00267 valid snapshot.
00268 ========================
00269 */
00270 static snapshot_t *CG_ReadNextSnapshot( void ) {
00271         qboolean        r;
00272         snapshot_t      *dest;
00273 
00274         if ( cg.latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) {
00275                 CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i", 
00276                         cg.latestSnapshotNum, cgs.processedSnapshotNum );
00277         }
00278 
00279         while ( cgs.processedSnapshotNum < cg.latestSnapshotNum ) {
00280                 // decide which of the two slots to load it into
00281                 if ( cg.snap == &cg.activeSnapshots[0] ) {
00282                         dest = &cg.activeSnapshots[1];
00283                 } else {
00284                         dest = &cg.activeSnapshots[0];
00285                 }
00286 
00287                 // try to read the snapshot from the client system
00288                 cgs.processedSnapshotNum++;
00289                 r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
00290 
00291                 // FIXME: why would trap_GetSnapshot return a snapshot with the same server time
00292                 if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) {
00293                         //continue;
00294                 }
00295 
00296                 // if it succeeded, return
00297                 if ( r ) {
00298                         CG_AddLagometerSnapshotInfo( dest );
00299                         return dest;
00300                 }
00301 
00302                 // a GetSnapshot will return failure if the snapshot
00303                 // never arrived, or  is so old that its entities
00304                 // have been shoved off the end of the circular
00305                 // buffer in the client system.
00306 
00307                 // record as a dropped packet
00308                 CG_AddLagometerSnapshotInfo( NULL );
00309 
00310                 // If there are additional snapshots, continue trying to
00311                 // read them.
00312         }
00313 
00314         // nothing left to read
00315         return NULL;
00316 }
00317 
00318 
00319 /*
00320 ============
00321 CG_ProcessSnapshots
00322 
00323 We are trying to set up a renderable view, so determine
00324 what the simulated time is, and try to get snapshots
00325 both before and after that time if available.
00326 
00327 If we don't have a valid cg.snap after exiting this function,
00328 then a 3D game view cannot be rendered.  This should only happen
00329 right after the initial connection.  After cg.snap has been valid
00330 once, it will never turn invalid.
00331 
00332 Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
00333 hasn't arrived yet (it becomes an extrapolating situation instead
00334 of an interpolating one)
00335 
00336 ============
00337 */
00338 void CG_ProcessSnapshots( void ) {
00339         snapshot_t              *snap;
00340         int                             n;
00341 
00342         // see what the latest snapshot the client system has is
00343         trap_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
00344         if ( n != cg.latestSnapshotNum ) {
00345                 if ( n < cg.latestSnapshotNum ) {
00346                         // this should never happen
00347                         CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
00348                 }
00349                 cg.latestSnapshotNum = n;
00350         }
00351 
00352         // If we have yet to receive a snapshot, check for it.
00353         // Once we have gotten the first snapshot, cg.snap will
00354         // always have valid data for the rest of the game
00355         while ( !cg.snap ) {
00356                 snap = CG_ReadNextSnapshot();
00357                 if ( !snap ) {
00358                         // we can't continue until we get a snapshot
00359                         return;
00360                 }
00361 
00362                 // set our weapon selection to what
00363                 // the playerstate is currently using
00364                 if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
00365                         CG_SetInitialSnapshot( snap );
00366                 }
00367         }
00368 
00369         // loop until we either have a valid nextSnap with a serverTime
00370         // greater than cg.time to interpolate towards, or we run
00371         // out of available snapshots
00372         do {
00373                 // if we don't have a nextframe, try and read a new one in
00374                 if ( !cg.nextSnap ) {
00375                         snap = CG_ReadNextSnapshot();
00376 
00377                         // if we still don't have a nextframe, we will just have to
00378                         // extrapolate
00379                         if ( !snap ) {
00380                                 break;
00381                         }
00382 
00383                         CG_SetNextSnap( snap );
00384 
00385 
00386                         // if time went backwards, we have a level restart
00387                         if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
00388                                 CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
00389                         }
00390                 }
00391 
00392                 // if our time is < nextFrame's, we have a nice interpolating state
00393                 if ( cg.time >= cg.snap->serverTime && cg.time < cg.nextSnap->serverTime ) {
00394                         break;
00395                 }
00396 
00397                 // we have passed the transition from nextFrame to frame
00398                 CG_TransitionSnapshot();
00399         } while ( 1 );
00400 
00401         // assert our valid conditions upon exiting
00402         if ( cg.snap == NULL ) {
00403                 CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
00404         }
00405         if ( cg.time < cg.snap->serverTime ) {
00406                 // this can happen right after a vid_restart
00407                 cg.time = cg.snap->serverTime;
00408         }
00409         if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) {
00410                 CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
00411         }
00412 
00413 }
00414