Fixed lag compensation for animations

The problem is that we still trust the client, although now we have a
good base to start with; The key difference here is that we don't need
to use anymore the cs player animestate client side anymore
because server side values are used

There are minor bugs like fire effect but they can be fixed
This commit is contained in:
Kamay Xutax 2024-07-11 01:47:26 +02:00
parent 9d17d2252c
commit 854991ab8c
14 changed files with 52 additions and 105 deletions

View file

@ -868,9 +868,9 @@ void C_BaseAnimating::UpdateRelevantInterpolatedVars()
void C_BaseAnimating::AddBaseAnimatingInterpolatedVars()
{
AddVar( m_flEncodedController, &m_iv_flEncodedController, LATCH_SIMULATION_VAR, true );
AddVar( m_flPoseParameter, &m_iv_flPoseParameter, LATCH_SIMULATION_VAR, true );
AddVar( &m_flCycle, &m_iv_flCycle, LATCH_SIMULATION_VAR, true );
AddVar( m_flEncodedController, &m_iv_flEncodedController, LATCH_ANIMATION_VAR, true );
AddVar( m_flPoseParameter, &m_iv_flPoseParameter, LATCH_ANIMATION_VAR, true );
AddVar( &m_flCycle, &m_iv_flCycle, LATCH_ANIMATION_VAR, true );
}
void C_BaseAnimating::RemoveBaseAnimatingInterpolatedVars()

View file

