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,7 +1948,8 @@ void C_BaseAnimating::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quat
return;
}
if (GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 )
// CS weapons have this sometimes ...
if ( GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 )
{
SetSequence( 0 );
}
@ -4928,7 +4929,8 @@ void C_BaseAnimating::Simulate()
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() );
}

View file

@ -534,6 +534,7 @@ protected:
float m_fadeMinDist;
float m_fadeMaxDist;
float m_flFadeScale;
bool m_bClientSideAnimation;
private:
@ -567,7 +568,6 @@ private:
float m_flOldEncodedController[MAXSTUDIOBONECTRLS];
// Clientside animation
bool m_bClientSideAnimation;
bool m_bLastClientSideFrameReset;
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" },
};
// -------------------------------------------------------------------------------- //
// 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 )
#ifdef CS_SHIELD_ENABLED
DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
@ -767,7 +733,7 @@ C_CSPlayer::C_CSPlayer() :
{
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_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE;
@ -1689,6 +1655,11 @@ bool C_CSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
void C_CSPlayer::UpdateClientSideAnimation()
{
if ( !m_bClientSideAnimation )
{
return;
}
// 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,
// 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 )
{
// do nothing ... let server doing its animations.
return;
}
void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
@ -2332,9 +2305,9 @@ void C_CSPlayer::Simulate( void )
BaseClass::Simulate();
if ((cl_showhitboxes.GetInt() == 1 || cl_showhitboxes.GetInt() == 2) && !IsLocalPlayer())
if ( ( cl_showhitboxes.GetInt() == 1 || cl_showhitboxes.GetInt() == 2 ) && !IsLocalPlayer() )
{
DrawClientHitboxes(gpGlobals->frametime, true);
DrawClientHitboxes( gpGlobals->frametime, true );
}
}

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++ )
{
cmd->simulationdata[i].m_bEntityExists = false;
cmd->simulationdata[i].entityexists = false;
}
// 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;
}
cmd->simulationdata[pEntity->index].m_flSimulationTime = pEntity->m_flInterpolatedSimulationTime;
cmd->simulationdata[pEntity->index].m_flAnimTime = pEntity->m_flSimulationTime;
cmd->simulationdata[pEntity->index].m_bEntityExists = true;
cmd->simulationdata[pEntity->index].lerp_time = pEntity->m_flInterpolatedSimulationTime;
cmd->simulationdata[pEntity->index].animated_sim_time = pEntity->m_flSimulationTime;
cmd->simulationdata[pEntity->index].entityexists = true;
}
#ifdef CSTRIKE_DLL

View file

@ -480,7 +480,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
if ( m_pIk )
{
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 );
}
else
@ -490,7 +490,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
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( );
Assert(pStudioHdr);
// Use this for lag compensation
float oldcurtime = gpGlobals->curtime;
gpGlobals->curtime = m_flSimulationTime;
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
#if defined( TF_DLL )
@ -2814,8 +2818,10 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
#endif
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 );
// in memory and still valid, use it!
return pcache;
@ -2849,6 +2855,8 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
pcache = Studio_GetBoneCache( m_boneCacheHandle );
}
Assert(pcache);
gpGlobals->curtime = oldcurtime;
return pcache;
}
@ -3035,7 +3043,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
if ( m_pIk )
{
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 );
}
else
@ -3045,7 +3053,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
if ( pStudioHdr->numbonecontrollers() )
{
boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() );
boneSetup.CalcBoneAdj( pos, q, GetBoneControllerArray() );
}
}

View file

@ -325,7 +325,7 @@ public:
CBaseEntity *GetLightingOrigin();
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,
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 ),
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
// 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
{
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();
private:
public:
ICSPlayerAnimState *m_PlayerAnimState;

View file

@ -22,8 +22,9 @@ abstract_class ILagCompensationManager
public:
// Called during player movement to set up/restore after lag compensation
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 TrackPlayerData( CBasePlayer *pPlayer ) = 0;
};
extern ILagCompensationManager *lagcompensation;

View file

