General fixes and improved lag comp performance

This commit is contained in:
Kamay Xutax 2024-08-31 06:18:28 +02:00
parent 380f352adf
commit 11d345802e
17 changed files with 499 additions and 527 deletions

View file

@ -1948,6 +1948,7 @@ void C_BaseAnimating::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quat
return; return;
} }
// CS weapons have this sometimes ...
if ( GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 ) if ( GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 )
{ {
SetSequence( 0 ); SetSequence( 0 );
@ -4928,7 +4929,8 @@ void C_BaseAnimating::Simulate()
DelayedInitModelEffects(); DelayedInitModelEffects();
} }
if ( gpGlobals->frametime != 0.0f ) // TODO_ENHANCED: check if there's other stuff like this! This can break lag compensation.
if ( gpGlobals->frametime != 0.0f && m_bClientSideAnimation )
{ {
DoAnimationEvents( GetModelPtr() ); DoAnimationEvents( GetModelPtr() );
} }

View file

@ -534,6 +534,7 @@ protected:
float m_fadeMinDist; float m_fadeMinDist;
float m_fadeMaxDist; float m_fadeMaxDist;
float m_flFadeScale; float m_flFadeScale;
bool m_bClientSideAnimation;
private: private:
@ -567,7 +568,6 @@ private:
float m_flOldEncodedController[MAXSTUDIOBONECTRLS]; float m_flOldEncodedController[MAXSTUDIOBONECTRLS];
// Clientside animation // Clientside animation
bool m_bClientSideAnimation;
bool m_bLastClientSideFrameReset; bool m_bLastClientSideFrameReset;
int m_nNewSequenceParity; int m_nNewSequenceParity;

View file

@ -124,40 +124,6 @@ CAddonInfo g_AddonInfo[] =
{ "eholster", 0, "models/weapons/w_eq_eholster_elite.mdl", "models/weapons/w_eq_eholster.mdl" }, { "eholster", 0, "models/weapons/w_eq_eholster_elite.mdl", "models/weapons/w_eq_eholster.mdl" },
}; };
// -------------------------------------------------------------------------------- //
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
// -------------------------------------------------------------------------------- //
class C_TEPlayerAnimEvent : public C_BaseTempEntity
{
public:
DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity );
DECLARE_CLIENTCLASS();
virtual void PostDataUpdate( DataUpdateType_t updateType )
{
// Create the effect.
C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() );
if ( pPlayer && !pPlayer->IsDormant() )
{
pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData );
}
}
public:
CNetworkHandle( CBasePlayer, m_hPlayer );
CNetworkVar( int, m_iEvent );
CNetworkVar( int, m_nData );
};
IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent );
BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent )
RecvPropEHandle( RECVINFO( m_hPlayer ) ),
RecvPropInt( RECVINFO( m_iEvent ) ),
RecvPropInt( RECVINFO( m_nData ) )
END_RECV_TABLE()
BEGIN_PREDICTION_DATA( C_CSPlayer ) BEGIN_PREDICTION_DATA( C_CSPlayer )
#ifdef CS_SHIELD_ENABLED #ifdef CS_SHIELD_ENABLED
DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
@ -767,7 +733,7 @@ C_CSPlayer::C_CSPlayer() :
{ {
m_angEyeAngles.Init(); m_angEyeAngles.Init();
AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR ); // AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
m_iLastAddonBits = m_iAddonBits = 0; m_iLastAddonBits = m_iAddonBits = 0;
m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE; m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE;
@ -1689,6 +1655,11 @@ bool C_CSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
void C_CSPlayer::UpdateClientSideAnimation() void C_CSPlayer::UpdateClientSideAnimation()
{ {
if ( !m_bClientSideAnimation )
{
return;
}
// We do this in a different order than the base class. // We do this in a different order than the base class.
// We need our cycle to be valid for when we call the playeranimstate update code, // We need our cycle to be valid for when we call the playeranimstate update code,
// or else it'll synchronize the upper body anims with the wrong cycle. // or else it'll synchronize the upper body anims with the wrong cycle.
@ -2074,6 +2045,8 @@ void C_CSPlayer::PlayReloadEffect()
void C_CSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) void C_CSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{ {
// do nothing ... let server doing its animations.
return;
} }
void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )

View file

@ -1306,7 +1306,7 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
for ( int i = 0; i < MAX_EDICTS; i++ ) for ( int i = 0; i < MAX_EDICTS; i++ )
{ {
cmd->simulationdata[i].m_bEntityExists = false; cmd->simulationdata[i].entityexists = false;
} }
// Send interpolated simulation time for lag compensation, let it also auto-vectorize this. // Send interpolated simulation time for lag compensation, let it also auto-vectorize this.
@ -1319,9 +1319,9 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
continue; continue;
} }
cmd->simulationdata[pEntity->index].m_flSimulationTime = pEntity->m_flInterpolatedSimulationTime; cmd->simulationdata[pEntity->index].lerp_time = pEntity->m_flInterpolatedSimulationTime;
cmd->simulationdata[pEntity->index].m_flAnimTime = pEntity->m_flSimulationTime; cmd->simulationdata[pEntity->index].animated_sim_time = pEntity->m_flSimulationTime;
cmd->simulationdata[pEntity->index].m_bEntityExists = true; cmd->simulationdata[pEntity->index].entityexists = true;
} }
#ifdef CSTRIKE_DLL #ifdef CSTRIKE_DLL

View file

@ -480,7 +480,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
if ( m_pIk ) if ( m_pIk )
{ {
CIKContext auto_ik; CIKContext auto_ik;
auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, boneMask ); auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, m_iIKCounter, boneMask );
boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik ); boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik );
} }
else else
@ -490,7 +490,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
if ( pStudioHdr->numbonecontrollers() ) if ( pStudioHdr->numbonecontrollers() )
{ {
boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() ); boneSetup.CalcBoneAdj( pos, q, GetBoneControllerArray() );
} }
} }

View file

@ -2805,8 +2805,12 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
CStudioHdr *pStudioHdr = GetModelPtr( ); CStudioHdr *pStudioHdr = GetModelPtr( );
Assert(pStudioHdr); Assert(pStudioHdr);
// Use this for lag compensation
float oldcurtime = gpGlobals->curtime;
gpGlobals->curtime = m_flSimulationTime;
CBoneCache *pcache = Studio_GetBoneCache( m_boneCacheHandle ); CBoneCache *pcache = Studio_GetBoneCache( m_boneCacheHandle );
int boneMask = BONE_USED_BY_HITBOX | BONE_USED_BY_ATTACHMENT; int boneMask = BONE_USED_BY_ANYTHING;
// TF queries these bones to position weapons when players are killed // TF queries these bones to position weapons when players are killed
#if defined( TF_DLL ) #if defined( TF_DLL )
@ -2814,8 +2818,10 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
#endif #endif
if ( pcache ) if ( pcache )
{ {
if ( pcache->IsValid( gpGlobals->curtime ) && (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid <= gpGlobals->curtime) // Only validate for current time.
if ( (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid == gpGlobals->curtime)
{ {
gpGlobals->curtime = oldcurtime;
// Msg("%s:%s:%s (%x:%x:%8.4f) cache\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask, pcache->m_boneMask, pcache->m_timeValid ); // Msg("%s:%s:%s (%x:%x:%8.4f) cache\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask, pcache->m_boneMask, pcache->m_timeValid );
// in memory and still valid, use it! // in memory and still valid, use it!
return pcache; return pcache;
@ -2849,6 +2855,8 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
pcache = Studio_GetBoneCache( m_boneCacheHandle ); pcache = Studio_GetBoneCache( m_boneCacheHandle );
} }
Assert(pcache); Assert(pcache);
gpGlobals->curtime = oldcurtime;
return pcache; return pcache;
} }
@ -3035,7 +3043,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
if ( m_pIk ) if ( m_pIk )
{ {
CIKContext auto_ik; CIKContext auto_ik;
auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, boneMask ); auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, m_iIKCounter, boneMask );
boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik ); boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik );
} }
else else
@ -3045,7 +3053,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
if ( pStudioHdr->numbonecontrollers() ) if ( pStudioHdr->numbonecontrollers() )
{ {
boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() ); boneSetup.CalcBoneAdj( pos, q, GetBoneControllerArray() );
} }
} }