@ -106,7 +106,7 @@ void ResizeAnimationLayerCallback( void *pStruct, int offsetToUtlVector, int len
{
IInterpolatedVar *pWatcher = &pVecIV->Element( i );
pWatcher->SetDebugName( s_m_iv_AnimOverlayNames[i] );
pEnt->AddVar( &pVec->Element( i ), pWatcher, LATCH_SIMULATION_VAR, true );
pEnt->AddVar( &pVec->Element( i ), pWatcher, LATCH_ANIMATION_VAR, true );
}
// FIXME: need to set historical values of nOrder in pVecIV to MAX_OVERLAY

View file

@ -842,7 +842,7 @@ C_BaseEntity::C_BaseEntity() :
// but that re-introduces a third-person hitching bug. One possible cause is the abrupt change
// in player size/position that occurs when ducking, and how prediction tries to work through that.
//
// AddVar( &m_vecVelocity, &m_iv_vecVelocity, LATCH_SIMULATION_VAR );
AddVar( &m_vecVelocity, &m_iv_vecVelocity, LATCH_SIMULATION_VAR );
m_DataChangeEventRef = -1;
m_EntClientFlags = 0;

View file

@ -786,8 +786,6 @@ END_RECV_TABLE()
C_CSPlayer::C_CSPlayer() :
m_iv_angEyeAngles( "C_CSPlayer::m_iv_angEyeAngles" )
{
m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true );
m_angEyeAngles.Init();
AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
@ -822,8 +820,6 @@ C_CSPlayer::~C_CSPlayer()
RemoveAddonModels();
ReleaseFlashlight();
m_PlayerAnimState->Release();
}
@ -937,10 +933,10 @@ const QAngle& C_CSPlayer::GetRenderAngles()
if ( IsRagdoll() )
{
return vec3_angle;
}
}
else
{
return m_PlayerAnimState->GetRenderAngles();
return GetAbsAngles();
}
}
@ -2092,15 +2088,6 @@ void C_CSPlayer::PlayReloadEffect()
void C_CSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{
if ( event == PLAYERANIMEVENT_THROW_GRENADE )
{
// Let the server handle this event. It will update m_iThrowGrenadeCounter and the client will
// pick up the event in CCSPlayerAnimState.
}
else
{
m_PlayerAnimState->DoAnimationEvent( event, nData );
}
}
void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
@ -2271,12 +2258,9 @@ void C_CSPlayer::Simulate( void )
BaseClass::Simulate();
// Update the animation data. It does the local check here so this works when using
// a third-person camera (and we don't have valid player angles).
if ( this == C_CSPlayer::GetLocalCSPlayer() )
m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
else
m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
static ConVar cl_showhitboxes("cl_showhitboxes", "-1");
if (cl_showhitboxes.GetInt() == index)
DrawClientHitboxes(0.0f, true);
}
void C_CSPlayer::ReleaseFlashlight( void )
@ -2536,11 +2520,11 @@ float C_CSPlayer::GetDeathCamInterpolationTime()
}
ConVar cl_cs_player_setupbones("cl_cs_player_setupbones", "1");
ConVar cl_server_setup_bones("cl_server_setup_bones", "1");
bool C_CSPlayer::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
{
if (cl_cs_player_setupbones.GetBool())
if (cl_server_setup_bones.GetBool())
{
AUTO_LOCK( m_BoneSetupLock );

View file

@ -218,12 +218,8 @@ public:
void SetActivity( Activity eActivity );
Activity GetActivity( void ) const;
ICSPlayerAnimState *GetPlayerAnimState() { return m_PlayerAnimState; }
public:
ICSPlayerAnimState *m_PlayerAnimState;
// Used to control animation state.
Activity m_Activity;

View file

@ -12,6 +12,7 @@
#include <cmath>
#include <cstdio>
#include "bone_setup.h"
#include "studio.h"
#include "util_shared.h"
#include "c_baseplayer.h"
#include "c_baseentity.h"
@ -1338,7 +1339,7 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
cmd->has_animation[pBasePlayer->index] = true;
cmd->animationdata[pBasePlayer->index].m_flAnimTime = pBasePlayer->m_flInterpolatedAnimTime;
pBasePlayer->GetBoneControllers(cmd->animationdata[pBasePlayer->index].m_encodedControllers);
pBasePlayer->GetPoseParameters(pBasePlayer->GetModelPtr(),
cmd->animationdata[pBasePlayer->index].m_poseParameters);
@ -1349,13 +1350,13 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
for (int j = 0; j < pBasePlayer->GetNumAnimOverlays(); j++)
{
cmd->animationdata[pBasePlayer->index].m_layerRecords[j].m_cycle =
pBasePlayer->GetAnimOverlay(j)->m_flCycle;
pBasePlayer->GetAnimOverlay(j)->m_flCycle.GetRaw();
cmd->animationdata[pBasePlayer->index].m_layerRecords[j].m_sequence =
pBasePlayer->GetAnimOverlay(j)->m_nSequence;
pBasePlayer->GetAnimOverlay(j)->m_nSequence.GetRaw();
cmd->animationdata[pBasePlayer->index].m_layerRecords[j].m_order =
pBasePlayer->GetAnimOverlay(j)->m_nOrder;
cmd->animationdata[pBasePlayer->index].m_layerRecords[j].m_weight =
pBasePlayer->GetAnimOverlay(j)->m_flWeight;
pBasePlayer->GetAnimOverlay(j)->m_flWeight.GetRaw();
}
}

View file

@ -1740,7 +1740,7 @@ void CBaseAnimating::BuildMatricesWithBoneMerge(
}
}
ConVar sv_pvsskipanimation( "sv_pvsskipanimation", "1", FCVAR_ARCHIVE, "Skips SetupBones when npc's are outside the PVS" );
ConVar sv_pvsskipanimation( "sv_pvsskipanimation", "0", FCVAR_ARCHIVE, "Skips SetupBones when npc's are outside the PVS" );
ConVar ai_setupbones_debug( "ai_setupbones_debug", "0", 0, "Shows that bones that are setup every think" );

View file

@ -8,6 +8,7 @@
#include "cbase.h"
#include "gamestringpool.h"
#include "ilagcompensationmanager.h"
#include "mapentities_shared.h"
#include "game.h"
#include "entityapi.h"
@ -235,10 +236,6 @@ static int g_nCommandClientIndex = 0;
// The chapter number of the current
static int g_nCurrentChapterIndex = -1;
#ifdef _DEBUG
static ConVar sv_showhitboxes( "sv_showhitboxes", "-1", FCVAR_CHEAT, "Send server-side hitboxes for specified entity to client (NOTE: this uses lots of bandwidth, use on listen server only)." );
#endif
void PrecachePointTemplates();
static ClientPutInServerOverrideFn g_pClientPutInServerOverride = NULL;
@ -1291,38 +1288,6 @@ void CServerGameDLL::PreClientUpdate( bool simulating )
//#endif
IGameSystem::PreClientUpdateAllSystems();
#ifdef _DEBUG
if ( sv_showhitboxes.GetInt() == -1 )
return;
if ( sv_showhitboxes.GetInt() == 0 )
{
// assume it's text
CBaseEntity *pEntity = NULL;
while (1)
{
pEntity = gEntList.FindEntityByName( pEntity, sv_showhitboxes.GetString() );
if ( !pEntity )
break;
CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( pEntity );
if (anim)
{
anim->DrawServerHitboxes();
}
}
return;
}
CBaseAnimating *anim = dynamic_cast< CBaseAnimating * >( CBaseEntity::Instance( engine->PEntityOfEntIndex( sv_showhitboxes.GetInt() ) ) );
if ( !anim )
return;
anim->DrawServerHitboxes();
#endif
}
void CServerGameDLL::Think( bool finalTick )