@ -350,7 +350,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
g_pGameMovement->StartTrackPredictionErrors( player );
// Prevent hacked clients from sending us invalid view angles to try to get leaf server code to crash
if ( !ucmd->viewangles.IsValid() || !IsEntityQAngleReasonable(ucmd->viewangles) )
if ( !ucmd->viewangles.IsValid() || !IsEntityQAngleReasonable( ucmd->viewangles ) )
{
ucmd->viewangles = vec3_angle;
}
@ -363,9 +363,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
{
// If no clipping and cheats enabled and noclipduring game enabled, then leave
// forwardmove and angles stuff in usercmd
if ( player->GetMoveType() == MOVETYPE_NOCLIP &&
sv_cheats->GetBool() &&
sv_noclipduringpause.GetBool() )
if ( player->GetMoveType() == MOVETYPE_NOCLIP && sv_cheats->GetBool() && sv_noclipduringpause.GetBool() )
{
gpGlobals->frametime = TICK_INTERVAL;
}
@ -385,7 +383,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
// Do weapon selection
if ( ucmd->weaponselect != 0 )
{
CBaseCombatWeapon *weapon = dynamic_cast< CBaseCombatWeapon * >( CBaseEntity::Instance( ucmd->weaponselect ) );
CBaseCombatWeapon* weapon = dynamic_cast< CBaseCombatWeapon* >( CBaseEntity::Instance( ucmd->weaponselect ) );
if ( weapon )
{
VPROF( "player->SelectItem()" );
@ -393,13 +391,14 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
}
}
IServerVehicle *pVehicle = player->GetVehicle();
IServerVehicle* pVehicle = player->GetVehicle();
// Latch in impulse.
if ( ucmd->impulse )
{
// 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() )
{
player->m_nImpulse = ucmd->impulse;
@ -420,7 +419,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
{
player->pl.v_angle = ucmd->viewangles;
}
else if( player->pl.fixangle == FIXANGLE_RELATIVE )
else if ( player->pl.fixangle == FIXANGLE_RELATIVE )
{
player->pl.v_angle = ucmd->viewangles + player->pl.anglechange;
}
@ -468,4 +467,6 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
{
player->m_nTickBase++;
}
lagcompensation->TrackPlayerData( player );
}

View file

@ -5,9 +5,11 @@
// $NoKeywords: $
//=============================================================================//
#include "bone_setup.h"
#include "cbase.h"
#include "icvar.h"
#include "player.h"
#include "shareddefs.h"
#include "studio.h"
#include "usercmd.h"
#include "igamesystem.h"
#include "ilagcompensationmanager.h"
@ -21,19 +23,19 @@
#include "tier0/memdbgon.h"
#define LC_NONE 0
#define LC_ALIVE (1<<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)
#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 )
#define MAX_TICKS_SAVED 2048
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", 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" );
ConVar sv_lagflushbonecache( "sv_lagflushbonecache", "0", 0, "Flushes entity bone cache on lag compensation" );
//-----------------------------------------------------------------------------
// Purpose:
@ -41,10 +43,10 @@ ConVar sv_unlag_fixstuck( "sv_unlag_fixstuck", "0", FCVAR_DEVELOPMENTONLY, "Disa
struct LagRecord
{
public:
public:
LagRecord()
{
m_fFlags = 0;
m_fFlags = LC_NONE;
m_vecOrigin.Init();
m_vecAngles.Init();
m_vecMinsPreScaled.Init();
@ -54,14 +56,19 @@ public:
m_masterSequence = 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_flSimulationTime = src.m_flSimulationTime;
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_masterSequence = src.m_masterSequence;
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];
}
for (int i = 0; i < MAX_LAYER_RECORDS; i++)
for ( int i = 0; i < MAXSTUDIOBONECTRLS; i++ )
{
m_encodedControllers[i] = src.m_encodedControllers[i];
}
@ -108,11 +117,10 @@ public:
LayerRecord m_layerRecords[MAX_LAYER_RECORDS];
int m_masterSequence;
float m_masterCycle;
float m_poseParameters[MAX_POSE_PARAMETERS];
float m_encodedControllers[MAX_ENCODED_CONTROLLERS];
float m_poseParameters[MAXSTUDIOPOSEPARAM];
float m_encodedControllers[MAXSTUDIOBONECTRLS];
};
//
// Try to take the player from his current origin to vWantedPos.
// If it can't get there, leave the player where he is.
@ -121,7 +129,8 @@ public:
ConVar sv_unlag_debug( "sv_unlag_debug", "0", FCVAR_GAMEDLL | FCVAR_DEVELOPMENTONLY );
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.
trace_t tr;
@ -132,28 +141,41 @@ static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos )
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 );
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 )
{
// 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->GetLocalOrigin(), vWantedPos, tr.fraction * g_flFractionScale, 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.
@ -161,14 +183,14 @@ static void RestorePlayerTo( CBasePlayer *pPlayer, const Vector &vWantedPos )
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CLagCompensationManager : public CAutoGameSystemPerFrame, public ILagCompensationManager
class CLagCompensationManager : public CAutoGameSystemPerFrame,
public ILagCompensationManager
{
public:
CLagCompensationManager( char const *name ) : CAutoGameSystemPerFrame( name )
public:
CLagCompensationManager( const char* name )
{
}
@ -183,105 +205,58 @@ public:
ClearHistory();
}
// called after entities think
virtual void FrameUpdatePostEntityThink();
// ILagCompensationManager stuff
// Called during player movement to set up/restore after lag compensation
void StartLagCompensation( CBasePlayer *player, CUserCmd *cmd );
void FinishLagCompensation( CBasePlayer *player );
void StartLagCompensation( CBasePlayer* player, CUserCmd* cmd );
void FinishLagCompensation( CBasePlayer* player );
virtual void TrackPlayerData( CBasePlayer* pPlayer );
private:
virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd );
private:
void BacktrackPlayer( CBasePlayer* player, CUserCmd* cmd );
void ClearHistory()
{
for ( int i=0; i<MAX_PLAYERS; i++ )
m_PlayerTrack[i].Purge();
for ( int i = 0; i < MAX_EDICTS; i++ )
{
m_EntityTrack[i].Clear();
}
}
// keep a list of lag records for each player
CUtlFixedLinkedList< LagRecord > m_PlayerTrack[ MAX_PLAYERS ];
// 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_PLAYERS> m_RestorePlayer;
CBitVec< MAX_EDICTS > m_RestorePlayer;
bool m_bNeedToRestore;
LagRecord m_RestoreData[ MAX_PLAYERS ]; // player data before we moved him back
LagRecord m_ChangeData[ MAX_PLAYERS ]; // player data where we moved him back
CBasePlayer *m_pCurrentPlayer; // The player we are doing lag compensation for
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;
ILagCompensationManager* lagcompensation = &g_LagCompensationManager;
//-----------------------------------------------------------------------------
// 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() )
{
ClearHistory();
return;
}
VPROF_BUDGET( "FrameUpdatePostEntityThink", "CLagCompensationManager" );
VPROF_BUDGET( "TrackPlayerData", "CLagCompensationManager" );
// remove all records before that time:
int flDeadtime = gpGlobals->curtime - sv_maxunlag.GetFloat();
// 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
}
auto track = &m_EntityTrack[pPlayer->entindex()];
// 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() )
{
record.m_fFlags |= LC_ALIVE;
@ -289,16 +264,17 @@ void CLagCompensationManager::FrameUpdatePostEntityThink()
record.m_flSimulationTime = pPlayer->GetSimulationTime();
record.m_flAnimTime = pPlayer->GetAnimTime();
record.m_vecAngles = pPlayer->GetLocalAngles();
record.m_vecOrigin = pPlayer->GetLocalOrigin();
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 )
for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
{
CAnimationLayer *currentLayer = pPlayer->GetAnimOverlay(layerIndex);
if( currentLayer )
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;
@ -306,56 +282,47 @@ void CLagCompensationManager::FrameUpdatePostEntityThink()
record.m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight;
}
}
record.m_masterSequence = pPlayer->GetSequence();
record.m_masterCycle = pPlayer->GetCycle();
CStudioHdr *hdr = pPlayer->GetModelPtr();
if( hdr )
CStudioHdr* hdr = pPlayer->GetModelPtr();
if ( hdr )
{
for( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
for ( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
{
record.m_poseParameters[paramIndex] = pPlayer->GetPoseParameterArray()[ paramIndex ];
record.m_poseParameters[paramIndex] = pPlayer->GetPoseParameterArray()[paramIndex];
}
}
if( hdr )
if ( hdr )
{
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.
m_pCurrentPlayer = NULL;
track->Push( record );
}
// 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
m_RestorePlayer.ClearAll();
m_bNeedToRestore = false;
m_pCurrentPlayer = player;
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
|| 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 );
@ -363,10 +330,10 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
Q_memset( m_ChangeData, 0, sizeof( m_ChangeData ) );
// Iterate all active players
const CBitVec<MAX_EDICTS> *pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 );
const CBitVec< MAX_EDICTS >* pEntityTransmitBits = engine->GetEntityTransmitBitsForClient( player->entindex() - 1 );
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
CBasePlayer* pPlayer = UTIL_PlayerByIndex( i );
if ( !pPlayer )
{
@ -381,75 +348,73 @@ void CLagCompensationManager::StartLagCompensation( CBasePlayer *player, CUserCm
// 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 )
void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* cmd )
{
Vector org;
Vector minsPreScaled;
Vector maxsPreScaled;
QAngle ang;
LagRecord* nextRecordSim;
LagRecord* recordSim;
LagRecord* recordAnim;
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 flTargetSimulatedAnimationTime = cmd->simulationdata[pl_index + 1].m_flAnimTime;
float flTargetLerpSimTime = cmd->simulationdata[pl_index].lerp_time;
float flTargetAnimatedSimulationTime = cmd->simulationdata[pl_index].animated_sim_time;
// get track history of this player
CUtlFixedLinkedList< LagRecord > *trackSim = &m_PlayerTrack[ pl_index ];
CUtlFixedLinkedList< LagRecord > *trackAnim = &m_PlayerTrack[ pl_index ];
auto track = &m_EntityTrack[pl_index];
// check if we have at leat one entry
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) )
for ( int i = 0; i < MAX_TICKS_SAVED; i++ )
{
// remember last record
prevRecordSim = recordSim;
recordSim = track->Get( i );
// get next record
recordSim = &trackSim->Element(currSim);
if (recordSim->m_flSimulationTime
<= flTargetSimulatedAnimationTime && !foundAnimationData)
if ( !recordSim )
{
recordAnim = recordSim;
foundAnimationData = true;
break;
}
if ( !(recordSim->m_fFlags & LC_ALIVE) )
if ( !( recordSim->m_fFlags & LC_ALIVE ) )
{
// player most be alive, lost track
return;
break;
}
// 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 <= flTargetSimulationTime )
break; // hurra, stop
if ( recordSim->m_flSimulationTime < flTargetLerpSimTime )
{
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
currSim = trackSim->Next( currSim );
if ( !recordAnim )
{
break;
}
if ( recordAnim->m_flSimulationTime == flTargetAnimatedSimulationTime )
{
break;
}
}
Assert( recordAnim );
@ -466,26 +431,25 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
}
float fracSim = 0.0f;
if ( prevRecordSim &&
(recordSim->m_flSimulationTime < flTargetSimulationTime) &&
(recordSim->m_flSimulationTime < prevRecordSim->m_flSimulationTime) )
if ( nextRecordSim && ( recordSim->m_flSimulationTime < flTargetLerpSimTime )
&& ( recordSim->m_flSimulationTime < nextRecordSim->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( flTargetSimulationTime < prevRecordSim->m_flSimulationTime );
Assert( flTargetLerpSimTime < prevRecordSim->m_flSimulationTime );
// calc fraction between both records
fracSim = float(( double(flTargetSimulationTime) - double(recordSim->m_flSimulationTime) ) /
( double(prevRecordSim->m_flSimulationTime) - double(recordSim->m_flSimulationTime) ));
fracSim = float( ( double( flTargetLerpSimTime ) - double( recordSim->m_flSimulationTime ) )
/ ( double( nextRecordSim->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 );
ang = Lerp( fracSim, recordSim->m_vecAngles, nextRecordSim->m_vecAngles );
org = Lerp( fracSim, recordSim->m_vecOrigin, nextRecordSim->m_vecOrigin );
minsPreScaled = Lerp( fracSim, recordSim->m_vecMinsPreScaled, nextRecordSim->m_vecMinsPreScaled );
maxsPreScaled = Lerp( fracSim, recordSim->m_vecMaxsPreScaled, nextRecordSim->m_vecMaxsPreScaled );
}
else
{
@ -497,72 +461,13 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
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
int flags = 0;
LagRecord *restore = &m_RestoreData[ pl_index ];
LagRecord *change = &m_ChangeData[ pl_index ];
LagRecord* restore = &m_RestoreData[pl_index];
LagRecord* change = &m_ChangeData[pl_index];
QAngle angdiff = pPlayer->GetLocalAngles() - ang;
Vector orgdiff = pPlayer->GetLocalOrigin() - org;
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();
@ -571,13 +476,14 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
if ( angdiff.LengthSqr() > 0.0f )
{
flags |= LC_ANGLES_CHANGED;
restore->m_vecAngles = pPlayer->GetLocalAngles();
pPlayer->SetLocalAngles( ang );
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() )
if ( minsPreScaled != pPlayer->CollisionProp()->OBBMinsPreScaled()
|| maxsPreScaled != pPlayer->CollisionProp()->OBBMaxsPreScaled() )
{
flags |= LC_SIZE_CHANGED;
@ -594,8 +500,8 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
if ( orgdiff.LengthSqr() > 0.0f )
{
flags |= LC_ORIGIN_CHANGED;
restore->m_vecOrigin = pPlayer->GetLocalOrigin();
pPlayer->SetLocalOrigin( org );
restore->m_vecOrigin = pPlayer->GetAbsOrigin();
pPlayer->SetAbsOrigin( org );
change->m_vecOrigin = org;
}
@ -606,22 +512,21 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
restore->m_masterSequence = pPlayer->GetSequence();
restore->m_masterCycle = pPlayer->GetCycle();
pPlayer->SetSequence(recordAnim->m_masterSequence);
pPlayer->SetCycle(recordAnim->m_masterCycle);
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 )
for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
{
CAnimationLayer *currentLayer = pPlayer->GetAnimOverlay(layerIndex);
if( currentLayer )
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_weight = currentLayer->m_flWeight;
currentLayer->m_flCycle = recordAnim->m_layerRecords[layerIndex].m_cycle;
currentLayer->m_nOrder = recordAnim->m_layerRecords[layerIndex].m_order;
@ -633,88 +538,86 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
flags |= LC_POSE_PARAMS_CHANGED;
// Now do pose parameters
CStudioHdr *hdr = pPlayer->GetModelPtr();
if( hdr )
CStudioHdr* hdr = pPlayer->GetModelPtr();
if ( hdr )
{
for( int paramIndex = 0; paramIndex < hdr->GetNumPoseParameters(); paramIndex++ )
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);
pPlayer->SetPoseParameterRaw( paramIndex, poseParameter );
}
}
flags |= LC_ENCD_CONS_CHANGED;
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];
float encodedController = recordAnim->m_encodedControllers[paramIndex];
restore->m_encodedControllers[encIndex] = pPlayer->GetBoneControllerArray()[encIndex];
float encodedController = recordAnim->m_encodedControllers[encIndex];
pPlayer->SetBoneControllerRaw( paramIndex, encodedController );
pPlayer->SetBoneControllerRaw( encIndex, encodedController );
}
}
if ( !flags )
{
return; // we didn't change anything
}
// Set lag compensated player's times
pPlayer->SetSimulationTime(flTargetSimulationTime);
pPlayer->SetSimulationTime( flTargetLerpSimTime );
// pPlayer->SetAnimTime(animationData->m_flAnimTime);
if ( sv_lagflushbonecache.GetBool() )
{
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
restore->m_fFlags = flags; // we need to restore 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 );
m_pCurrentPlayer = NULL;
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++ )
{
int pl_index = i - 1;
if ( !m_RestorePlayer.Get( pl_index ) )
if ( !m_RestorePlayer.Get( i ) )
{
// player wasn't changed by lag compensation
continue;
}
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
CBasePlayer* pPlayer = UTIL_PlayerByIndex( i );
if ( !pPlayer )
{
continue;
}
LagRecord *restore = &m_RestoreData[ pl_index ];
LagRecord *change = &m_ChangeData[ pl_index ];
LagRecord* restore = &m_RestoreData[i];
LagRecord* change = &m_ChangeData[i];
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 )
if ( pPlayer->CollisionProp()->OBBMinsPreScaled() == change->m_vecMinsPreScaled
&& pPlayer->CollisionProp()->OBBMaxsPreScaled() == change->m_vecMaxsPreScaled )
{
// Restore it
pPlayer->SetSize( restore->m_vecMinsPreScaled, restore->m_vecMaxsPreScaled );
@ -729,30 +632,30 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
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 )
{
// 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 );
}
if( restore->m_fFlags & LC_ANIMATION_CHANGED )
if ( restore->m_fFlags & LC_ANIMATION_CHANGED )
{
pPlayer->SetSequence(restore->m_masterSequence);
pPlayer->SetCycle(restore->m_masterCycle);
pPlayer->SetSequence( restore->m_masterSequence );
pPlayer->SetCycle( restore->m_masterCycle );
int layerCount = pPlayer->GetNumAnimOverlays();
for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
for ( int layerIndex = 0; layerIndex < layerCount; ++layerIndex )
{
CAnimationLayer *currentLayer = pPlayer->GetAnimOverlay(layerIndex);
if( currentLayer )
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;
@ -762,33 +665,31 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
}
}
if( restore->m_fFlags & LC_POSE_PARAMS_CHANGED )
CStudioHdr* hdr = pPlayer->GetModelPtr();
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++ )
{
pPlayer->SetPoseParameterRaw( paramIndex, restore->m_poseParameters[paramIndex] );
}
}
}
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] );
}
}
}
pPlayer->SetSimulationTime( restore->m_flSimulationTime );
pPlayer->SetAnimTime(restore->m_flAnimTime);
pPlayer->SetAnimTime( restore->m_flAnimTime );
}
}