View file

@ -325,7 +325,7 @@ public:
CBaseEntity *GetLightingOrigin(); CBaseEntity *GetLightingOrigin();
const float* GetPoseParameterArray() { return m_flPoseParameter.Base(); } const float* GetPoseParameterArray() { return m_flPoseParameter.Base(); }
const float *GetEncodedControllerArray() { return m_flEncodedController.Base(); } const float *GetBoneControllerArray() { return m_flEncodedController.Base(); }
void BuildMatricesWithBoneMerge( const CStudioHdr *pStudioHdr, const QAngle& angles, void BuildMatricesWithBoneMerge( const CStudioHdr *pStudioHdr, const QAngle& angles,
const Vector& origin, const Vector pos[MAXSTUDIOBONES], const Vector& origin, const Vector pos[MAXSTUDIOBONES],

View file

@ -240,45 +240,6 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CCSRagdoll, DT_CSRagdoll )
SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ),
END_SEND_TABLE() END_SEND_TABLE()
// -------------------------------------------------------------------------------- //
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
// -------------------------------------------------------------------------------- //
class CTEPlayerAnimEvent : public CBaseTempEntity
{
public:
DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
DECLARE_SERVERCLASS();
CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
{
}
CNetworkHandle( CBasePlayer, m_hPlayer );
CNetworkVar( int, m_iEvent );
CNetworkVar( int, m_nData );
};
IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
SendPropEHandle( SENDINFO( m_hPlayer ) ),
SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_nData ), 32 )
END_SEND_TABLE()
static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
{
CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );
g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
g_TEPlayerAnimEvent.m_iEvent = event;
g_TEPlayerAnimEvent.m_nData = nData;
g_TEPlayerAnimEvent.Create( filter, 0 );
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: Filters updates to a variable so that only non-local players see // Purpose: Filters updates to a variable so that only non-local players see
// the changes. This is so we can send a low-res origin to non-local players // the changes. This is so we can send a low-res origin to non-local players
@ -6664,7 +6625,6 @@ void CCSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
else else
{ {
m_PlayerAnimState->DoAnimationEvent( event, nData ); m_PlayerAnimState->DoAnimationEvent( event, nData );
TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
} }
} }

View file

@ -782,7 +782,7 @@ protected:
void PushawayThink(); void PushawayThink();
private: public:
ICSPlayerAnimState *m_PlayerAnimState; ICSPlayerAnimState *m_PlayerAnimState;

View file

@ -24,6 +24,7 @@ public:
virtual void StartLagCompensation( CBasePlayer *player, CUserCmd *cmd ) = 0; virtual void StartLagCompensation( CBasePlayer *player, CUserCmd *cmd ) = 0;
virtual void FinishLagCompensation(CBasePlayer *player) = 0; virtual void FinishLagCompensation(CBasePlayer *player) = 0;
virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ) = 0; virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ) = 0;
virtual void TrackPlayerData( CBasePlayer *pPlayer ) = 0;
}; };
extern ILagCompensationManager *lagcompensation; extern ILagCompensationManager *lagcompensation;

View file

