Files
Jedi-Academy/codemp/cgame/cg_snapshot.c
2013-04-04 14:32:05 -07:00

498 lines
14 KiB
C

// Copyright (C) 1999-2000 Id Software, Inc.
//
// cg_snapshot.c -- things that happen on snapshot transition,
// not necessarily every single rendered frame
#include "cg_local.h"
#ifdef _XBOX
#include "../client/cl_data.h"
#endif
/*
==================
CG_ResetEntity
==================
*/
static void CG_ResetEntity( centity_t *cent ) {
// if the previous snapshot this entity was updated in is at least
// an event window back in time then we can reset the previous event
if ( cent->snapShotTime < cg->time - EVENT_VALID_MSEC ) {
#ifdef _XBOX
cent->previousEvent[0] = 0;
cent->previousEvent[1] = 0;
#else
cent->previousEvent = 0;
#endif
}
cent->trailTime = cg->snap->serverTime;
VectorCopy (cent->currentState.origin, cent->lerpOrigin);
VectorCopy (cent->currentState.angles, cent->lerpAngles);
if (cent->currentState.eFlags & EF_G2ANIMATING)
{ //reset the animation state
cent->pe.torso.animationNumber = -1;
cent->pe.legs.animationNumber = -1;
}
#if 0
if (cent->isRagging && (cent->currentState.eFlags & EF_DEAD))
{
VectorAdd(cent->lerpOrigin, cent->lerpOriginOffset, cent->lerpOrigin);
}
#endif
if ( cent->currentState.eType == ET_PLAYER || cent->currentState.eType == ET_NPC ) {
CG_ResetPlayerEntity( cent );
}
}
/*
===============
CG_TransitionEntity
cent->nextState is moved to cent->currentState and events are fired
===============
*/
static void CG_TransitionEntity( centity_t *cent ) {
cent->currentState = cent->nextState;
#ifdef _XBOX
cent->currentValid[ClientManager::ActiveClientNum()] = qtrue;
// reset if the entity wasn't in the last frame or was teleported
if ( !cent->interpolate[ClientManager::ActiveClientNum()] ) {
CG_ResetEntity( cent );
}
// clear the next state. if will be set by the next CG_SetNextSnap
cent->interpolate[ClientManager::ActiveClientNum()] = qfalse;
#else
cent->currentValid = qtrue;
// reset if the entity wasn't in the last frame or was teleported
if ( !cent->interpolate ) {
CG_ResetEntity( cent );
}
// clear the next state. if will be set by the next CG_SetNextSnap
cent->interpolate = qfalse;
#endif
// check for events
CG_CheckEvents( cent );
}
/*
==================
CG_SetInitialSnapshot
This will only happen on the very first snapshot, or
on tourney restarts. All other times will use
CG_TransitionSnapshot instead.
FIXME: Also called by map_restart?
==================
*/
void CG_SetInitialSnapshot( snapshot_t *snap ) {
int i;
centity_t *cent;
entityState_t *state;
cg->snap = snap;
if ((cg_entities[snap->ps.clientNum].ghoul2 == NULL) && trap_G2_HaveWeGhoul2Models(cgs.clientinfo[snap->ps.clientNum].ghoul2Model))
{
trap_G2API_DuplicateGhoul2Instance(cgs.clientinfo[snap->ps.clientNum].ghoul2Model, &cg_entities[snap->ps.clientNum].ghoul2);
CG_CopyG2WeaponInstance(&cg_entities[snap->ps.clientNum], FIRST_WEAPON, cg_entities[snap->ps.clientNum].ghoul2);
if (trap_G2API_AddBolt(cg_entities[snap->ps.clientNum].ghoul2, 0, "face") == -1)
{ //check now to see if we have this bone for setting anims and such
cg_entities[snap->ps.clientNum].noFace = qtrue;
}
}
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
// sort out solid entities
CG_BuildSolidList();
CG_ExecuteNewServerCommands( snap->serverCommandSequence );
// set our local weapon selection pointer to
// what the server has indicated the current weapon is
CG_Respawn();
for ( i = 0 ; i < cg->snap->numEntities ; i++ ) {
state = &cg->snap->entities[ i ];
cent = &cg_entities[ state->number ];
memcpy(&cent->currentState, state, sizeof(entityState_t));
//cent->currentState = *state;
#ifdef _XBOX
cent->interpolate[ClientManager::ActiveClientNum()] = qfalse;
cent->currentValid[ClientManager::ActiveClientNum()] = qtrue;
#else
cent->interpolate = qfalse;
cent->currentValid = qtrue;
#endif
CG_ResetEntity( cent );
// check for events
CG_CheckEvents( cent );
}
}
/*
===================
CG_TransitionSnapshot
The transition point from snap to nextSnap has passed
===================
*/
extern qboolean CG_UsingEWeb(void); //cg_predict.c
static void CG_TransitionSnapshot( void ) {
centity_t *cent;
snapshot_t *oldFrame;
int i;
if ( !cg->snap ) {
CG_Error( "CG_TransitionSnapshot: NULL cg->snap" );
}
if ( !cg->nextSnap ) {
CG_Error( "CG_TransitionSnapshot: NULL cg->nextSnap" );
}
// execute any server string commands before transitioning entities
CG_ExecuteNewServerCommands( cg->nextSnap->serverCommandSequence );
// if we had a map_restart, set everthing with initial
if ( !cg->snap ) {
}
// clear the currentValid flag for all entities in the existing snapshot
for ( i = 0 ; i < cg->snap->numEntities ; i++ ) {
cent = &cg_entities[ cg->snap->entities[ i ].number ];
#ifdef _XBOX
cent->currentValid[ClientManager::ActiveClientNum()] = qfalse;
#else
cent->currentValid = qfalse;
#endif
}
// move nextSnap to snap and do the transitions
oldFrame = cg->snap;
cg->snap = cg->nextSnap;
//CG_CheckPlayerG2Weapons(&cg->snap->ps, &cg_entities[cg->snap->ps.clientNum]);
//CG_CheckPlayerG2Weapons(&cg->snap->ps, &cg->predictedPlayerEntity);
BG_PlayerStateToEntityState( &cg->snap->ps, &cg_entities[ cg->snap->ps.clientNum ].currentState, qfalse );
#ifdef _XBOX
cg_entities[ cg->snap->ps.clientNum ].interpolate[ClientManager::ActiveClientNum()] = qfalse;
#else
cg_entities[ cg->snap->ps.clientNum ].interpolate = qfalse;
#endif
for ( i = 0 ; i < cg->snap->numEntities ; i++ ) {
cent = &cg_entities[ cg->snap->entities[ i ].number ];
CG_TransitionEntity( cent );
// remember time of snapshot this entity was last updated in
cent->snapShotTime = cg->snap->serverTime;
}
cg->nextSnap = NULL;
// check for playerstate transition events
if ( oldFrame ) {
playerState_t *ops, *ps;
ops = &oldFrame->ps;
ps = &cg->snap->ps;
// teleporting checks are irrespective of prediction
if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
cg->thisFrameTeleport = qtrue; // will be cleared by prediction code
}
#ifdef _XBOX
if ( ClientManager::splitScreenMode == qtrue ) {
CG_TransitionPlayerState( ps, ops );
}
else
#endif
// if we are not doing client side movement prediction for any
// reason, then the client events and view changes will be issued now
if ( cg->demoPlayback || (cg->snap->ps.pm_flags & PMF_FOLLOW)
|| cg_nopredict.integer || cg_synchronousClients.integer || CG_UsingEWeb() ) {
CG_TransitionPlayerState( ps, ops );
}
}
}
/*
===================
CG_SetNextSnap
A new snapshot has just been read in from the client system.
===================
*/
static void CG_SetNextSnap( snapshot_t *snap ) {
int num;
entityState_t *es;
centity_t *cent;
cg->nextSnap = snap;
//CG_CheckPlayerG2Weapons(&cg->snap->ps, &cg_entities[cg->snap->ps.clientNum]);
//CG_CheckPlayerG2Weapons(&cg->snap->ps, &cg->predictedPlayerEntity);
BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].nextState, qfalse );
//cg_entities[ cg->snap->ps.clientNum ].interpolate = qtrue;
//No longer want to do this, as the cg_entities[clnum] and cg->predictedPlayerEntity are one in the same.
// check for extrapolation errors
for ( num = 0 ; num < snap->numEntities ; num++ )
{
es = &snap->entities[num];
cent = &cg_entities[ es->number ];
memcpy(&cent->nextState, es, sizeof(entityState_t));
//cent->nextState = *es;
// if this frame is a teleport, or the entity wasn't in the
// previous frame, don't interpolate
#ifdef _XBOX
if ( !cent->currentValid[ClientManager::ActiveClientNum()] || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) {
cent->interpolate[ClientManager::ActiveClientNum()] = qfalse;
} else {
cent->interpolate[ClientManager::ActiveClientNum()] = qtrue;
}
#else
if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ es->eFlags ) & EF_TELEPORT_BIT ) ) {
cent->interpolate = qfalse;
} else {
cent->interpolate = qtrue;
}
#endif
}
// if the next frame is a teleport for the playerstate, we
// can't interpolate during demos
if ( cg->snap && ( ( snap->ps.eFlags ^ cg->snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
cg->nextFrameTeleport = qtrue;
} else {
cg->nextFrameTeleport = qfalse;
}
// if changing follow mode, don't interpolate
if ( cg->nextSnap->ps.clientNum != cg->snap->ps.clientNum ) {
cg->nextFrameTeleport = qtrue;
}
// if changing server restarts, don't interpolate
if ( ( cg->nextSnap->snapFlags ^ cg->snap->snapFlags ) & SNAPFLAG_SERVERCOUNT ) {
cg->nextFrameTeleport = qtrue;
}
// sort out solid entities
CG_BuildSolidList();
}
/*
========================
CG_ReadNextSnapshot
This is the only place new snapshots are requested
This may increment cgs.processedSnapshotNum multiple
times if the client system fails to return a
valid snapshot.
========================
*/
static snapshot_t *CG_ReadNextSnapshot( void ) {
qboolean r;
snapshot_t *dest;
#ifdef _XBOX
if(ClientManager::splitScreenMode == qtrue)
{
if ( cg->latestSnapshotNum > cgs.processedSnapshotNum[ClientManager::ActiveClientNum()] + 1000 ) {
#ifndef FINAL_BUILD
CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
cg->latestSnapshotNum, cgs.processedSnapshotNum[ClientManager::ActiveClientNum()] );
#endif
}
}
else
{
if ( cg->latestSnapshotNum > cgs.processedSnapshotNum[0] + 1000 ) {
#ifndef FINAL_BUILD
CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
cg->latestSnapshotNum, cgs.processedSnapshotNum[0] );
#endif
}
}
#else
if ( cg->latestSnapshotNum > cgs.processedSnapshotNum + 1000 ) {
#ifndef FINAL_BUILD
CG_Printf( "WARNING: CG_ReadNextSnapshot: way out of range, %i > %i",
cg->latestSnapshotNum, cgs.processedSnapshotNum );
#endif
}
#endif
#ifdef _XBOX
int clnum = 0;
if(ClientManager::splitScreenMode == qtrue)
clnum = ClientManager::ActiveClientNum();
while ( cgs.processedSnapshotNum[clnum] < cg->latestSnapshotNum ) {
#else
while ( cgs.processedSnapshotNum < cg->latestSnapshotNum ) {
#endif
// decide which of the two slots to load it into
if ( cg->snap == &cg->activeSnapshots[0] ) {
dest = &cg->activeSnapshots[1];
} else {
dest = &cg->activeSnapshots[0];
}
// try to read the snapshot from the client system
#ifdef _XBOX
cgs.processedSnapshotNum[clnum]++;
r = trap_GetSnapshot( cgs.processedSnapshotNum[clnum], dest );
#else
cgs.processedSnapshotNum++;
r = trap_GetSnapshot( cgs.processedSnapshotNum, dest );
#endif
// FIXME: why would trap_GetSnapshot return a snapshot with the same server time
if ( cg->snap && r && dest->serverTime == cg->snap->serverTime ) {
//continue;
}
// if it succeeded, return
if ( r ) {
// CG_AddLagometerSnapshotInfo( dest );
return dest;
}
// a GetSnapshot will return failure if the snapshot
// never arrived, or is so old that its entities
// have been shoved off the end of the circular
// buffer in the client system.
// record as a dropped packet
// CG_AddLagometerSnapshotInfo( NULL );
// If there are additional snapshots, continue trying to
// read them.
}
// nothing left to read
return NULL;
}
/*
============
CG_ProcessSnapshots
We are trying to set up a renderable view, so determine
what the simulated time is, and try to get snapshots
both before and after that time if available.
If we don't have a valid cg->snap after exiting this function,
then a 3D game view cannot be rendered. This should only happen
right after the initial connection. After cg->snap has been valid
once, it will never turn invalid.
Even if cg->snap is valid, cg->nextSnap may not be, if the snapshot
hasn't arrived yet (it becomes an extrapolating situation instead
of an interpolating one)
============
*/
void CG_ProcessSnapshots( void ) {
snapshot_t *snap;
int n;
// see what the latest snapshot the client system has is
trap_GetCurrentSnapshotNumber( &n, &cg->latestSnapshotTime );
if ( n != cg->latestSnapshotNum ) {
if ( n < cg->latestSnapshotNum ) {
// this should never happen
CG_Error( "CG_ProcessSnapshots: n < cg->latestSnapshotNum" );
}
cg->latestSnapshotNum = n;
}
// If we have yet to receive a snapshot, check for it.
// Once we have gotten the first snapshot, cg->snap will
// always have valid data for the rest of the game
while ( !cg->snap ) {
snap = CG_ReadNextSnapshot();
if ( !snap ) {
// we can't continue until we get a snapshot
return;
}
// set our weapon selection to what
// the playerstate is currently using
if ( !( snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
CG_SetInitialSnapshot( snap );
}
}
// loop until we either have a valid nextSnap with a serverTime
// greater than cg->time to interpolate towards, or we run
// out of available snapshots
do {
// if we don't have a nextframe, try and read a new one in
if ( !cg->nextSnap ) {
snap = CG_ReadNextSnapshot();
// if we still don't have a nextframe, we will just have to
// extrapolate
if ( !snap ) {
break;
}
CG_SetNextSnap( snap );
// if time went backwards, we have a level restart
if ( cg->nextSnap->serverTime < cg->snap->serverTime ) {
CG_Error( "CG_ProcessSnapshots: Server time went backwards" );
}
}
// if our time is < nextFrame's, we have a nice interpolating state
if ( cg->time >= cg->snap->serverTime && cg->time < cg->nextSnap->serverTime ) {
break;
}
// we have passed the transition from nextFrame to frame
CG_TransitionSnapshot();
} while ( 1 );
// assert our valid conditions upon exiting
if ( cg->snap == NULL ) {
CG_Error( "CG_ProcessSnapshots: cg->snap == NULL" );
}
if ( cg->time < cg->snap->serverTime ) {
// this can happen right after a vid_restart
cg->time = cg->snap->serverTime;
}
if ( cg->nextSnap != NULL && cg->nextSnap->serverTime <= cg->time ) {
CG_Error( "CG_ProcessSnapshots: cg->nextSnap->serverTime <= cg->time" );
}
}