View file

@ -5,6 +5,7 @@
//=============================================================================//
#include "cbase.h"
#include "const.h"
#include "debugoverlay_shared.h"
#include "strtools.h"
#ifndef CLIENT_DLL
@ -38,7 +39,7 @@
#include "engine/ivdebugoverlay.h"
#include "obstacle_pushaway.h"
#include "props_shared.h"
#include <iostream>
ConVar weapon_accuracy_nospread( "weapon_accuracy_nospread", "0", FCVAR_REPLICATED );
#define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS)
@ -193,7 +194,7 @@ void UTIL_ClipTraceToPlayersHull(const Vector& vecAbsStart, const Vector& vecAbs
float smallestFraction = tr->fraction;
const float maxRange = 60.0f;
ray.Init( vecAbsStart, vecAbsEnd , mins, maxs );
ray.Init( vecAbsStart, vecAbsEnd, mins, maxs );
for ( int k = 1; k <= gpGlobals->maxClients; ++k )
{
@ -508,7 +509,8 @@ void CCSPlayer::FireBullet(
CBasePlayer *lastPlayerHit = NULL;
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;
// TODO_ENHANCED:
@ -527,6 +529,28 @@ void CCSPlayer::FireBullet(
CBasePlayer* lagPlayer = UTIL_PlayerByIndex(i);
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
if (!m_pCurrentCommand->hasbeenpredicted)
{

View file

@ -108,7 +108,7 @@ bool CBaseCSGrenade::Deploy()
bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
{
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;
#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 ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) )
{
#ifndef CLIENT_DLL
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );
#endif
StartGrenadeThrow();
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
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->WriteBitFloat( to->simulationdata[i].m_flSimulationTime );
buf->WriteBitFloat( to->simulationdata[i].lerp_time );
}
else
{
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->WriteBitFloat( to->simulationdata[i].m_flAnimTime );
buf->WriteBitFloat( to->simulationdata[i].animated_sim_time );
}
else
{
@ -309,12 +309,12 @@ void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from )
{
if (buf->ReadOneBit())
{
move->simulationdata[i].m_flSimulationTime = buf->ReadBitFloat();
move->simulationdata[i].lerp_time = buf->ReadBitFloat();
}
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
#define MAX_LAYER_RECORDS (CBaseAnimatingOverlay::MAX_OVERLAYS)
#define MAX_POSE_PARAMETERS (CBaseAnimating::NUM_POSEPAREMETERS)
#define MAX_POSE_PARAMETERS (MAXSTUDIOPOSEPARAM)
#define MAX_ENCODED_CONTROLLERS (MAXSTUDIOBONECTRLS)
class bf_read;
@ -57,9 +57,9 @@ struct SimulationData
// TODO_ENHANCED:
// For now we send the last received update for animations.
// anim time is unreliable on low fps.
float m_flSimulationTime;
float m_flAnimTime;
bool m_bEntityExists;
float lerp_time;
float animated_sim_time;
bool entityexists;
};
class CEntityGroundContact

View file

@ -1531,5 +1531,106 @@ private:
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