From c67fd1bd96adb4ebfa37982afea3563da8f7420a Mon Sep 17 00:00:00 2001 From: Kamay Xutax Date: Thu, 11 Jul 2024 01:47:26 +0200 Subject: [PATCH] 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 --- game/client/c_baseanimating.cpp | 6 +-- game/client/c_baseanimatingoverlay.cpp | 2 +- game/client/c_baseentity.cpp | 2 +- game/client/cstrike/c_cs_player.cpp | 30 ++++----------- game/client/cstrike/c_cs_player.h | 4 -- game/client/in_main.cpp | 9 +++-- game/server/baseanimating.cpp | 2 +- game/server/gameinterface.cpp | 37 +------------------ game/server/ilagcompensationmanager.h | 3 +- game/server/player_command.cpp | 13 ++++++- game/server/player_lagcompensation.cpp | 30 +++++---------- .../cstrike/basecsgrenade_projectile.cpp | 2 + game/shared/cstrike/cs_player_shared.cpp | 7 ++-- game/shared/cstrike/fx_cs_shared.cpp | 10 ++--- 14 files changed, 52 insertions(+), 105 deletions(-) diff --git a/game/client/c_baseanimating.cpp b/game/client/c_baseanimating.cpp index 4575ff2787..a6b1b7c5b6 100644 --- a/game/client/c_baseanimating.cpp +++ b/game/client/c_baseanimating.cpp @@ -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() diff --git a/game/client/c_baseanimatingoverlay.cpp b/game/client/c_baseanimatingoverlay.cpp index 525b638443..42fb57d89f 100644 --- a/game/client/c_baseanimatingoverlay.cpp +++ b/game/client/c_baseanimatingoverlay.cpp @@ -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 diff --git a/game/client/c_baseentity.cpp b/game/client/c_baseentity.cpp index 54c7ed1f41..abe7a2be58 100644 --- a/game/client/c_baseentity.cpp +++ b/game/client/c_baseentity.cpp @@ -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; diff --git a/game/client/cstrike/c_cs_player.cpp b/game/client/cstrike/c_cs_player.cpp index 74c87ad9b7..c33ec6e233 100644 --- a/game/client/cstrike/c_cs_player.cpp +++ b/game/client/cstrike/c_cs_player.cpp @@ -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 ); diff --git a/game/client/cstrike/c_cs_player.h b/game/client/cstrike/c_cs_player.h index cbf975b203..ee0aa3ea7b 100644 --- a/game/client/cstrike/c_cs_player.h +++ b/game/client/cstrike/c_cs_player.h @@ -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; diff --git a/game/client/in_main.cpp b/game/client/in_main.cpp index 4bae678489..548b3090ac 100644 --- a/game/client/in_main.cpp +++ b/game/client/in_main.cpp @@ -12,6 +12,7 @@ #include #include #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(); } } diff --git a/game/server/baseanimating.cpp b/game/server/baseanimating.cpp index 1dcb235c68..8e211a0de1 100644 --- a/game/server/baseanimating.cpp +++ b/game/server/baseanimating.cpp @@ -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" ); diff --git a/game/server/gameinterface.cpp b/game/server/gameinterface.cpp index 364b63d179..195f1ab030 100644 --- a/game/server/gameinterface.cpp +++ b/game/server/gameinterface.cpp @@ -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 ) diff --git a/game/server/ilagcompensationmanager.h b/game/server/ilagcompensationmanager.h index 507d29bf8b..465d3cceab 100644 --- a/game/server/ilagcompensationmanager.h +++ b/game/server/ilagcompensationmanager.h @@ -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; diff --git a/game/server/player_command.cpp b/game/server/player_command.cpp index b607bbab8a..1bbc73ad80 100644 --- a/game/server/player_command.cpp +++ b/game/server/player_command.cpp @@ -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++; - } + } } diff --git a/game/server/player_lagcompensation.cpp b/game/server/player_lagcompensation.cpp index 6cc2cac1cf..2ad9132d41 100644 --- a/game/server/player_lagcompensation.cpp +++ b/game/server/player_lagcompensation.cpp @@ -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); } } diff --git a/game/shared/cstrike/basecsgrenade_projectile.cpp b/game/shared/cstrike/basecsgrenade_projectile.cpp index 5d6641b83e..e1df72df72 100644 --- a/game/shared/cstrike/basecsgrenade_projectile.cpp +++ b/game/shared/cstrike/basecsgrenade_projectile.cpp @@ -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 ); } diff --git a/game/shared/cstrike/cs_player_shared.cpp b/game/shared/cstrike/cs_player_shared.cpp index 2c4717a8e7..142b53634a 100644 --- a/game/shared/cstrike/cs_player_shared.cpp +++ b/game/shared/cstrike/cs_player_shared.cpp @@ -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); } } diff --git a/game/shared/cstrike/fx_cs_shared.cpp b/game/shared/cstrike/fx_cs_shared.cpp index 446d2b55d8..f4a04504c2 100644 --- a/game/shared/cstrike/fx_cs_shared.cpp +++ b/game/shared/cstrike/fx_cs_shared.cpp @@ -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 );