Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

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

Generated on Thu Aug 25 12:37:25 2005 for Quake III Arena by  doxygen 1.3.9.1