@ -363,9 +363,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
{ {
// If no clipping and cheats enabled and noclipduring game enabled, then leave // If no clipping and cheats enabled and noclipduring game enabled, then leave
// forwardmove and angles stuff in usercmd // forwardmove and angles stuff in usercmd
if ( player->GetMoveType() == MOVETYPE_NOCLIP && if ( player->GetMoveType() == MOVETYPE_NOCLIP && sv_cheats->GetBool() && sv_noclipduringpause.GetBool() )
sv_cheats->GetBool() &&
sv_noclipduringpause.GetBool() )
{ {
gpGlobals->frametime = TICK_INTERVAL; gpGlobals->frametime = TICK_INTERVAL;
} }
@ -399,7 +397,8 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
if ( ucmd->impulse ) if ( ucmd->impulse )
{ {
// Discard impulse commands unless the vehicle allows them. // Discard impulse commands unless the vehicle allows them.
// FIXME: UsingStandardWeapons seems like a bad filter for this. The flashlight is an impulse command, for example. // FIXME: UsingStandardWeapons seems like a bad filter for this. The flashlight is an impulse command, for
// example.
if ( !pVehicle || player->UsingStandardWeaponsInVehicle() ) if ( !pVehicle || player->UsingStandardWeaponsInVehicle() )
{ {
player->m_nImpulse = ucmd->impulse; player->m_nImpulse = ucmd->impulse;
@ -468,4 +467,6 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
{ {
player->m_nTickBase++; player->m_nTickBase++;
} }
lagcompensation->TrackPlayerData( player );
} }

View file

@ -5,9 +5,11 @@
// $NoKeywords: $ // $NoKeywords: $
//=============================================================================// //=============================================================================//
#include "bone_setup.h"
#include "cbase.h" #include "cbase.h"
#include "icvar.h" #include "icvar.h"
#include "player.h"
#include "shareddefs.h"
#include "studio.h"
#include "usercmd.h" #include "usercmd.h"
#include "igamesystem.h" #include "igamesystem.h"
#include "ilagcompensationmanager.h" #include "ilagcompensationmanager.h"
@ -30,10 +32,10 @@
#define LC_POSE_PARAMS_CHANGED ( 1 << 12 ) #define LC_POSE_PARAMS_CHANGED ( 1 << 12 )
#define LC_ENCD_CONS_CHANGED ( 1 << 13 ) #define LC_ENCD_CONS_CHANGED ( 1 << 13 )
#define MAX_TICKS_SAVED 2048
ConVar sv_unlag( "sv_unlag", "1", FCVAR_DEVELOPMENTONLY, "Enables player lag compensation" ); ConVar sv_unlag( "sv_unlag", "1", FCVAR_DEVELOPMENTONLY, "Enables player lag compensation" );
ConVar sv_maxunlag( "sv_maxunlag", "1.0", FCVAR_DEVELOPMENTONLY, "Maximum lag compensation in seconds", true, 0.0f, true, 1.0f ); ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "0", 0, "Flushes entity bone cache on lag compensation" );
ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "0", FCVAR_DEVELOPMENTONLY, "Flushes entity bone cache on lag compensation" );
ConVar sv_unlag_fixstuck( "sv_unlag_fixstuck", "0", FCVAR_DEVELOPMENTONLY, "Disallow backtracking a player for lag compensation if it will cause them to become stuck" );
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: // Purpose:
@ -44,7 +46,7 @@ struct LagRecord
public: public:
LagRecord() LagRecord()
{ {
m_fFlags = 0; m_fFlags = LC_NONE;
m_vecOrigin.Init(); m_vecOrigin.Init();
m_vecAngles.Init(); m_vecAngles.Init();
m_vecMinsPreScaled.Init(); m_vecMinsPreScaled.Init();
@ -54,14 +56,19 @@ public:
m_masterSequence = 0; m_masterSequence = 0;
m_masterCycle = 0; m_masterCycle = 0;
for (int i = 0; i < MAX_LAYER_RECORDS; i++) for ( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex )
{ {
m_poseParameters[i] = -1; m_layerRecords[layerIndex] = {};
} }
for (int i = 0; i < MAX_ENCODED_CONTROLLERS; i++) for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ )
{ {
m_encodedControllers[i] = -1; m_poseParameters[i] = 0;
}
for ( int i = 0; i < MAXSTUDIOBONECTRLS; i++ )
{
m_encodedControllers[i] = 0;
} }
} }
@ -74,19 +81,21 @@ public:
m_vecMaxsPreScaled = src.m_vecMaxsPreScaled; m_vecMaxsPreScaled = src.m_vecMaxsPreScaled;
m_flSimulationTime = src.m_flSimulationTime; m_flSimulationTime = src.m_flSimulationTime;
m_flAnimTime = src.m_flAnimTime; m_flAnimTime = src.m_flAnimTime;
for ( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex ) for ( int layerIndex = 0; layerIndex < MAX_LAYER_RECORDS; ++layerIndex )
{ {
m_layerRecords[layerIndex] = src.m_layerRecords[layerIndex]; m_layerRecords[layerIndex] = src.m_layerRecords[layerIndex];
} }
m_masterSequence = src.m_masterSequence; m_masterSequence = src.m_masterSequence;
m_masterCycle = src.m_masterCycle; m_masterCycle = src.m_masterCycle;
for (int i = 0; i < MAX_LAYER_RECORDS; i++) for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ )
{ {
m_poseParameters[i] = src.m_poseParameters[i]; m_poseParameters[i] = src.m_poseParameters[i];
} }
for (int i = 0; i < MAX_LAYER_RECORDS; i++) for ( int i = 0; i < MAXSTUDIOBONECTRLS; i++ )
{ {
m_encodedControllers[i] = src.m_encodedControllers[i]; m_encodedControllers[i] = src.m_encodedControllers[i];
} }
@ -108,11 +117,10 @@ public:
LayerRecord m_layerRecords[MAX_LAYER_RECORDS]; LayerRecord m_layerRecords[MAX_LAYER_RECORDS];
int m_masterSequence; int m_masterSequence;
float m_masterCycle; float m_masterCycle;
float m_poseParameters[MAX_POSE_PARAMETERS]; float m_poseParameters[MAXSTUDIOPOSEPARAM];
float m_encodedControllers[MAX_ENCODED_CONTROLLERS]; float m_encodedControllers[MAXSTUDIOBONECTRLS];
}; };
// //
// Try to take the player from his current origin to vWantedPos. // Try to take the player from his current origin to vWantedPos.
// If it can't get there, leave the player where he is. // If it can't get there, leave the player where he is.
@ -121,6 +129,7 @@ public:
ConVar sv_unlag_debug( "sv_unlag_debug", "0", FCVAR_GAMEDLL | FCVAR_DEVELOPMENTONLY ); ConVar sv_unlag_debug( "sv_unlag_debug", "0", FCVAR_GAMEDLL | FCVAR_DEVELOPMENTONLY );
float g_flFractionScale = 0.95; float g_flFractionScale = 0.95;
static void RestorePlayerTo( CBasePlayer* pPlayer, const Vector& vWantedPos ) static void RestorePlayerTo( CBasePlayer* pPlayer, const Vector& vWantedPos )
{ {
// Try to move to the wanted position from our current position. // Try to move to the wanted position from our current position.
@ -132,28 +141,41 @@ static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos )
if ( sv_unlag_debug.GetBool() ) if ( sv_unlag_debug.GetBool() )
{ {
DevMsg( "RestorePlayerTo() could not restore player position for client \"%s\" ( %.1f %.1f %.1f )\n", DevMsg( "RestorePlayerTo() could not restore player position for client \"%s\" ( %.1f %.1f %.1f )\n",
pPlayer->GetPlayerName(), vWantedPos.x, vWantedPos.y, vWantedPos.z ); pPlayer->GetPlayerName(),
vWantedPos.x,
vWantedPos.y,
vWantedPos.z );
} }
UTIL_TraceEntity( pPlayer, pPlayer->GetLocalOrigin(), vWantedPos, MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); UTIL_TraceEntity( pPlayer,
pPlayer->GetAbsOrigin(),
vWantedPos,
MASK_PLAYERSOLID,
pPlayer,
COLLISION_GROUP_PLAYER_MOVEMENT,
&tr );
if ( tr.startsolid || tr.allsolid ) if ( tr.startsolid || tr.allsolid )
{ {
// In this case, the guy got stuck back wherever we lag compensated him to. Nasty. // In this case, the guy got stuck back wherever we lag compensated him to. Nasty.
if ( sv_unlag_debug.GetBool() ) if ( sv_unlag_debug.GetBool() )
{
DevMsg( " restore failed entirely\n" ); DevMsg( " restore failed entirely\n" );
} }
}
else else
{ {
// We can get to a valid place, but not all the way back to where we were. // We can get to a valid place, but not all the way back to where we were.
Vector vPos; Vector vPos;
VectorLerp( pPlayer->GetLocalOrigin(), vWantedPos, tr.fraction * g_flFractionScale, vPos ); VectorLerp( pPlayer->GetAbsOrigin(), vWantedPos, tr.fraction * g_flFractionScale, vPos );
UTIL_SetOrigin( pPlayer, vPos, true ); UTIL_SetOrigin( pPlayer, vPos, true );
if ( sv_unlag_debug.GetBool() ) if ( sv_unlag_debug.GetBool() )
{
DevMsg( " restore got most of the way\n" ); DevMsg( " restore got most of the way\n" );
} }
} }
}
else else
{ {
// Cool, the player can go back to whence he came. // Cool, the player can go back to whence he came.
@ -161,14 +183,14 @@ static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos )
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: // Purpose:
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
class CLagCompensationManager : public CAutoGameSystemPerFrame, public ILagCompensationManager class CLagCompensationManager : public CAutoGameSystemPerFrame,
public ILagCompensationManager
{ {
public: public:
CLagCompensationManager( char const *name ) : CAutoGameSystemPerFrame( name ) CLagCompensationManager( const char* name )
{ {
} }
@ -183,45 +205,42 @@ public:
ClearHistory(); ClearHistory();
} }
// called after entities think
virtual void FrameUpdatePostEntityThink();
// ILagCompensationManager stuff // ILagCompensationManager stuff
// Called during player movement to set up/restore after lag compensation // Called during player movement to set up/restore after lag compensation
void StartLagCompensation( CBasePlayer* player, CUserCmd* cmd ); void StartLagCompensation( CBasePlayer* player, CUserCmd* cmd );
void FinishLagCompensation( CBasePlayer* player ); void FinishLagCompensation( CBasePlayer* player );
virtual void TrackPlayerData( CBasePlayer* pPlayer );
private: private:
virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ); void BacktrackPlayer( CBasePlayer* player, CUserCmd* cmd );
void ClearHistory() void ClearHistory()
{ {
for ( int i=0; i<MAX_PLAYERS; i++ ) for ( int i = 0; i < MAX_EDICTS; i++ )
m_PlayerTrack[i].Purge(); {
m_EntityTrack[i].Clear();
}
} }
// keep a list of lag records for each player // keep a list of lag records for each entities
CUtlFixedLinkedList< LagRecord > m_PlayerTrack[ MAX_PLAYERS ]; CUtlCircularBuffer< LagRecord, MAX_TICKS_SAVED > m_EntityTrack[MAX_EDICTS];
// Scratchpad for determining what needs to be restored // Scratchpad for determining what needs to be restored
CBitVec<MAX_PLAYERS> m_RestorePlayer; CBitVec< MAX_EDICTS > m_RestorePlayer;
bool m_bNeedToRestore; bool m_bNeedToRestore;
LagRecord m_RestoreData[ MAX_PLAYERS ]; // player data before we moved him back LagRecord m_RestoreData[MAX_EDICTS]; // entities data before we moved him back
LagRecord m_ChangeData[ MAX_PLAYERS ]; // player data where we moved him back LagRecord m_ChangeData[MAX_EDICTS]; // entities data where we moved him back
CBasePlayer *m_pCurrentPlayer; // The player we are doing lag compensation for
}; };
static CLagCompensationManager g_LagCompensationManager( "CLagCompensationManager" ); static CLagCompensationManager g_LagCompensationManager( "CLagCompensationManager" );
ILagCompensationManager* lagcompensation = &g_LagCompensationManager; ILagCompensationManager* lagcompensation = &g_LagCompensationManager;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: Called once per frame after all entities have had a chance to think // Purpose: Called once per frame after all entities have had a chance to think
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void CLagCompensationManager::FrameUpdatePostEntityThink() void CLagCompensationManager::TrackPlayerData( CBasePlayer* pPlayer )
{ {
if ( ( gpGlobals->maxClients <= 1 ) || !sv_unlag.GetBool() ) if ( ( gpGlobals->maxClients <= 1 ) || !sv_unlag.GetBool() )
{ {
@ -229,59 +248,15 @@ void CLagCompensationManager::FrameUpdatePostEntityThink()
return; return;
} }
VPROF_BUDGET( "FrameUpdatePostEntityThink", "CLagCompensationManager" ); VPROF_BUDGET( "TrackPlayerData", "CLagCompensationManager" );
// remove all records before that time: // remove all records before that time:
int flDeadtime = gpGlobals->curtime - sv_maxunlag.GetFloat(); auto track = &m_EntityTrack[pPlayer->entindex()];
// Iterate all active players
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
CUtlFixedLinkedList< LagRecord > *track = &m_PlayerTrack[i-1];
if ( !pPlayer )
{
if ( track->Count() > 0 )
{
track->RemoveAll();
}
continue;
}
Assert( track->Count() < 1000 ); // insanity check
// remove tail records that are too old
intp tailIndex = track->Tail();
while ( track->IsValidIndex( tailIndex ) )
{
LagRecord &tail = track->Element( tailIndex );
// if tail is within limits, stop
if ( tail.m_flSimulationTime >= flDeadtime )
break;
// remove tail, get new tail
track->Remove( tailIndex );
tailIndex = track->Tail();
}
// check if head has same simulation time
if ( track->Count() > 0 )
{
LagRecord &head = track->Element( track->Head() );
// check if player changed simulation time since last time updated
if ( head.m_flSimulationTime >= pPlayer->GetSimulationTime() )
continue; // don't add new entry for same or older time
}
// add new record to player track // add new record to player track
LagRecord &record = track->Element( track->AddToHead() ); LagRecord record;
record.m_fFlags = 0; record.m_fFlags = LC_NONE;
if ( pPlayer->IsAlive() ) if ( pPlayer->IsAlive() )
{ {
record.m_fFlags |= LC_ALIVE; record.m_fFlags |= LC_ALIVE;
@ -289,12 +264,13 @@ void CLagCompensationManager::FrameUpdatePostEntityThink()
record.m_flSimulationTime = pPlayer->GetSimulationTime(); record.m_flSimulationTime = pPlayer->GetSimulationTime();
record.m_flAnimTime = pPlayer->GetAnimTime(); record.m_flAnimTime = pPlayer->GetAnimTime();
record.m_vecAngles = pPlayer->GetLocalAngles(); record.m_vecAngles = pPlayer->GetAbsAngles();
record.m_vecOrigin = pPlayer->GetLocalOrigin(); record.m_vecOrigin = pPlayer->GetAbsOrigin();
record.m_vecMinsPreScaled = pPlayer->CollisionProp()->OBBMinsPreScaled(); record.m_vecMinsPreScaled = pPlayer->CollisionProp()->OBBMinsPreScaled();
record.m_vecMaxsPreScaled = pPlayer->CollisionProp()->OBBMaxsPreScaled(); record.m_vecMaxsPreScaled = pPlayer->CollisionProp()->OBBMaxsPreScaled();
int layerCount = pPlayer->GetNumAnimOverlays(); int layerCount = pPlayer->GetNumAnimOverlays();
for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
{ {
CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex ); CAnimationLayer* currentLayer = pPlayer->GetAnimOverlay( layerIndex );
@ -306,10 +282,12 @@ void CLagCompensationManager::FrameUpdatePostEntityThink()
record.m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight; record.m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight;
} }
} }
record.m_masterSequence = pPlayer->GetSequence(); record.m_masterSequence = pPlayer->GetSequence();
record.m_masterCycle = pPlayer->GetCycle(); record.m_masterCycle = pPlayer->GetCycle();
CStudioHdr* hdr = pPlayer->GetModelPtr(); CStudioHdr* hdr = pPlayer->GetModelPtr();
if ( hdr ) if ( hdr )
{ {
for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
@ -322,40 +300,29 @@ void CLagCompensationManager::FrameUpdatePostEntityThink()
{ {
for ( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ ) for ( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ )
{ {
record.m_encodedControllers[paramIndex] = pPlayer->GetEncodedControllerArray()[ paramIndex ]; record.m_encodedControllers[paramIndex] = pPlayer->GetBoneControllerArray()[paramIndex];
}
} }
} }
//Clear the current player. track->Push( record );
m_pCurrentPlayer = NULL;
} }
// Called during player movement to set up/restore after lag compensation // Called during player movement to set up/restore after lag compensation
void CLagCompensationManager::StartLagCompensation( CBasePlayer* player, CUserCmd* cmd ) void CLagCompensationManager::StartLagCompensation( CBasePlayer* player, CUserCmd* cmd )
{ {
//DONT LAG COMP AGAIN THIS FRAME IF THERES ALREADY ONE IN PROGRESS
//IF YOU'RE HITTING THIS THEN IT MEANS THERES A CODE BUG
if ( m_pCurrentPlayer )
{
Assert( m_pCurrentPlayer == NULL );
Warning( "Trying to start a new lag compensation session while one is already active!\n" );
return;
}
// Assume no players need to be restored // Assume no players need to be restored
m_RestorePlayer.ClearAll(); m_RestorePlayer.ClearAll();
m_bNeedToRestore = false; m_bNeedToRestore = false;
m_pCurrentPlayer = player;
if ( !player->m_bLagCompensation // Player not wanting lag compensation if ( !player->m_bLagCompensation // Player not wanting lag compensation
|| ( gpGlobals->maxClients <= 1 ) // no lag compensation in single player || ( gpGlobals->maxClients <= 1 ) // no lag compensation in single player
|| !sv_unlag.GetBool() // disabled by server admin || !sv_unlag.GetBool() // disabled by server admin
|| player->IsBot() // not for bots || player->IsBot() // not for bots
|| player->IsObserver() // not for spectators || player->IsObserver() // not for spectators
) )
{
return; return;
}
// NOTE: Put this here so that it won't show up in single player mode. // NOTE: Put this here so that it won't show up in single player mode.
VPROF_BUDGET( "StartLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING ); VPROF_BUDGET( "StartLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING );
@ -381,7 +348,9 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
// Custom checks for if things should lag compensate (based on things like what team the player is on). // Custom checks for if things should lag compensate (based on things like what team the player is on).
if ( !player->WantsLagCompensationOnEntity( pPlayer, cmd, pEntityTransmitBits ) ) if ( !player->WantsLagCompensationOnEntity( pPlayer, cmd, pEntityTransmitBits ) )
{
continue; continue;
}
// Move other player back in time // Move other player back in time
BacktrackPlayer( pPlayer, cmd ); BacktrackPlayer( pPlayer, cmd );
@ -394,62 +363,58 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
Vector minsPreScaled; Vector minsPreScaled;
Vector maxsPreScaled; Vector maxsPreScaled;
QAngle ang; QAngle ang;
LagRecord* nextRecordSim;
LagRecord* recordSim;
LagRecord* recordAnim;
VPROF_BUDGET( "BacktrackPlayer", "CLagCompensationManager" ); VPROF_BUDGET( "BacktrackPlayer", "CLagCompensationManager" );
int pl_index = pPlayer->entindex() - 1; int pl_index = pPlayer->entindex();
float flTargetSimulationTime = cmd->simulationdata[pl_index + 1].m_flSimulationTime; float flTargetLerpSimTime = cmd->simulationdata[pl_index].lerp_time;
float flTargetSimulatedAnimationTime = cmd->simulationdata[pl_index + 1].m_flAnimTime; float flTargetAnimatedSimulationTime = cmd->simulationdata[pl_index].animated_sim_time;
// get track history of this player // get track history of this player
CUtlFixedLinkedList< LagRecord > *trackSim = &m_PlayerTrack[ pl_index ]; auto track = &m_EntityTrack[pl_index];
CUtlFixedLinkedList< LagRecord > *trackAnim = &m_PlayerTrack[ pl_index ];
// check if we have at leat one entry for ( int i = 0; i < MAX_TICKS_SAVED; i++ )
if ( trackSim->Count() <= 0 || trackAnim->Count() <= 0 )
return;
intp currSim = trackSim->Head();
LagRecord *prevRecordSim = NULL;
LagRecord *recordSim = NULL;
LagRecord *recordAnim = NULL;
Vector prevOrg = pPlayer->GetLocalOrigin();
bool foundAnimationData = false;
// Walk context looking for any invalidating event
while( trackSim->IsValidIndex(currSim) )
{ {
// remember last record recordSim = track->Get( i );
prevRecordSim = recordSim;
// get next record if ( !recordSim )
recordSim = &trackSim->Element(currSim);
if (recordSim->m_flSimulationTime
<= flTargetSimulatedAnimationTime && !foundAnimationData)
{ {
recordAnim = recordSim; break;
foundAnimationData = true;
} }
if ( !( recordSim->m_fFlags & LC_ALIVE ) ) if ( !( recordSim->m_fFlags & LC_ALIVE ) )
{ {
// player most be alive, lost track break;
return;
} }
// TODO_ENHANCED: do proper teleportation checks. if ( flTargetLerpSimTime == recordSim->m_flSimulationTime )
{
break;
}
// did we find a context smaller than target time ? if ( recordSim->m_flSimulationTime < flTargetLerpSimTime )
if ( recordSim->m_flSimulationTime <= flTargetSimulationTime ) {
break; // hurra, stop nextRecordSim = track->Get( i - 1 );
break;
}
}
prevOrg = recordSim->m_vecOrigin; for ( int i = 0; i < MAX_TICKS_SAVED; i++ )
{
recordAnim = track->Get( i );
// go one step back if ( !recordAnim )
currSim = trackSim->Next( currSim ); {
break;
}
if ( recordAnim->m_flSimulationTime == flTargetAnimatedSimulationTime )
{
break;
}
} }
Assert( recordAnim ); Assert( recordAnim );
@ -466,26 +431,25 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
} }
float fracSim = 0.0f; float fracSim = 0.0f;
if ( prevRecordSim && if ( nextRecordSim && ( recordSim->m_flSimulationTime < flTargetLerpSimTime )
(recordSim->m_flSimulationTime < flTargetSimulationTime) && && ( recordSim->m_flSimulationTime < nextRecordSim->m_flSimulationTime ) )
(recordSim->m_flSimulationTime < prevRecordSim->m_flSimulationTime) )
{ {
// we didn't find the exact time but have a valid previous record // we didn't find the exact time but have a valid previous record
// so interpolate between these two records; // so interpolate between these two records;
Assert( prevRecordSim->m_flSimulationTime > recordSim->m_flSimulationTime ); Assert( prevRecordSim->m_flSimulationTime > recordSim->m_flSimulationTime );
Assert( flTargetSimulationTime < prevRecordSim->m_flSimulationTime ); Assert( flTargetLerpSimTime < prevRecordSim->m_flSimulationTime );
// calc fraction between both records // calc fraction between both records
fracSim = float(( double(flTargetSimulationTime) - double(recordSim->m_flSimulationTime) ) / fracSim = float( ( double( flTargetLerpSimTime ) - double( recordSim->m_flSimulationTime ) )
( double(prevRecordSim->m_flSimulationTime) - double(recordSim->m_flSimulationTime) )); / ( double( nextRecordSim->m_flSimulationTime ) - double( recordSim->m_flSimulationTime ) ) );
Assert( fracSim > 0 && fracSim < 1 ); // should never extrapolate Assert( fracSim > 0 && fracSim < 1 ); // should never extrapolate
ang = Lerp( fracSim, recordSim->m_vecAngles, prevRecordSim->m_vecAngles ); ang = Lerp( fracSim, recordSim->m_vecAngles, nextRecordSim->m_vecAngles );
org = Lerp( fracSim, recordSim->m_vecOrigin, prevRecordSim->m_vecOrigin ); org = Lerp( fracSim, recordSim->m_vecOrigin, nextRecordSim->m_vecOrigin );
minsPreScaled = Lerp( fracSim, recordSim->m_vecMinsPreScaled, prevRecordSim->m_vecMinsPreScaled ); minsPreScaled = Lerp( fracSim, recordSim->m_vecMinsPreScaled, nextRecordSim->m_vecMinsPreScaled );
maxsPreScaled = Lerp( fracSim, recordSim->m_vecMaxsPreScaled, prevRecordSim->m_vecMaxsPreScaled ); maxsPreScaled = Lerp( fracSim, recordSim->m_vecMaxsPreScaled, nextRecordSim->m_vecMaxsPreScaled );
} }
else else
{ {
@ -497,72 +461,13 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
maxsPreScaled = recordSim->m_vecMaxsPreScaled; maxsPreScaled = recordSim->m_vecMaxsPreScaled;
} }
// See if this is still a valid position for us to teleport to
if ( sv_unlag_fixstuck.GetBool() )
{
// Try to move to the wanted position from our current position.
trace_t tr;
UTIL_TraceEntity( pPlayer, org, org, MASK_PLAYERSOLID, &tr );
if ( tr.startsolid || tr.allsolid )
{
if ( sv_unlag_debug.GetBool() )
DevMsg( "WARNING: BackupPlayer trying to back player into a bad position - client %s\n", pPlayer->GetPlayerName() );
CBasePlayer *pHitPlayer = dynamic_cast<CBasePlayer *>( tr.m_pEnt );
// don't lag compensate the current player
if ( pHitPlayer && ( pHitPlayer != m_pCurrentPlayer ) )
{
// If we haven't backtracked this player, do it now
// this deliberately ignores WantsLagCompensationOnEntity.
if ( !m_RestorePlayer.Get( pHitPlayer->entindex() - 1 ) )
{
// prevent recursion - save a copy of m_RestorePlayer,
// pretend that this player is off-limits
int pl_index = pPlayer->entindex() - 1;
// Temp turn this flag on
m_RestorePlayer.Set( pl_index );
BacktrackPlayer( pHitPlayer, cmd );
// Remove the temp flag
m_RestorePlayer.Clear( pl_index );
}
}
// now trace us back as far as we can go
UTIL_TraceEntity( pPlayer, pPlayer->GetLocalOrigin(), org, MASK_PLAYERSOLID, &tr );
if ( tr.startsolid || tr.allsolid )
{
// Our starting position is bogus
if ( sv_unlag_debug.GetBool() )
DevMsg( "Backtrack failed completely, bad starting position\n" );
}
else
{
// We can get to a valid place, but not all the way to the target
Vector vPos;
VectorLerp( pPlayer->GetLocalOrigin(), org, tr.fraction * g_flFractionScale, vPos );
// This is as close as we're going to get
org = vPos;
if ( sv_unlag_debug.GetBool() )
DevMsg( "Backtrack got most of the way\n" );
}
}
}
// See if this represents a change for the player // See if this represents a change for the player
int flags = 0; int flags = 0;
LagRecord* restore = &m_RestoreData[pl_index]; LagRecord* restore = &m_RestoreData[pl_index];
LagRecord* change = &m_ChangeData[pl_index]; LagRecord* change = &m_ChangeData[pl_index];
QAngle angdiff = pPlayer->GetLocalAngles() - ang; QAngle angdiff = pPlayer->GetAbsAngles() - ang;
Vector orgdiff = pPlayer->GetLocalOrigin() - org; Vector orgdiff = pPlayer->GetAbsOrigin() - org;
// Always remember the pristine simulation time in case we need to restore it. // Always remember the pristine simulation time in case we need to restore it.
restore->m_flSimulationTime = pPlayer->GetSimulationTime(); restore->m_flSimulationTime = pPlayer->GetSimulationTime();
@ -571,13 +476,14 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
if ( angdiff.LengthSqr() > 0.0f ) if ( angdiff.LengthSqr() > 0.0f )
{ {
flags |= LC_ANGLES_CHANGED; flags |= LC_ANGLES_CHANGED;
restore->m_vecAngles = pPlayer->GetLocalAngles(); restore->m_vecAngles = pPlayer->GetAbsAngles();
pPlayer->SetLocalAngles( ang ); pPlayer->SetAbsAngles( ang );
change->m_vecAngles = ang; change->m_vecAngles = ang;
} }
// Use absolute equality here // Use absolute equality here
if ( minsPreScaled != pPlayer->CollisionProp()->OBBMinsPreScaled() || maxsPreScaled != pPlayer->CollisionProp()->OBBMaxsPreScaled() ) if ( minsPreScaled != pPlayer->CollisionProp()->OBBMinsPreScaled()
|| maxsPreScaled != pPlayer->CollisionProp()->OBBMaxsPreScaled() )
{ {
flags |= LC_SIZE_CHANGED; flags |= LC_SIZE_CHANGED;
@ -594,8 +500,8 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
if ( orgdiff.LengthSqr() > 0.0f ) if ( orgdiff.LengthSqr() > 0.0f )
{ {
flags |= LC_ORIGIN_CHANGED; flags |= LC_ORIGIN_CHANGED;
restore->m_vecOrigin = pPlayer->GetLocalOrigin(); restore->m_vecOrigin = pPlayer->GetAbsOrigin();
pPlayer->SetLocalOrigin( org ); pPlayer->SetAbsOrigin( org );
change->m_vecOrigin = org; change->m_vecOrigin = org;
} }
@ -620,8 +526,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
restore->m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle; restore->m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle;
restore->m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder; restore->m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder;
restore->m_layerRecords[layerIndex].m_sequence = currentLayer->m_nSequence; restore->m_layerRecords[layerIndex].m_sequence = currentLayer->m_nSequence;
restore->m_layerRecords[layerIndex].m_weight = currentLayer restore->m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight;
->m_flWeight;
currentLayer->m_flCycle = recordAnim->m_layerRecords[layerIndex].m_cycle; currentLayer->m_flCycle = recordAnim->m_layerRecords[layerIndex].m_cycle;
currentLayer->m_nOrder = recordAnim->m_layerRecords[layerIndex].m_order; currentLayer->m_nOrder = recordAnim->m_layerRecords[layerIndex].m_order;
@ -649,29 +554,28 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
if ( hdr ) if ( hdr )
{ {
for( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ ) for ( int encIndex = 0; encIndex < hdr->GetNumBoneControllers(); encIndex++ )
{ {
restore->m_encodedControllers[paramIndex] = pPlayer->GetEncodedControllerArray()[paramIndex]; restore->m_encodedControllers[encIndex] = pPlayer->GetBoneControllerArray()[encIndex];
float encodedController = recordAnim->m_encodedControllers[paramIndex]; float encodedController = recordAnim->m_encodedControllers[encIndex];
pPlayer->SetBoneControllerRaw( paramIndex, encodedController ); pPlayer->SetBoneControllerRaw( encIndex, encodedController );
} }
} }
if ( !flags ) if ( !flags )
{
return; // we didn't change anything return; // we didn't change anything
}
// Set lag compensated player's times // Set lag compensated player's times
pPlayer->SetSimulationTime(flTargetSimulationTime); pPlayer->SetSimulationTime( flTargetLerpSimTime );
// pPlayer->SetAnimTime(animationData->m_flAnimTime); // pPlayer->SetAnimTime(animationData->m_flAnimTime);
if ( sv_lagflushbonecache.GetBool() ) if ( sv_lagflushbonecache.GetBool() )
{
pPlayer->InvalidateBoneCache(); pPlayer->InvalidateBoneCache();
}
/*char text[256]; Q_snprintf( text, sizeof(text), "time %.2f", flTargetTime );
pPlayer->DrawServerHitboxes( 10 );
NDebugOverlay::Text( org, text, false, 10 );
NDebugOverlay::EntityBounds( pPlayer, 255, 0, 0, 32, 10 ); */
m_RestorePlayer.Set( pl_index ); // remember that we changed this player m_RestorePlayer.Set( pl_index ); // remember that we changed this player
m_bNeedToRestore = true; // we changed at least one player m_bNeedToRestore = true; // we changed at least one player
@ -679,22 +583,21 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
change->m_fFlags = flags; // we have changed these flags change->m_fFlags = flags; // we have changed these flags
} }
void CLagCompensationManager::FinishLagCompensation( CBasePlayer* player ) void CLagCompensationManager::FinishLagCompensation( CBasePlayer* player )
{ {
VPROF_BUDGET_FLAGS( "FinishLagCompensation", VPROF_BUDGETGROUP_OTHER_NETWORKING, BUDGETFLAG_CLIENT|BUDGETFLAG_SERVER ); VPROF_BUDGET_FLAGS( "FinishLagCompensation",
VPROF_BUDGETGROUP_OTHER_NETWORKING,
m_pCurrentPlayer = NULL; BUDGETFLAG_CLIENT | BUDGETFLAG_SERVER );
if ( !m_bNeedToRestore ) if ( !m_bNeedToRestore )
{
return; // no player was changed at all return; // no player was changed at all
}
// Iterate all active players // Iterate all active players
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{ {
int pl_index = i - 1; if ( !m_RestorePlayer.Get( i ) )
if ( !m_RestorePlayer.Get( pl_index ) )
{ {
// player wasn't changed by lag compensation // player wasn't changed by lag compensation
continue; continue;
@ -706,15 +609,15 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
continue; continue;
} }
LagRecord *restore = &m_RestoreData[ pl_index ]; LagRecord* restore = &m_RestoreData[i];
LagRecord *change = &m_ChangeData[ pl_index ]; LagRecord* change = &m_ChangeData[i];
if ( restore->m_fFlags & LC_SIZE_CHANGED ) if ( restore->m_fFlags & LC_SIZE_CHANGED )
{ {
// see if simulation made any changes, if no, then do the restore, otherwise, // see if simulation made any changes, if no, then do the restore, otherwise,
// leave new values in // leave new values in
if ( pPlayer->CollisionProp()->OBBMinsPreScaled() == change->m_vecMinsPreScaled && if ( pPlayer->CollisionProp()->OBBMinsPreScaled() == change->m_vecMinsPreScaled
pPlayer->CollisionProp()->OBBMaxsPreScaled() == change->m_vecMaxsPreScaled ) && pPlayer->CollisionProp()->OBBMaxsPreScaled() == change->m_vecMaxsPreScaled )
{ {
// Restore it // Restore it
pPlayer->SetSize( restore->m_vecMinsPreScaled, restore->m_vecMaxsPreScaled ); pPlayer->SetSize( restore->m_vecMinsPreScaled, restore->m_vecMaxsPreScaled );
@ -729,16 +632,16 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
if ( restore->m_fFlags & LC_ANGLES_CHANGED ) if ( restore->m_fFlags & LC_ANGLES_CHANGED )
{ {
if ( pPlayer->GetLocalAngles() == change->m_vecAngles ) if ( pPlayer->GetAbsAngles() == change->m_vecAngles )
{ {
pPlayer->SetLocalAngles( restore->m_vecAngles ); pPlayer->SetAbsAngles( restore->m_vecAngles );
} }
} }
if ( restore->m_fFlags & LC_ORIGIN_CHANGED ) if ( restore->m_fFlags & LC_ORIGIN_CHANGED )
{ {
// Okay, let's see if we can do something reasonable with the change // Okay, let's see if we can do something reasonable with the change
Vector delta = pPlayer->GetLocalOrigin() - change->m_vecOrigin; Vector delta = pPlayer->GetAbsOrigin() - change->m_vecOrigin;
RestorePlayerTo( pPlayer, restore->m_vecOrigin + delta ); RestorePlayerTo( pPlayer, restore->m_vecOrigin + delta );
} }
@ -762,9 +665,10 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
} }
} }
CStudioHdr* hdr = pPlayer->GetModelPtr();
if ( restore->m_fFlags & LC_POSE_PARAMS_CHANGED ) if ( restore->m_fFlags & LC_POSE_PARAMS_CHANGED )
{ {
CStudioHdr *hdr = pPlayer->GetModelPtr();
if ( hdr ) if ( hdr )
{ {
for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ ) for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
@ -776,12 +680,11 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
if ( restore->m_fFlags & LC_ENCD_CONS_CHANGED ) if ( restore->m_fFlags & LC_ENCD_CONS_CHANGED )
{ {
CStudioHdr *hdr = pPlayer->GetModelPtr();
if ( hdr ) if ( hdr )
{ {
for( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ ) for ( int encIndex = 0; encIndex < hdr->GetNumBoneControllers(); encIndex++ )
{ {
pPlayer->SetBoneControllerRaw( paramIndex, restore->m_encodedControllers[paramIndex] ); pPlayer->SetBoneControllerRaw( encIndex, restore->m_encodedControllers[encIndex] );
} }
} }
} }
@ -790,5 +693,3 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
pPlayer->SetAnimTime( restore->m_flAnimTime ); pPlayer->SetAnimTime( restore->m_flAnimTime );
} }
} }

View file

@ -5,6 +5,7 @@
//=============================================================================// //=============================================================================//
#include "cbase.h" #include "cbase.h"
#include "const.h"
#include "debugoverlay_shared.h" #include "debugoverlay_shared.h"
#include "strtools.h" #include "strtools.h"
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
@ -38,7 +39,7 @@
#include "engine/ivdebugoverlay.h" #include "engine/ivdebugoverlay.h"
#include "obstacle_pushaway.h" #include "obstacle_pushaway.h"
#include "props_shared.h" #include "props_shared.h"
#include <iostream>
ConVar weapon_accuracy_nospread( "weapon_accuracy_nospread", "0", FCVAR_REPLICATED ); ConVar weapon_accuracy_nospread( "weapon_accuracy_nospread", "0", FCVAR_REPLICATED );
#define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS) #define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS)
@ -508,7 +509,8 @@ void CCSPlayer::FireBullet(
CBasePlayer *lastPlayerHit = NULL; CBasePlayer *lastPlayerHit = NULL;
MDLCACHE_CRITICAL_SECTION(); MDLCACHE_CRITICAL_SECTION();
bool shouldDebugHitboxesOnFire = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_FIRE; // Only show for one bullet.
bool shouldDebugHitboxesOnFire = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_FIRE && iBullet == 0;
bool shouldDebugHitboxesOnHit = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_HIT; bool shouldDebugHitboxesOnHit = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_HIT;
// TODO_ENHANCED: // TODO_ENHANCED:
@ -527,6 +529,28 @@ void CCSPlayer::FireBullet(
CBasePlayer* lagPlayer = UTIL_PlayerByIndex(i); CBasePlayer* lagPlayer = UTIL_PlayerByIndex(i);
if (lagPlayer && lagPlayer != this) if (lagPlayer && lagPlayer != this)
{ {
#ifdef CLIENT_DLL
if ( !m_pCurrentCommand->hasbeenpredicted )
{
std::string text = "client:\n";
auto angles = lagPlayer->GetRenderAngles();
text += "x: " + std::to_string(angles.x) + ", y: " + std::to_string(angles.y) + ", z: " + std::to_string(angles.z) + '\n';
NDebugOverlay::EntityBounds( lagPlayer, 255, 0, 0, 32, 60 );
std::cout << text;
}
#else
std::string text = "server:\n";
auto angles = lagPlayer->GetAbsAngles();
text += "x: " + std::to_string(angles.x) + ", y: " + std::to_string(angles.y) + ", z: " + std::to_string(angles.z) + '\n';
NDebugOverlay::EntityBounds( lagPlayer, 0, 255, 0, 32, 60 );
std::cout << text;
#endif
#ifdef CLIENT_DLL #ifdef CLIENT_DLL
if (!m_pCurrentCommand->hasbeenpredicted) if (!m_pCurrentCommand->hasbeenpredicted)
{ {

View file

@ -108,7 +108,7 @@ bool CBaseCSGrenade::Deploy()
bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo ) bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
{ {
m_bRedraw = false; m_bRedraw = false;
m_bPinPulled = false; // when this is holstered make sure the pin isnt pulled. m_bPinPulled = false; // when this is holstered make sure the pin isn<EFBFBD>t pulled.
m_fThrowTime = 0; m_fThrowTime = 0;
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
@ -224,8 +224,9 @@ void CBaseCSGrenade::ItemPostFrame()
// If they let go of the fire button, they want to throw the grenade. // If they let go of the fire button, they want to throw the grenade.
if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) ) if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) )
{ {
#ifndef CLIENT_DLL
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE ); pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );
#endif
StartGrenadeThrow(); StartGrenadeThrow();
MDLCACHE_CRITICAL_SECTION(); MDLCACHE_CRITICAL_SECTION();

View file

@ -162,20 +162,20 @@ void WriteUsercmd( bf_write *buf, const CUserCmd *to, const CUserCmd *from )
// Write finally simulation data with entity index // Write finally simulation data with entity index
for ( unsigned int i = 0; i <= highestEntityIndex; i++ ) for ( unsigned int i = 0; i <= highestEntityIndex; i++ )
{ {
if ( from->simulationdata[i].m_flSimulationTime != to->simulationdata[i].m_flSimulationTime ) if ( from->simulationdata[i].lerp_time != to->simulationdata[i].lerp_time )
{ {
buf->WriteOneBit( 1 ); buf->WriteOneBit( 1 );
buf->WriteBitFloat( to->simulationdata[i].m_flSimulationTime ); buf->WriteBitFloat( to->simulationdata[i].lerp_time );
} }
else else
{ {
buf->WriteOneBit( 0 ); buf->WriteOneBit( 0 );
} }
if ( from->simulationdata[i].m_flAnimTime != to->simulationdata[i].m_flAnimTime ) if ( from->simulationdata[i].animated_sim_time != to->simulationdata[i].animated_sim_time )
{ {
buf->WriteOneBit( 1 ); buf->WriteOneBit( 1 );
buf->WriteBitFloat( to->simulationdata[i].m_flAnimTime ); buf->WriteBitFloat( to->simulationdata[i].animated_sim_time );
} }
else else
{ {
@ -309,12 +309,12 @@ void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from )
{ {
if (buf->ReadOneBit()) if (buf->ReadOneBit())
{ {
move->simulationdata[i].m_flSimulationTime = buf->ReadBitFloat(); move->simulationdata[i].lerp_time = buf->ReadBitFloat();
} }
if (buf->ReadOneBit()) if (buf->ReadOneBit())
{ {
move->simulationdata[i].m_flAnimTime = buf->ReadBitFloat(); move->simulationdata[i].animated_sim_time = buf->ReadBitFloat();
} }
} }

View file

@ -30,7 +30,7 @@
#endif #endif
#define MAX_LAYER_RECORDS (CBaseAnimatingOverlay::MAX_OVERLAYS) #define MAX_LAYER_RECORDS (CBaseAnimatingOverlay::MAX_OVERLAYS)
#define MAX_POSE_PARAMETERS (CBaseAnimating::NUM_POSEPAREMETERS) #define MAX_POSE_PARAMETERS (MAXSTUDIOPOSEPARAM)
#define MAX_ENCODED_CONTROLLERS (MAXSTUDIOBONECTRLS) #define MAX_ENCODED_CONTROLLERS (MAXSTUDIOBONECTRLS)
class bf_read; class bf_read;
@ -57,9 +57,9 @@ struct SimulationData
// TODO_ENHANCED: // TODO_ENHANCED:
// For now we send the last received update for animations. // For now we send the last received update for animations.
// anim time is unreliable on low fps. // anim time is unreliable on low fps.
float m_flSimulationTime; float lerp_time;
float m_flAnimTime; float animated_sim_time;
bool m_bEntityExists; bool entityexists;
}; };
class CEntityGroundContact class CEntityGroundContact

View file

@ -1531,5 +1531,106 @@ private:
char *m_szBuffer; // a copy of original string, with '\0' instead of separators char *m_szBuffer; // a copy of original string, with '\0' instead of separators
}; };
template < typename T, size_t N >
class CUtlCircularBuffer
{
public:
using pT = T*;
enum PushType
{
Filling,
Updating
};
public:
inline pT Get( const int wantedSlot = 0 )
{
const auto real_slot = _Slot( wantedSlot );
return ( real_slot == -1 ) ? NULL : &_buffer[real_slot];
}
public:
inline PushType Push( T&& element )
{
if ( _filled_history >= N )
{
if ( _index >= ( N - 1 ) )
{
_index = 0;
}
else
{
_index++;
}
_buffer[_index] = element;
return Updating;
}
_buffer[_filled_history] = element;
_index = _filled_history;
_filled_history++;
return Filling;
}
inline PushType Push( const T& element )
{
if ( _filled_history >= N )
{
if ( _index >= ( N - 1 ) )
{
_index = 0;
}
else
{
_index++;
}
_buffer[_index] = element;
return Updating;
}
_buffer[_filled_history] = element;
_index = _filled_history;
_filled_history++;
return Filling;
}
inline void Clear()
{
_index = 0;
_filled_history = 0;
}
private:
inline int _Slot( const int wantedSlot = 0 ) const
{
if ( wantedSlot >= _filled_history || wantedSlot < 0 || _filled_history <= 0 )
{
return -1;
}
if ( _filled_history >= N )
{
const int calc_slot = ( _index - wantedSlot );
return ( calc_slot < 0 ) ? calc_slot + N : calc_slot;
}
else
{
return ( _filled_history - 1 ) - wantedSlot;
}
}
private:
T _buffer[N];
int _filled_history {};
int _index {};
};
#endif // CCVECTOR_H #endif // CCVECTOR_H