View file

@ -22,7 +22,8 @@ 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;
};
extern ILagCompensationManager *lagcompensation;

View file

@ -5,6 +5,9 @@
//=============================================================================//
#include "cbase.h"
#include "baseentity.h"
#include "convar.h"
#include "ilagcompensationmanager.h"
#include "player.h"
#include "usercmd.h"
#include "igamemovement.h"
@ -339,6 +342,14 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
gpGlobals->curtime = playerCurTime;
gpGlobals->frametime = playerFrameTime;
extern ConVar sv_showhitboxes;
if (sv_showhitboxes.GetInt() >= 0)
{
lagcompensation->StartLagCompensation( player, player->GetCurrentCommand() );
lagcompensation->FinishLagCompensation( 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) )
{
@ -457,5 +468,5 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
if ( gpGlobals->frametime > 0 )
{
player->m_nTickBase++;
}
}
}

View file

@ -31,9 +31,8 @@
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", "1", FCVAR_DEVELOPMENTONLY, "Flushes entity bone cache on lag compensation" );
ConVar sv_showlagcompensation( "sv_showlagcompensation", "0", FCVAR_CHEAT, "Show lag compensated hitboxes whenever a player is lag compensated." );
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_showhitboxes( "sv_showhitboxes", "-1");
//-----------------------------------------------------------------------------
// Purpose:
@ -110,8 +109,6 @@ public:
float m_masterCycle;
float m_poseParameters[MAX_POSE_PARAMETERS];
float m_encodedControllers[MAX_ENCODED_CONTROLLERS];
// TODO: do proper eyeAngles lag compensation
QAngle m_angEyeAngles;
};
@ -195,7 +192,7 @@ public:
void FinishLagCompensation( CBasePlayer *player );
private:
void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd );
virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd );
void ClearHistory()
{
@ -401,8 +398,8 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
int pl_index = pPlayer->entindex() - 1;
float flTargetSimulationTime = cmd->simulationtimes[pl_index + 1];
auto animationData = &cmd->animationdata[pl_index + 1];
auto animationData = &cmd->animationdata[pl_index + 1];
// get track history of this player
CUtlFixedLinkedList< LagRecord > *trackSim = &m_PlayerTrack[ pl_index ];
@ -617,7 +614,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
currentLayer->m_flCycle = animationData->m_layerRecords[layerIndex].m_cycle;
currentLayer->m_nOrder = animationData->m_layerRecords[layerIndex].m_order;
currentLayer->m_nSequence = animationData->m_layerRecords[layerIndex].m_sequence;
currentLayer->m_flWeight = animationData->m_layerRecords[layerIndex].m_weight;
currentLayer->m_flWeight = animationData->m_layerRecords[layerIndex].m_weight;
}
}
@ -643,7 +640,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
for( int paramIndex = 0; paramIndex < hdr->GetNumBoneControllers(); paramIndex++ )
{
restore->m_encodedControllers[paramIndex] = pPlayer->GetBoneController(paramIndex);
float encodedController = animationData->m_encodedControllers[paramIndex];
float encodedController = animationData->m_encodedControllers[paramIndex];
pPlayer->SetBoneControllerRaw( paramIndex, encodedController );
}
@ -657,7 +654,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
pPlayer->SetAnimTime(animationData->m_flAnimTime);
if ( sv_lagflushbonecache.GetBool() )
pPlayer->InvalidateBoneCache();
pPlayer->InvalidateBoneCache();
/*char text[256]; Q_snprintf( text, sizeof(text), "time %.2f", flTargetTime );
pPlayer->DrawServerHitboxes( 10 );
@ -669,17 +666,10 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer *pPlayer, CUserCmd *c
restore->m_fFlags = flags; // we need to restore these flags
change->m_fFlags = flags; // we have changed these flags
if( sv_showlagcompensation.GetInt() == pPlayer->entindex() )
{
pPlayer->DrawServerHitboxes(60, true);
}
static ConVar *sv_showplayerhitboxes = g_pCVar->FindVar("sv_showplayerhitboxes");
if ( sv_showplayerhitboxes->GetInt() == pPlayer->entindex() )
{
DevMsg("Server: %s => %f %f %f => %f (frac: %f)\n", pPlayer->GetPlayerName(), change->m_vecOrigin.x, change->m_vecOrigin.y, change->m_vecOrigin.z, flTargetSimulationTime, fracSim);
}
if (sv_showhitboxes.GetInt() == pPlayer->entindex())
pPlayer->DrawServerHitboxes(0.0f, true);
}
@ -790,7 +780,7 @@ void CLagCompensationManager::FinishLagCompensation( CBasePlayer *player )
}
pPlayer->SetSimulationTime( restore->m_flSimulationTime );
pPlayer->SetAnimTime( restore->m_flAnimTime );
pPlayer->SetAnimTime(restore->m_flAnimTime);
}
}

View file

@ -75,6 +75,7 @@ END_NETWORK_TABLE()
// During the first half-second of our life, don't draw ourselves if he's
// still playing his throw animation.
// (better yet, we could draw ourselves in his hand).
#ifndef CLIENT_DLL
if ( GetThrower() != C_BasePlayer::GetLocalPlayer() )
{
if ( gpGlobals->curtime - m_flSpawnTime < 0.5 )
@ -86,6 +87,7 @@ END_NETWORK_TABLE()
}
}
}
#endif
return BaseClass::DrawModel( flags );
}

View file

@ -414,14 +414,15 @@ void CCSPlayer::FireBullet(
{
CBasePlayer *lagPlayer = UTIL_PlayerByIndex( sv_showplayerhitboxes.GetInt() );
if( lagPlayer )
{
{
#ifdef CLIENT_DLL
DevMsg("Client:");
lagPlayer->DrawClientHitboxes(60, true);
DevMsg("Client: %s => %f %f %f => %f (%f)\n", lagPlayer->GetPlayerName(), lagPlayer->GetAbsOrigin().x, lagPlayer->GetAbsOrigin().y, lagPlayer->GetAbsOrigin().z,
lagPlayer->m_flInterpolatedSimulationTime, fmod(lagPlayer->m_flInterpolatedSimulationTime, TICK_INTERVAL) / TICK_INTERVAL);
#else
DevMsg("Server:");
lagPlayer->DrawServerHitboxes(60, true);
#endif
DevMsg("%s => %f %f %f\n", lagPlayer->GetPlayerName(), lagPlayer->GetAbsOrigin().x, lagPlayer->GetAbsOrigin().y, lagPlayer->GetAbsOrigin().z);
}
}

View file

@ -174,6 +174,7 @@ void FX_FireBullets(
CCSWeaponInfo *pWeaponInfo = static_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
#ifndef CLIENT_DLL
// Do the firing animation event.
if ( pPlayer && !pPlayer->IsDormant() )
{
@ -183,7 +184,6 @@ void FX_FireBullets(
pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_SECONDARY );
}
#ifndef CLIENT_DLL
// if this is server code, send the effect over to client as temp entity
// Dispatch one message for all the bullet impacts and sounds.
TE_FireBullets(
@ -311,11 +311,8 @@ void FX_FireBullets(
// On the client, it plays the planting animation.
void FX_PlantBomb( int iPlayerIndex, const Vector &vOrigin, PlantBombOption_t option )
{
#ifdef CLIENT_DLL
C_CSPlayer *pPlayer = ToCSPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) );
#else
#ifndef CLIENT_DLL
CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex) );
#endif
// Do the firing animation event.
if ( pPlayer && !pPlayer->IsDormant() )
@ -334,9 +331,8 @@ void FX_PlantBomb( int iPlayerIndex, const Vector &vOrigin, PlantBombOption_t op
}
break;
}
}
}
#ifndef CLIENT_DLL
// if this is server code, send the effect over to client as temp entity
// Dispatch one message for all the bullet impacts and sounds.
TE_PlantBomb( iPlayerIndex, vOrigin, option );