772 lines
21 KiB
C++
772 lines
21 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#ifdef CSTRIKE_DLL
|
|
#include "cs_player.h"
|
|
#endif
|
|
#include "icvar.h"
|
|
#include "player.h"
|
|
#include "shareddefs.h"
|
|
#include "studio.h"
|
|
#include "usercmd.h"
|
|
#include "igamesystem.h"
|
|
#include "ilagcompensationmanager.h"
|
|
#include "inetchannelinfo.h"
|
|
#include "util.h"
|
|
#include "utllinkedlist.h"
|
|
#include "BaseAnimatingOverlay.h"
|
|
#include "tier0/vprof.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#define LC_NONE 0
|
|
#define LC_ALIVE ( 1 << 0 )
|
|
|
|
#define LC_ORIGIN_CHANGED ( 1 << 8 )
|
|
#define LC_ANGLES_CHANGED ( 1 << 9 )
|
|
#define LC_SIZE_CHANGED ( 1 << 10 )
|
|
#define LC_ANIMATION_CHANGED ( 1 << 11 )
|
|
#define LC_POSE_PARAMS_CHANGED ( 1 << 12 )
|
|
#define LC_ENCD_CONS_CHANGED ( 1 << 13 )
|
|
|
|
// Default to 1 second max.
|
|
#define MAX_TICKS_SAVED 1000
|
|
|
|
ConVar sv_unlag( "sv_unlag", "1", FCVAR_DEVELOPMENTONLY, "Enables player lag compensation" );
|
|
// Enable by default to avoid some bugs.
|
|
ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "1", 0, "Flushes entity bone cache on lag compensation" );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
|
|
struct LayerRecord
|
|
{
|
|
int m_sequence;
|
|
float m_cycle;
|
|
float m_weight;
|
|
int m_order;
|
|
int m_flags;
|
|
|
|
LayerRecord()
|
|
{
|
|
m_sequence = 0;
|
|
m_cycle = 0;
|
|
m_weight = 0;
|
|
m_order = 0;
|
|
m_flags = 0;
|
|
}
|
|
};
|
|
|
|
struct LagRecord
|
|
{
|
|
public:
|
|
LagRecord()
|
|
{
|
|
m_fFlags = LC_NONE;
|
|
m_vecOrigin.Init();
|
|
m_vecAngles.Init();
|
|
m_vecMinsPreScaled.Init();
|
|
m_vecMaxsPreScaled.Init();
|
|
m_flSimulationTime = -1;
|
|
m_flAnimTime = -1;
|
|
m_masterSequence = 0;
|
|
m_masterCycle = 0;
|
|
|
|
for ( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex )
|
|
{
|
|
m_layerRecords[layerIndex] = {};
|
|
}
|
|
|
|
for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ )
|
|
{
|
|
m_poseParameters[i] = 0;
|
|
}
|
|
|
|
for ( int i = 0; i < MAXSTUDIOBONECTRLS; i++ )
|
|
{
|
|
m_encodedControllers[i] = 0;
|
|
}
|
|
#ifdef CSTRIKE_DLL
|
|
m_angRenderAngles.Init();
|
|
#endif
|
|
}
|
|
|
|
LagRecord( const LagRecord& src )
|
|
{
|
|
m_fFlags = src.m_fFlags;
|
|
m_vecOrigin = src.m_vecOrigin;
|
|
m_vecAngles = src.m_vecAngles;
|
|
m_vecMinsPreScaled = src.m_vecMinsPreScaled;
|
|
m_vecMaxsPreScaled = src.m_vecMaxsPreScaled;
|
|
m_flSimulationTime = src.m_flSimulationTime;
|
|
m_flAnimTime = src.m_flAnimTime;
|
|
|
|
for ( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex )
|
|
{
|
|
m_layerRecords[layerIndex] = src.m_layerRecords[layerIndex];
|
|
}
|
|
|
|
m_masterSequence = src.m_masterSequence;
|
|
m_masterCycle = src.m_masterCycle;
|
|
|
|
for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ )
|
|
{
|
|
m_poseParameters[i] = src.m_poseParameters[i];
|
|
}
|
|
|
|
for ( int i = 0; i < MAXSTUDIOBONECTRLS; i++ )
|
|
{
|
|
m_encodedControllers[i] = src.m_encodedControllers[i];
|
|
}
|
|
|
|
#ifdef CSTRIKE_DLL
|
|
m_angRenderAngles = src.m_angRenderAngles;
|
|
#endif
|
|
}
|
|
|
|
// Did player die this frame
|
|
int m_fFlags;
|
|
|
|
// Player position, orientation and bbox
|
|
Vector m_vecOrigin;
|
|
QAngle m_vecAngles;
|
|
Vector m_vecMinsPreScaled;
|
|
Vector m_vecMaxsPreScaled;
|
|
|
|
float m_flSimulationTime;
|
|
float m_flAnimTime;
|
|
|
|
// Player animation details, so we can get the legs in the right spot.
|
|
LayerRecord m_layerRecords[MAX_LAYER_RECORDS];
|
|
int m_masterSequence;
|
|
float m_masterCycle;
|
|
float m_poseParameters[MAXSTUDIOPOSEPARAM];
|
|
float m_encodedControllers[MAXSTUDIOBONECTRLS];
|
|
#ifdef CSTRIKE_DLL
|
|
QAngle m_angRenderAngles;
|
|
#endif
|
|
};
|
|
|
|
//
|
|
// Try to take the player from his current origin to vWantedPos.
|
|
// If it can't get there, leave the player where he is.
|
|
//
|
|
|
|
ConVar sv_unlag_debug( "sv_unlag_debug", "0" );
|
|
|
|
float g_flFractionScale = 0.95;
|
|
|
|
static void RestorePlayerTo( CBasePlayer* pPlayer, const Vector& vWantedPos )
|
|
{
|
|
// Try to move to the wanted position from our current position.
|
|
trace_t tr;
|
|
VPROF_BUDGET( "RestorePlayerTo", "CLagCompensationManager" );
|
|
UTIL_TraceEntity( pPlayer, vWantedPos, vWantedPos, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &tr );
|
|
if ( tr.startsolid || tr.allsolid )
|
|
{
|
|
if ( sv_unlag_debug.GetBool() )
|
|
{
|
|
DevMsg( "RestorePlayerTo() could not restore player position for client \"%s\" ( %.1f %.1f %.1f )\n",
|
|
pPlayer->GetPlayerName(),
|
|
vWantedPos.x,
|
|
vWantedPos.y,
|
|
vWantedPos.z );
|
|
}
|
|
|
|
UTIL_TraceEntity( pPlayer,
|
|
pPlayer->GetAbsOrigin(),
|
|
vWantedPos,
|
|
MASK_PLAYERSOLID,
|
|
pPlayer,
|
|
COLLISION_GROUP_PLAYER_MOVEMENT,
|
|
&tr );
|
|
if ( tr.startsolid || tr.allsolid )
|
|
{
|
|
// In this case, the guy got stuck back wherever we lag compensated him to. Nasty.
|
|
|
|
if ( sv_unlag_debug.GetBool() )
|
|
{
|
|
DevMsg( " restore failed entirely\n" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We can get to a valid place, but not all the way back to where we were.
|
|
Vector vPos;
|
|
VectorLerp( pPlayer->GetAbsOrigin(), vWantedPos, tr.fraction * g_flFractionScale, vPos );
|
|
UTIL_SetOrigin( pPlayer, vPos, true );
|
|
|
|
if ( sv_unlag_debug.GetBool() )
|
|
{
|
|
DevMsg( " restore got most of the way\n" );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Cool, the player can go back to whence he came.
|
|
UTIL_SetOrigin( pPlayer, tr.endpos, true );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
class CLagCompensationManager : public CAutoGameSystemPerFrame,
|
|
public ILagCompensationManager
|
|
{
|
|
public:
|
|
CLagCompensationManager( const char* name )
|
|
{
|
|
}
|
|
|
|
// IServerSystem stuff
|
|
virtual void Shutdown()
|
|
{
|
|
ClearHistory();
|
|
}
|
|
|
|
virtual void LevelShutdownPostEntity()
|
|
{
|
|
ClearHistory();
|
|
}
|
|
|
|
// ILagCompensationManager stuff
|
|
|
|
// Called during player movement to set up/restore after lag compensation
|
|
void StartLagCompensation( CBasePlayer* player, CUserCmd* cmd );
|
|
void FinishLagCompensation( CBasePlayer* player );
|
|
virtual void TrackPlayerData( CBasePlayer* pPlayer );
|
|
|
|
private:
|
|
void BacktrackPlayer( CBasePlayer* player, CUserCmd* cmd );
|
|
|
|
void ClearHistory()
|
|
{
|
|
for ( int i = 0; i < MAX_EDICTS; i++ )
|
|
{
|
|
m_EntityTrack[i].Clear();
|
|
}
|
|
}
|
|
|
|
// keep a list of lag records for each entities
|
|
CUtlCircularBuffer< LagRecord, MAX_TICKS_SAVED > m_EntityTrack[MAX_EDICTS];
|
|
|
|
// Scratchpad for determining what needs to be restored
|
|
CBitVec< MAX_EDICTS > m_RestorePlayer;
|
|
bool m_bNeedToRestore;
|
|
|
|
LagRecord m_RestoreData[MAX_EDICTS]; // entities data before we moved him back
|
|
LagRecord m_ChangeData[MAX_EDICTS]; // entities data where we moved him back
|
|
};
|
|
|
|
static CLagCompensationManager g_LagCompensationManager( "CLagCompensationManager" );
|
|
ILagCompensationManager* lagcompensation = &g_LagCompensationManager;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Called once per frame after all entities have had a chance to think
|
|
//-----------------------------------------------------------------------------
|
|
void CLagCompensationManager::TrackPlayerData( CBasePlayer* pPlayer )
|
|
{
|
|
if ( ( gpGlobals->maxClients <= 1 ) || !sv_unlag.GetBool() )
|
|
{
|
|
ClearHistory();
|
|
return;
|
|
}
|
|
|
|
VPROF_BUDGET( "TrackPlayerData", "CLagCompensationManager" );
|
|
|
|
// remove all records before that time:
|
|
auto track = &m_EntityTrack[pPlayer->entindex()];
|
|
|
|
// add new record to player track
|
|
LagRecord record;
|
|
|
|
record.m_fFlags = LC_NONE;
|
|
if ( pPlayer->IsAlive() )
|
|
{
|
|
record.m_fFlags |= LC_ALIVE;
|
|
}
|
|
|
|
record.m_flSimulationTime = pPlayer->GetSimulationTime();
|
|
record.m_flAnimTime = pPlayer->GetAnimTime();
|
|
record.m_vecAngles = pPlayer->GetAbsAngles();
|
|
record.m_vecOrigin = pPlayer->GetAbsOrigin();
|
|
record.m_vecMinsPreScaled = pPlayer->CollisionProp()->OBBMinsPreScaled();
|
|
record.m_vecMaxsPreScaled = pPlayer->CollisionProp()->OBBMaxsPreScaled();
|
|
|
|
int layerCount = pPlayer->GetNumAnimOverlays();
|
|
|
|
for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
|
|
{
|
|
CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex );
|
|
if ( currentLayer )
|
|
{
|
|
record.m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle;
|
|
record.m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder;
|
|
record.m_layerRecords[layerIndex].m_sequence = currentLayer->m_nSequence;
|
|
record.m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight;
|
|
record.m_layerRecords[layerIndex].m_flags = currentLayer->m_fFlags;
|
|
}
|
|
}
|
|
|
|
record.m_masterSequence = pPlayer->GetSequence();
|
|
record.m_masterCycle = pPlayer->GetCycle();
|
|
|
|
CStudioHdr* hdr = pPlayer->GetModelPtr();
|
|
|
|
if ( hdr )
|
|
{
|
|
for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
|
|
{
|
|
record.m_poseParameters[paramIndex] = pPlayer->GetPoseParameterArray()[paramIndex];
|
|
}
|
|
}
|
|
|
|
if ( hdr )
|
|
{
|
|
for ( int boneIndex = 0; boneIndex < hdr->GetNumBoneControllers(); boneIndex++ )
|
|
{
|
|
record.m_encodedControllers[boneIndex] = pPlayer->GetBoneControllerArray()[boneIndex];
|
|
}
|
|
}
|
|
|
|
#ifdef CSTRIKE_DLL
|
|
const auto csPlayer = ToCSPlayer( pPlayer );
|
|
|
|
if ( csPlayer )
|
|
{
|
|
record.m_angRenderAngles = csPlayer->m_angRenderAngles;
|
|
}
|
|
#endif
|
|
|
|
track->Push( record );
|
|
}
|
|
|
|
// Called during player movement to set up/restore after lag compensation
|
|
void CLagCompensationManager::StartLagCompensation( CBasePlayer* player, CUserCmd* cmd )
|
|
{
|
|
// Assume no players need to be restored
|
|
m_RestorePlayer.ClearAll();
|
|
m_bNeedToRestore = false;
|
|
|
|
if ( !player->m_bLagCompensation // Player not wanting lag compensation
|
|
|| ( gpGlobals->maxClients <= 1 ) // no lag compensation in single player
|
|
|| !sv_unlag.GetBool() // disabled by server admin
|
|
|| player->IsBot() // not for bots
|
|
|| player->IsObserver() // not for spectators
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// NOTE: Put this here so that it won't show up in single player mode.
|
|
VPROF_BUDGET( "StartLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
|
Q_memset( m_RestoreData, 0, sizeof( m_RestoreData ) );
|
|
Q_memset( m_ChangeData, 0, sizeof( m_ChangeData ) );
|
|
|
|
// Iterate all active players
|
|
const CBitVec< MAX_EDICTS >* pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 );
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBasePlayer* pPlayer = UTIL_PlayerByIndex( i );
|
|
|
|
if ( !pPlayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Don't lag compensate yourself you loser...
|
|
if ( player == pPlayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Custom checks for if things should lag compensate (based on things like what team the player is on).
|
|
if ( !player->WantsLagCompensationOnEntity( pPlayer, cmd, pEntityTransmitBits ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Move other player back in time
|
|
BacktrackPlayer( pPlayer, cmd );
|
|
}
|
|
}
|
|
|
|
void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* cmd )
|
|
{
|
|
VPROF_BUDGET( "BacktrackPlayer", "CLagCompensationManager" );
|
|
|
|
Vector org;
|
|
Vector minsPreScaled;
|
|
Vector maxsPreScaled;
|
|
QAngle ang;
|
|
#ifdef CSTRIKE_DLL
|
|
QAngle renderAngles;
|
|
#endif
|
|
LagRecord* prevRecordSim;
|
|
LagRecord* recordSim;
|
|
LagRecord* recordAnim;
|
|
|
|
#ifdef CSTRIKE_DLL
|
|
auto csPlayer = ToCSPlayer( pPlayer );
|
|
#endif
|
|
|
|
int pl_index = pPlayer->entindex();
|
|
|
|
float flTargetSimTime = cmd->simulationdata[pl_index].sim_time;
|
|
float flTargetAnimTime = cmd->simulationdata[pl_index].anim_time;
|
|
|
|
// get track history of this player
|
|
auto track = &m_EntityTrack[pl_index];
|
|
bool foundSim = false;
|
|
bool foundAnim = false;
|
|
|
|
for ( int i = 0; i < MAX_TICKS_SAVED; i++ )
|
|
{
|
|
recordSim = track->Get( i );
|
|
|
|
if ( !recordSim )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( !( recordSim->m_fFlags & LC_ALIVE ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( flTargetSimTime == recordSim->m_flSimulationTime )
|
|
{
|
|
foundSim = true;
|
|
break;
|
|
}
|
|
|
|
if ( recordSim->m_flSimulationTime < flTargetSimTime )
|
|
{
|
|
foundSim = true;
|
|
prevRecordSim = track->Get( i - 1 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < MAX_TICKS_SAVED; i++ )
|
|
{
|
|
recordAnim = track->Get( i );
|
|
|
|
if ( !recordAnim )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( recordAnim->m_flAnimTime == flTargetAnimTime )
|
|
{
|
|
foundAnim = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert( recordAnim );
|
|
Assert( recordSim );
|
|
|
|
if ( !foundAnim || !foundSim )
|
|
{
|
|
if ( sv_unlag_debug.GetBool() )
|
|
{
|
|
DevMsg( "No valid positions in history for BacktrackPlayer client ( %s )\n", pPlayer->GetPlayerName() );
|
|
}
|
|
|
|
return; // that should never happen
|
|
}
|
|
|
|
float fracSim = 0.0f;
|
|
if ( prevRecordSim && ( recordSim->m_flSimulationTime < flTargetSimTime )
|
|
&& ( recordSim->m_flSimulationTime < prevRecordSim->m_flSimulationTime ) )
|
|
{
|
|
// we didn't find the exact time but have a valid previous record
|
|
// so interpolate between these two records;
|
|
|
|
Assert( prevRecordSim->m_flSimulationTime > recordSim->m_flSimulationTime );
|
|
Assert( flTargetSimTime < prevRecordSim->m_flSimulationTime );
|
|
|
|
// calc fraction between both records
|
|
fracSim = float( ( double( flTargetSimTime ) - double( recordSim->m_flSimulationTime ) )
|
|
/ ( double( prevRecordSim->m_flSimulationTime ) - double( recordSim->m_flSimulationTime ) ) );
|
|
|
|
Assert( fracSim > 0 && fracSim < 1 ); // should never extrapolate
|
|
|
|
ang = Lerp( fracSim, recordSim->m_vecAngles, prevRecordSim->m_vecAngles );
|
|
org = Lerp( fracSim, recordSim->m_vecOrigin, prevRecordSim->m_vecOrigin );
|
|
minsPreScaled = Lerp( fracSim, recordSim->m_vecMinsPreScaled, prevRecordSim->m_vecMinsPreScaled );
|
|
maxsPreScaled = Lerp( fracSim, recordSim->m_vecMaxsPreScaled, prevRecordSim->m_vecMaxsPreScaled );
|
|
}
|
|
else
|
|
{
|
|
// we found the exact record or no other record to interpolate with
|
|
// just copy these values since they are the best we have
|
|
org = recordSim->m_vecOrigin;
|
|
ang = recordSim->m_vecAngles;
|
|
minsPreScaled = recordSim->m_vecMinsPreScaled;
|
|
maxsPreScaled = recordSim->m_vecMaxsPreScaled;
|
|
}
|
|
|
|
// See if this represents a change for the player
|
|
int flags = 0;
|
|
LagRecord* restore = &m_RestoreData[pl_index];
|
|
LagRecord* change = &m_ChangeData[pl_index];
|
|
|
|
QAngle angdiff = pPlayer->GetAbsAngles() - ang;
|
|
Vector orgdiff = pPlayer->GetAbsOrigin() - org;
|
|
|
|
// Always remember the pristine simulation time in case we need to restore it.
|
|
restore->m_flSimulationTime = pPlayer->GetSimulationTime();
|
|
restore->m_flAnimTime = pPlayer->GetAnimTime();
|
|
|
|
#ifdef CSTRIKE_DLL
|
|
if ( csPlayer )
|
|
{
|
|
renderAngles = recordAnim->m_angRenderAngles;
|
|
restore->m_angRenderAngles = csPlayer->m_angRenderAngles;
|
|
csPlayer->m_angRenderAngles = renderAngles;
|
|
}
|
|
#endif
|
|
|
|
if ( angdiff.LengthSqr() > 0.0f )
|
|
{
|
|
flags |= LC_ANGLES_CHANGED;
|
|
restore->m_vecAngles = pPlayer->GetAbsAngles();
|
|
pPlayer->SetAbsAngles( ang );
|
|
change->m_vecAngles = ang;
|
|
}
|
|
|
|
// Use absolute equality here
|
|
if ( minsPreScaled != pPlayer->CollisionProp()->OBBMinsPreScaled()
|
|
|| maxsPreScaled != pPlayer->CollisionProp()->OBBMaxsPreScaled() )
|
|
{
|
|
flags |= LC_SIZE_CHANGED;
|
|
|
|
restore->m_vecMinsPreScaled = pPlayer->CollisionProp()->OBBMinsPreScaled();
|
|
restore->m_vecMaxsPreScaled = pPlayer->CollisionProp()->OBBMaxsPreScaled();
|
|
|
|
pPlayer->SetSize( minsPreScaled, maxsPreScaled );
|
|
|
|
change->m_vecMinsPreScaled = minsPreScaled;
|
|
change->m_vecMaxsPreScaled = maxsPreScaled;
|
|
}
|
|
|
|
// Note, do origin at end since it causes a relink into the k/d tree
|
|
if ( orgdiff.LengthSqr() > 0.0f )
|
|
{
|
|
flags |= LC_ORIGIN_CHANGED;
|
|
restore->m_vecOrigin = pPlayer->GetAbsOrigin();
|
|
pPlayer->SetAbsOrigin( org );
|
|
change->m_vecOrigin = org;
|
|
}
|
|
|
|
// Sorry for the loss of the optimization for the case of people
|
|
// standing still, but you breathe even on the server.
|
|
// This is quicker than actually comparing all bazillion floats.
|
|
flags |= LC_ANIMATION_CHANGED;
|
|
restore->m_masterSequence = pPlayer->GetSequence();
|
|
restore->m_masterCycle = pPlayer->GetCycle();
|
|
|
|
pPlayer->SetSequence( recordAnim->m_masterSequence );
|
|
pPlayer->SetCycle( recordAnim->m_masterCycle );
|
|
|
|
////////////////////////
|
|
// Now do all the layers
|
|
int layerCount = pPlayer->GetNumAnimOverlays();
|
|
for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
|
|
{
|
|
CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex );
|
|
if ( currentLayer )
|
|
{
|
|
restore->m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle;
|
|
restore->m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder;
|
|
restore->m_layerRecords[layerIndex].m_sequence = currentLayer->m_nSequence;
|
|
restore->m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight;
|
|
restore->m_layerRecords[layerIndex].m_flags = currentLayer->m_fFlags;
|
|
|
|
currentLayer->m_flCycle = recordAnim->m_layerRecords[layerIndex].m_cycle;
|
|
currentLayer->m_nOrder = recordAnim->m_layerRecords[layerIndex].m_order;
|
|
currentLayer->m_nSequence = recordAnim->m_layerRecords[layerIndex].m_sequence;
|
|
currentLayer->m_flWeight = recordAnim->m_layerRecords[layerIndex].m_weight;
|
|
currentLayer->m_fFlags = recordAnim->m_layerRecords[layerIndex].m_flags;
|
|
}
|
|
}
|
|
|
|
flags |= LC_POSE_PARAMS_CHANGED;
|
|
|
|
// Now do pose parameters
|
|
CStudioHdr* hdr = pPlayer->GetModelPtr();
|
|
if ( hdr )
|
|
{
|
|
for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
|
|
{
|
|
restore->m_poseParameters[paramIndex] = pPlayer->GetPoseParameterArray()[paramIndex];
|
|
float poseParameter = recordAnim->m_poseParameters[paramIndex];
|
|
|
|
pPlayer->SetPoseParameterRaw( paramIndex, poseParameter );
|
|
}
|
|
}
|
|
|
|
flags |= LC_ENCD_CONS_CHANGED;
|
|
|
|
if ( hdr )
|
|
{
|
|
for ( int encIndex = 0; encIndex < hdr->GetNumBoneControllers(); encIndex++ )
|
|
{
|
|
restore->m_encodedControllers[encIndex] = pPlayer->GetBoneControllerArray()[encIndex];
|
|
float encodedController = recordAnim->m_encodedControllers[encIndex];
|
|
|
|
pPlayer->SetBoneControllerRaw( encIndex, encodedController );
|
|
}
|
|
}
|
|
|
|
if ( !flags )
|
|
{
|
|
return; // we didn't change anything
|
|
}
|
|
|
|
// Set lag compensated player's times
|
|
pPlayer->SetSimulationTime( flTargetSimTime );
|
|
pPlayer->SetAnimTime( flTargetAnimTime );
|
|
|
|
if ( sv_lagflushbonecache.GetBool() )
|
|
{
|
|
pPlayer->InvalidateBoneCache();
|
|
}
|
|
|
|
m_RestorePlayer.Set( pl_index ); // remember that we changed this player
|
|
m_bNeedToRestore = true; // we changed at least one player
|
|
restore->m_fFlags = flags; // we need to restore these flags
|
|
change->m_fFlags = flags; // we have changed these flags
|
|
}
|
|
|
|
void CLagCompensationManager::FinishLagCompensation( CBasePlayer* player )
|
|
{
|
|
VPROF_BUDGET_FLAGS( "FinishLagCompensation",
|
|
VPROF_BUDGETGROUP_OTHER_NETWORKING,
|
|
BUDGETFLAG_CLIENT | BUDGETFLAG_SERVER );
|
|
|
|
if ( !m_bNeedToRestore )
|
|
{
|
|
return; // no player was changed at all
|
|
}
|
|
|
|
// Iterate all active players
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
if ( !m_RestorePlayer.Get( i ) )
|
|
{
|
|
// player wasn't changed by lag compensation
|
|
continue;
|
|
}
|
|
|
|
CBasePlayer* pPlayer = UTIL_PlayerByIndex( i );
|
|
if ( !pPlayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LagRecord* restore = &m_RestoreData[i];
|
|
LagRecord* change = &m_ChangeData[i];
|
|
|
|
#ifdef CSTRIKE_DLL
|
|
auto csPlayer = ToCSPlayer( pPlayer );
|
|
|
|
if ( csPlayer )
|
|
{
|
|
csPlayer->m_angRenderAngles = restore->m_angRenderAngles;
|
|
}
|
|
#endif
|
|
|
|
if ( restore->m_fFlags & LC_SIZE_CHANGED )
|
|
{
|
|
// see if simulation made any changes, if no, then do the restore, otherwise,
|
|
// leave new values in
|
|
if ( pPlayer->CollisionProp()->OBBMinsPreScaled() == change->m_vecMinsPreScaled
|
|
&& pPlayer->CollisionProp()->OBBMaxsPreScaled() == change->m_vecMaxsPreScaled )
|
|
{
|
|
// Restore it
|
|
pPlayer->SetSize( restore->m_vecMinsPreScaled, restore->m_vecMaxsPreScaled );
|
|
}
|
|
#ifdef STAGING_ONLY
|
|
else
|
|
{
|
|
Warning( "Should we really not restore the size?\n" );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ( restore->m_fFlags & LC_ANGLES_CHANGED )
|
|
{
|
|
if ( pPlayer->GetAbsAngles() == change->m_vecAngles )
|
|
{
|
|
pPlayer->SetAbsAngles( restore->m_vecAngles );
|
|
}
|
|
}
|
|
|
|
if ( restore->m_fFlags & LC_ORIGIN_CHANGED )
|
|
{
|
|
// Okay, let's see if we can do something reasonable with the change
|
|
Vector delta = pPlayer->GetAbsOrigin() - change->m_vecOrigin;
|
|
|
|
RestorePlayerTo( pPlayer, restore->m_vecOrigin + delta );
|
|
}
|
|
|
|
if ( restore->m_fFlags & LC_ANIMATION_CHANGED )
|
|
{
|
|
pPlayer->SetSequence( restore->m_masterSequence );
|
|
pPlayer->SetCycle( restore->m_masterCycle );
|
|
|
|
int layerCount = pPlayer->GetNumAnimOverlays();
|
|
for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
|
|
{
|
|
CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex );
|
|
if ( currentLayer )
|
|
{
|
|
currentLayer->m_flCycle = restore->m_layerRecords[layerIndex].m_cycle;
|
|
currentLayer->m_nOrder = restore->m_layerRecords[layerIndex].m_order;
|
|
currentLayer->m_nSequence = restore->m_layerRecords[layerIndex].m_sequence;
|
|
currentLayer->m_flWeight = restore->m_layerRecords[layerIndex].m_weight;
|
|
currentLayer->m_fFlags = restore->m_layerRecords[layerIndex].m_flags;
|
|
}
|
|
}
|
|
}
|
|
|
|
CStudioHdr* hdr = pPlayer->GetModelPtr();
|
|
|
|
if ( restore->m_fFlags & LC_POSE_PARAMS_CHANGED )
|
|
{
|
|
if ( hdr )
|
|
{
|
|
for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
|
|
{
|
|
pPlayer->SetPoseParameterRaw( paramIndex, restore->m_poseParameters[paramIndex] );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( restore->m_fFlags & LC_ENCD_CONS_CHANGED )
|
|
{
|
|
if ( hdr )
|
|
{
|
|
for ( int encIndex = 0; encIndex < hdr->GetNumBoneControllers(); encIndex++ )
|
|
{
|
|
pPlayer->SetBoneControllerRaw( encIndex, restore->m_encodedControllers[encIndex] );
|
|
}
|
|
}
|
|
}
|
|
|
|
pPlayer->SetSimulationTime( restore->m_flSimulationTime );
|
|
pPlayer->SetAnimTime( restore->m_flAnimTime );
|
|
}
|
|
}
|