From f0a87484c3b3949d387288cb1e8b36ff80469450 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 4 Jan 2025 17:18:36 +0100 Subject: [PATCH 1/6] Fix synced animations --- Client/mods/deathmatch/logic/CClientPed.cpp | 125 +++++++++++------- Client/mods/deathmatch/logic/CClientPed.h | 29 ++-- .../mods/deathmatch/logic/CPacketHandler.cpp | 8 +- .../logic/CStaticFunctionDefinitions.cpp | 6 + .../logic/packets/CEntityAddPacket.cpp | 6 +- 5 files changed, 104 insertions(+), 70 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientPed.cpp b/Client/mods/deathmatch/logic/CClientPed.cpp index adf37d02248..3e57ff789ab 100644 --- a/Client/mods/deathmatch/logic/CClientPed.cpp +++ b/Client/mods/deathmatch/logic/CClientPed.cpp @@ -38,10 +38,10 @@ using std::vector; extern CClientGame* g_pClientGame; #ifndef M_PI -#define M_PI 3.14159265358979323846 + #define M_PI 3.14159265358979323846 #endif -#define INVALID_VALUE 0xFFFFFFFF +#define INVALID_VALUE 0xFFFFFFFF #define PED_INTERPOLATION_WARP_THRESHOLD 5 // Minimal threshold #define PED_INTERPOLATION_WARP_THRESHOLD_FOR_SPEED 5 // Units to increment the threshold per speed unit @@ -1166,7 +1166,7 @@ CClientVehicle* CClientPed::GetClosestEnterableVehicle(bool bGetPositionFromClos for (; iter != listEnd; iter++) { pTempVehicle = *iter; - + if (pTempVehicle->IsLocalEntity() != localVehicles) continue; @@ -2885,11 +2885,7 @@ void CClientPed::StreamedInPulse(bool bDoStandardPulses) m_bRequestedAnimation = false; - // Copy our name incase it gets deleted - SString strAnimName = m_AnimationCache.strName; - // Run our animation - RunNamedAnimation(m_pAnimationBlock, strAnimName, m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop, - m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame); + RunAnimationFromCache(); } } @@ -3400,8 +3396,8 @@ void CClientPed::Interpolate() { // We're not at the end? if (ulCurrentTime < m_ulEndRotationTime) - { - const float fDelta = GetOffsetRadians(m_fBeginRotation, m_fTargetRotationA); + { + const float fDelta = GetOffsetRadians(m_fBeginRotation, m_fTargetRotationA); // Hack for the wrap-around (the edge seems to be varying...) if (fDelta < -M_PI || fDelta > M_PI) @@ -3411,7 +3407,7 @@ void CClientPed::Interpolate() } else { - // Interpolate the player rotation + // Interpolate the player rotation const float fDeltaTime = float(m_ulEndRotationTime - m_ulBeginRotationTime); const float fCameraDelta = GetOffsetRadians(m_fBeginCameraRotation, m_fTargetCameraRotation); const float fProgress = float(ulCurrentTime - m_ulBeginRotationTime); @@ -3676,18 +3672,15 @@ void CClientPed::_CreateModel() Kill(WEAPONTYPE_UNARMED, 0, false, true); } - // Are we still playing a looped animation? - if (m_AnimationCache.bLoop && m_pAnimationBlock) + // Are we still playing animation? + if ((m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame || m_AnimationCache.progressWaitForStreamIn) && m_pAnimationBlock) { if (m_bisCurrentAnimationCustom) { m_bisNextAnimationCustom = true; } - // Copy our anim name incase it gets deleted - SString strAnimName = m_AnimationCache.strName; - // Run our animation - RunNamedAnimation(m_pAnimationBlock, strAnimName, m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop, - m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame); + + RunAnimationFromCache(); } // Set the voice that corresponds to our model @@ -3939,7 +3932,7 @@ void CClientPed::_ChangeModel() // So make sure clothes geometry is built now... m_pClothes->AddAllToModel(); m_pPlayerPed->RebuildPlayer(); - } + } // Remove reference to the old model we used (Flag extra GTA reference to be removed as well) pLoadedModel->RemoveRef(true); @@ -3958,18 +3951,14 @@ void CClientPed::_ChangeModel() m_bDontChangeRadio = false; // Are we still playing a looped animation? - if (m_AnimationCache.bLoop && m_pAnimationBlock) + if ((m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame || m_AnimationCache.progressWaitForStreamIn) && m_pAnimationBlock) { if (m_bisCurrentAnimationCustom) { m_bisNextAnimationCustom = true; } - // Copy our anim name incase it gets deleted - SString strAnimName = m_AnimationCache.strName; - // Run our animation - RunNamedAnimation(m_pAnimationBlock, strAnimName, m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop, - m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame); + RunAnimationFromCache(); } // Set the voice that corresponds to the new model @@ -5260,7 +5249,7 @@ void CClientPed::Respawn(CVector* pvecPosition, bool bRestoreState, bool bCamera // Restore the camera's interior whether we're restoring player states or not g_pGame->GetWorld()->SetCurrentArea(ucCameraInterior); - // Reset goggle effect + // Reset goggle effect g_pMultiplayer->SetNightVisionEnabled(bOldNightVision, false); g_pMultiplayer->SetThermalVisionEnabled(bOldThermalVision, false); @@ -5794,6 +5783,10 @@ void CClientPed::RunNamedAnimation(std::unique_ptr& pBlock, const ch m_AnimationCache.bUpdatePosition = bUpdatePosition; m_AnimationCache.bInterruptable = bInterruptable; m_AnimationCache.bFreezeLastFrame = bFreezeLastFrame; + m_AnimationCache.progress = 0.0f; + m_AnimationCache.speed = 1.0f; + m_AnimationCache.progressWaitForStreamIn = false; + m_AnimationCache.elapsedTime = 0.0f; } void CClientPed::KillAnimation() @@ -5827,6 +5820,45 @@ std::unique_ptr CClientPed::GetAnimationBlock() return nullptr; } +void CClientPed::RunAnimationFromCache() +{ + if (!m_pAnimationBlock) + return; + + bool needCalcProgress = m_AnimationCache.progressWaitForStreamIn; + float elapsedTime = m_AnimationCache.elapsedTime; + + // Copy our name incase it gets deleted + std::string animName = m_AnimationCache.strName; + + // Run our animation + RunNamedAnimation(m_pAnimationBlock, animName.c_str(), m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop, m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame); + + auto animAssoc = g_pGame->GetAnimManager()->RpAnimBlendClumpGetAssociation(GetClump(), animName.c_str()); + if (!animAssoc) + return; + + // If the anim is synced from the server side, we need to calculate the progress + float progress = m_AnimationCache.progress; + if (needCalcProgress) + { + float animLength = animAssoc->GetLength(); + + if (m_AnimationCache.bFreezeLastFrame) // time and loop is ignored if freezeLastFrame is true + progress = (elapsedTime / animLength) * m_AnimationCache.speed; + else + { + if (m_AnimationCache.bLoop) + progress = std::fmod(elapsedTime * m_AnimationCache.speed, animLength) / animLength; + else + // For non-looped animations, limit duration to animLength if time exceeds it + progress = (elapsedTime / (m_AnimationCache.iTime <= animLength ? m_AnimationCache.iTime : animLength)) * m_AnimationCache.speed; + } + } + + animAssoc->SetCurrentSpeed(m_AnimationCache.speed); +} + void CClientPed::PostWeaponFire() { m_ulLastTimeFired = CClientTime::GetTime(); @@ -5919,7 +5951,7 @@ bool CClientPed::SetOnFire(bool bIsOnFire) { if (m_pPlayerPed) return m_pPlayerPed->SetOnFire(bIsOnFire); - + m_bIsOnFire = bIsOnFire; return true; } @@ -6296,9 +6328,9 @@ void CClientPed::HandleWaitingForGroundToLoad() { // If not near any MTA objects, then don't bother waiting SetFrozenWaitingForGroundToLoad(false); - #ifdef ASYNC_LOADING_DEBUG_OUTPUTA +#ifdef ASYNC_LOADING_DEBUG_OUTPUTA OutputDebugLine("[AsyncLoading] FreezeUntilCollisionLoaded - Early stop"); - #endif +#endif return; } @@ -6319,29 +6351,29 @@ void CClientPed::HandleWaitingForGroundToLoad() bool bASync = g_pGame->IsASyncLoadingEnabled(); bool bMTAObjLimit = pObjectManager->IsObjectLimitReached(); bool bHasModel = GetModelInfo() != NULL; - #ifndef ASYNC_LOADING_DEBUG_OUTPUTA +#ifndef ASYNC_LOADING_DEBUG_OUTPUTA bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded(vecPosition, fUseRadius, m_usDimension); - #else +#else SString strAround; bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded(vecPosition, fUseRadius, m_usDimension, &strAround); - #endif +#endif - #ifdef ASYNC_LOADING_DEBUG_OUTPUTA +#ifdef ASYNC_LOADING_DEBUG_OUTPUTA SString status = SString( "%2.2f,%2.2f,%2.2f bASync:%d bHasModel:%d bMTALoaded:%d bMTAObjLimit:%d m_fGroundCheckTolerance:%2.2f m_fObjectsAroundTolerance:%2.2f " "fUseRadius:%2.1f", vecPosition.fX, vecPosition.fY, vecPosition.fZ, bASync, bHasModel, bMTALoaded, bMTAObjLimit, m_fGroundCheckTolerance, m_fObjectsAroundTolerance, fUseRadius); - #endif +#endif // See if ground is ready if ((!bHasModel || !bMTALoaded) && m_fObjectsAroundTolerance < 1.f) { m_fGroundCheckTolerance = 0.f; m_fObjectsAroundTolerance = std::min(1.f, m_fObjectsAroundTolerance + 0.01f); - #ifdef ASYNC_LOADING_DEBUG_OUTPUTA +#ifdef ASYNC_LOADING_DEBUG_OUTPUTA status += (" FreezeUntilCollisionLoaded - wait"); - #endif +#endif } else { @@ -6354,16 +6386,16 @@ void CClientPed::HandleWaitingForGroundToLoad() if (fUseDist > -0.2f && fUseDist < 1.5f) SetFrozenWaitingForGroundToLoad(false); - #ifdef ASYNC_LOADING_DEBUG_OUTPUTA +#ifdef ASYNC_LOADING_DEBUG_OUTPUTA status += (SString(" GetDistanceFromGround: fDist:%2.2f fUseDist:%2.2f", fDist, fUseDist)); - #endif +#endif // Stop waiting after 3 frames, if the object limit has not been reached. (bASync should always be false here) if (m_fGroundCheckTolerance > 0.03f && !bMTAObjLimit && !bASync) SetFrozenWaitingForGroundToLoad(false); } - #ifdef ASYNC_LOADING_DEBUG_OUTPUTA +#ifdef ASYNC_LOADING_DEBUG_OUTPUTA OutputDebugLine(SStringX("[AsyncLoading] ")++ status); g_pCore->GetGraphics()->DrawString(10, 220, -1, 1, status); @@ -6371,7 +6403,7 @@ void CClientPed::HandleWaitingForGroundToLoad() strAround.Split("\n", lineList); for (unsigned int i = 0; i < lineList.size(); i++) g_pCore->GetGraphics()->DrawString(10, 230 + i * 10, -1, 1, lineList[i]); - #endif +#endif } // @@ -6658,7 +6690,6 @@ bool CClientPed::ExitVehicle() return false; } - // Check the server is compatible if we are a ped if (!IsLocalPlayer() && !g_pNet->CanServerBitStream(eBitStreamVersion::PedEnterExit)) { @@ -6789,7 +6820,7 @@ void CClientPed::UpdateVehicleInOut() CClientVehicle* vehicle = GetRealOccupiedVehicle(); if (!vehicle) return; - + // Call the onClientVehicleEnter event for the ped // Check if it is cancelled before allowing the ped to enter the vehicle CLuaArguments arguments; @@ -6816,7 +6847,7 @@ void CClientPed::UpdateVehicleInOut() if (realVehicle) return; - + // Call the onClientVehicleExit event for the ped CLuaArguments arguments; arguments.PushElement(this); // player / ped @@ -6843,7 +6874,7 @@ void CClientPed::UpdateVehicleInOut() // If we aren't working on leaving the car (he's eiter finished or cancelled/failed leaving) if (IsLeavingVehicle()) return; - + // Are we outside the car? CClientVehicle* pVehicle = GetRealOccupiedVehicle(); if (pVehicle) @@ -7047,7 +7078,7 @@ void CClientPed::UpdateVehicleInOut() // If we aren't getting jacked if (m_bIsGettingJacked) return; - + CClientVehicle* pVehicle = GetRealOccupiedVehicle(); CClientVehicle* pOccupiedVehicle = GetOccupiedVehicle(); @@ -7062,7 +7093,7 @@ void CClientPed::UpdateVehicleInOut() // Are we supposed to be in a vehicle? But aren't? if (!pOccupiedVehicle || pVehicle || IsWarpInToVehicleRequired()) return; - + // Jax: this happens when we try to warp into a streamed out vehicle, including when we use CClientVehicle::StreamInNow // ..maybe we need a different way to detect bike falls? @@ -7070,7 +7101,7 @@ void CClientPed::UpdateVehicleInOut() NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); if (!pBitStream) return; - + // Write the ped or player ID to it if (g_pNet->CanServerBitStream(eBitStreamVersion::PedEnterExit)) { diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index 65df08efff7..4286d64e910 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -127,23 +127,17 @@ struct SReplacedAnimation struct SAnimationCache { - SString strName; - int iTime; - bool bLoop; - bool bUpdatePosition; - bool bInterruptable; - bool bFreezeLastFrame; - int iBlend; - - SAnimationCache() - { - iTime = -1; - bLoop = false; - bUpdatePosition = false; - bInterruptable = false; - bFreezeLastFrame = true; - iBlend = 250; - } + std::string strName; + int iTime{-1}; + bool bLoop{false}; + bool bUpdatePosition{false}; + bool bInterruptable{false}; + bool bFreezeLastFrame{true}; + int iBlend{250}; + float progress{0.0f}; + float speed{1.0f}; + bool progressWaitForStreamIn{false}; // for sync anim only + float elapsedTime{0.0f}; // for sync anim only }; class CClientObject; @@ -466,6 +460,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule void KillAnimation(); std::unique_ptr GetAnimationBlock(); const SAnimationCache& GetAnimationCache() const noexcept { return m_AnimationCache; } + void RunAnimationFromCache(); bool IsUsingGun(); diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index ea5443f1768..9001f6ffdea 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -4004,7 +4004,7 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) std::string blockName, animName; int time, blendTime; bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore; - float progress, speed; + float elapsedTime, speed; // Read data bitStream.ReadString(blockName); @@ -4016,12 +4016,14 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) bitStream.ReadBit(freezeLastFrame); bitStream.Read(blendTime); bitStream.ReadBit(taskRestore); - bitStream.Read(progress); + bitStream.Read(elapsedTime); bitStream.Read(speed); // Run anim CStaticFunctionDefinitions::SetPedAnimation(*pPed, blockName, animName.c_str(), time, blendTime, looped, updatePosition, interruptable, freezeLastFrame); - CStaticFunctionDefinitions::SetPedAnimationProgress(*pPed, animName, progress); + pPed->m_AnimationCache.progressWaitForStreamIn = true; + pPed->m_AnimationCache.elapsedTime = elapsedTime; + CStaticFunctionDefinitions::SetPedAnimationSpeed(*pPed, animName, speed); pPed->SetHasSyncedAnim(true); diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 7098e7fdbd7..259bd7951c9 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -2274,10 +2274,14 @@ bool CStaticFunctionDefinitions::SetPedAnimationProgress(CClientEntity& Entity, pAnimAssociation->SetCurrentProgress(fProgress); return true; } + + Ped.m_AnimationCache.progress = fProgress; } else { Ped.KillAnimation(); + Ped.m_AnimationCache.progress = 0.0f; + return true; } } @@ -2300,6 +2304,8 @@ bool CStaticFunctionDefinitions::SetPedAnimationSpeed(CClientEntity& Entity, con pAnimAssociation->SetCurrentSpeed(fSpeed); return true; } + + Ped.m_AnimationCache.speed = fSpeed; } } diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index 6a77404f59a..5521512417c 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -998,9 +998,9 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(animData.blendTime); BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); - // Write progress & speed - float deltaTime = GetTickCount64_() - animData.startedTick; - BitStream.Write((deltaTime / animData.time) * animData.speed); + // Write elapsed time & speed + float elapsedTime = GetTickCount64_() - animData.startedTick; + BitStream.Write(elapsedTime); BitStream.Write(animData.speed); } } From 94fbed9dc2fd880791b744776fc32c2623458f4d Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 4 Jan 2025 17:24:00 +0100 Subject: [PATCH 2/6] Update CClientPed.cpp --- Client/mods/deathmatch/logic/CClientPed.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Client/mods/deathmatch/logic/CClientPed.cpp b/Client/mods/deathmatch/logic/CClientPed.cpp index 3e57ff789ab..318dbce324b 100644 --- a/Client/mods/deathmatch/logic/CClientPed.cpp +++ b/Client/mods/deathmatch/logic/CClientPed.cpp @@ -5856,6 +5856,7 @@ void CClientPed::RunAnimationFromCache() } } + animAssoc->SetCurrentProgress(progress); animAssoc->SetCurrentSpeed(m_AnimationCache.speed); } From 7863e386e706c7e64a0aae489b6530433e300aa2 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 4 Jan 2025 17:25:42 +0100 Subject: [PATCH 3/6] clamp --- Client/mods/deathmatch/logic/CClientPed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CClientPed.cpp b/Client/mods/deathmatch/logic/CClientPed.cpp index 318dbce324b..30554d60b6e 100644 --- a/Client/mods/deathmatch/logic/CClientPed.cpp +++ b/Client/mods/deathmatch/logic/CClientPed.cpp @@ -5856,7 +5856,7 @@ void CClientPed::RunAnimationFromCache() } } - animAssoc->SetCurrentProgress(progress); + animAssoc->SetCurrentProgress(std::clamp(progress, 0.0f, 1.0f)); animAssoc->SetCurrentSpeed(m_AnimationCache.speed); } From b85cce4c1f8ab874e15df876f4ed435a841e8135 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 4 Jan 2025 20:24:20 +0100 Subject: [PATCH 4/6] Update CStaticFunctionDefinitions.cpp --- Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 259bd7951c9..fdb7a897a88 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -2280,7 +2280,6 @@ bool CStaticFunctionDefinitions::SetPedAnimationProgress(CClientEntity& Entity, else { Ped.KillAnimation(); - Ped.m_AnimationCache.progress = 0.0f; return true; } From 2755fd8e96a724544d0e8d77f833d7c3a964a85f Mon Sep 17 00:00:00 2001 From: FileEX Date: Fri, 6 Jun 2025 21:05:49 +0200 Subject: [PATCH 5/6] Fix animation progress & speed --- Client/mods/deathmatch/logic/CClientPed.cpp | 77 ++++++++++++------- Client/mods/deathmatch/logic/CClientPed.h | 27 ++++--- .../mods/deathmatch/logic/CPacketHandler.cpp | 12 +-- .../logic/CStaticFunctionDefinitions.cpp | 2 + Client/mods/deathmatch/logic/rpc/CPedRPCs.cpp | 8 ++ Server/mods/deathmatch/logic/CPed.h | 2 +- Server/mods/deathmatch/logic/CPedSync.cpp | 4 +- .../logic/packets/CEntityAddPacket.cpp | 5 +- Shared/sdk/SharedUtil.Time.h | 2 + Shared/sdk/SharedUtil.Time.hpp | 9 +++ 10 files changed, 98 insertions(+), 50 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientPed.cpp b/Client/mods/deathmatch/logic/CClientPed.cpp index 59ce2f4cde7..45f13651891 100644 --- a/Client/mods/deathmatch/logic/CClientPed.cpp +++ b/Client/mods/deathmatch/logic/CClientPed.cpp @@ -2895,6 +2895,11 @@ void CClientPed::StreamedInPulse(bool bDoStandardPulses) } } + // Are we need to update anim speed & progress? + // We need to do it here because the anim starts on the next frame after calling RunNamedAnimation + if (m_pAnimationBlock && m_AnimationCache.progressWaitForStreamIn && IsAnimationInProgress()) + UpdateAnimationProgressAndSpeed(); + // Update our alpha unsigned char ucAlpha = m_ucAlpha; // Are we in a different interior to the camera? set our alpha to 0 @@ -3685,8 +3690,8 @@ void CClientPed::_CreateModel() Kill(WEAPONTYPE_UNARMED, 0, false, true); } - // Are we still playing animation? - if ((m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame || m_AnimationCache.progressWaitForStreamIn) && m_pAnimationBlock) + // Are we still playing a animation? + if (m_pAnimationBlock && IsAnimationInProgress()) { if (m_bisCurrentAnimationCustom) { @@ -3963,8 +3968,8 @@ void CClientPed::_ChangeModel() } m_bDontChangeRadio = false; - // Are we still playing a looped animation? - if ((m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame || m_AnimationCache.progressWaitForStreamIn) && m_pAnimationBlock) + // Are we still playing a animation? + if (m_pAnimationBlock && IsAnimationInProgress()) { if (m_bisCurrentAnimationCustom) { @@ -5728,7 +5733,23 @@ bool CClientPed::IsRunningAnimation() } return false; } - return (m_AnimationCache.bLoop && m_pAnimationBlock); + return (m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame && m_pAnimationBlock); +} + +bool CClientPed::IsAnimationInProgress() +{ + bool constAnim = m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame; + + if (!m_pAnimationBlock) + return constAnim; + + float elapsedTime = static_cast(GetTimestamp() - m_AnimationCache.startTime) / 1000.0f; + + auto animBlendHierarchy = g_pGame->GetAnimManager()->GetAnimation(m_AnimationCache.strName.c_str(), m_pAnimationBlock); + if (!animBlendHierarchy) + return constAnim; + + return constAnim || elapsedTime < animBlendHierarchy->GetTotalTime(); } void CClientPed::RunNamedAnimation(std::unique_ptr& pBlock, const char* szAnimName, int iTime, int iBlend, bool bLoop, bool bUpdatePosition, @@ -5816,10 +5837,6 @@ void CClientPed::RunNamedAnimation(std::unique_ptr& pBlock, const ch m_AnimationCache.bUpdatePosition = bUpdatePosition; m_AnimationCache.bInterruptable = bInterruptable; m_AnimationCache.bFreezeLastFrame = bFreezeLastFrame; - m_AnimationCache.progress = 0.0f; - m_AnimationCache.speed = 1.0f; - m_AnimationCache.progressWaitForStreamIn = false; - m_AnimationCache.elapsedTime = 0.0f; } void CClientPed::KillAnimation() @@ -5858,39 +5875,45 @@ void CClientPed::RunAnimationFromCache() if (!m_pAnimationBlock) return; - bool needCalcProgress = m_AnimationCache.progressWaitForStreamIn; - float elapsedTime = m_AnimationCache.elapsedTime; - // Copy our name incase it gets deleted std::string animName = m_AnimationCache.strName; // Run our animation RunNamedAnimation(m_pAnimationBlock, animName.c_str(), m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop, m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame); - auto animAssoc = g_pGame->GetAnimManager()->RpAnimBlendClumpGetAssociation(GetClump(), animName.c_str()); + // Set anim progress & speed + m_AnimationCache.progressWaitForStreamIn = true; +} + +void CClientPed::UpdateAnimationProgressAndSpeed() +{ + if (!m_AnimationCache.progressWaitForStreamIn) + return; + + // Get current anim + auto animAssoc = g_pGame->GetAnimManager()->RpAnimBlendClumpGetAssociation(GetClump(), m_AnimationCache.strName.c_str()); if (!animAssoc) return; - // If the anim is synced from the server side, we need to calculate the progress - float progress = m_AnimationCache.progress; - if (needCalcProgress) - { - float animLength = animAssoc->GetLength(); + float animLength = animAssoc->GetLength(); + float progress = 0.0f; + float elapsedTime = static_cast(GetTimestamp() - m_AnimationCache.startTime) / 1000.0f; - if (m_AnimationCache.bFreezeLastFrame) // time and loop is ignored if freezeLastFrame is true - progress = (elapsedTime / animLength) * m_AnimationCache.speed; + if (m_AnimationCache.bFreezeLastFrame) // time and loop is ignored if freezeLastFrame is true + progress = (elapsedTime / animLength) * m_AnimationCache.speed; + else + { + if (m_AnimationCache.bLoop) + progress = std::fmod(elapsedTime * m_AnimationCache.speed, animLength) / animLength; else - { - if (m_AnimationCache.bLoop) - progress = std::fmod(elapsedTime * m_AnimationCache.speed, animLength) / animLength; - else - // For non-looped animations, limit duration to animLength if time exceeds it - progress = (elapsedTime / (m_AnimationCache.iTime <= animLength ? m_AnimationCache.iTime : animLength)) * m_AnimationCache.speed; - } + // For non-looped animations, limit duration to animLength if time exceeds it + progress = (elapsedTime / (m_AnimationCache.iTime <= animLength ? m_AnimationCache.iTime : animLength)) * m_AnimationCache.speed; } animAssoc->SetCurrentProgress(std::clamp(progress, 0.0f, 1.0f)); animAssoc->SetCurrentSpeed(m_AnimationCache.speed); + + m_AnimationCache.progressWaitForStreamIn = false; } void CClientPed::PostWeaponFire() diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index 6f2ba652fb4..3a329499d78 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -129,17 +129,17 @@ struct SReplacedAnimation struct SAnimationCache { - std::string strName; - int iTime{-1}; - bool bLoop{false}; - bool bUpdatePosition{false}; - bool bInterruptable{false}; - bool bFreezeLastFrame{true}; - int iBlend{250}; - float progress{0.0f}; - float speed{1.0f}; - bool progressWaitForStreamIn{false}; // for sync anim only - float elapsedTime{0.0f}; // for sync anim only + std::string strName; + int iTime{-1}; + bool bLoop{false}; + bool bUpdatePosition{false}; + bool bInterruptable{false}; + bool bFreezeLastFrame{true}; + int iBlend{250}; + float progress{0.0f}; + float speed{1.0f}; + bool progressWaitForStreamIn{false}; + std::int64_t startTime{0}; }; class CClientObject; @@ -456,6 +456,10 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule bool GetRunningAnimationName(SString& strBlockName, SString& strAnimName); bool IsRunningAnimation(); + + // It checks whether the animation is still playing based on time, not on task execution. + bool IsAnimationInProgress(); + void RunNamedAnimation(std::unique_ptr& pBlock, const char* szAnimName, int iTime = -1, int iBlend = 250, bool bLoop = true, bool bUpdatePosition = true, bool bInterruptable = false, bool bFreezeLastFrame = true, bool bRunInSequence = false, bool bOffsetPed = false, bool bHoldLastFrame = false); @@ -463,6 +467,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule std::unique_ptr GetAnimationBlock(); const SAnimationCache& GetAnimationCache() const noexcept { return m_AnimationCache; } void RunAnimationFromCache(); + void UpdateAnimationProgressAndSpeed(); bool IsUsingGun(); diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 1f50f7ac679..3e94d83bf52 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -4010,7 +4010,8 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) std::string blockName, animName; int time, blendTime; bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore; - float elapsedTime, speed; + float speed; + double startTime; // Read data bitStream.ReadString(blockName); @@ -4022,15 +4023,14 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) bitStream.ReadBit(freezeLastFrame); bitStream.Read(blendTime); bitStream.ReadBit(taskRestore); - bitStream.Read(elapsedTime); + bitStream.Read(startTime); bitStream.Read(speed); // Run anim CStaticFunctionDefinitions::SetPedAnimation(*pPed, blockName, animName.c_str(), time, blendTime, looped, updatePosition, interruptable, freezeLastFrame); - pPed->m_AnimationCache.progressWaitForStreamIn = true; - pPed->m_AnimationCache.elapsedTime = elapsedTime; - - CStaticFunctionDefinitions::SetPedAnimationSpeed(*pPed, animName, speed); + pPed->m_AnimationCache.startTime = static_cast(startTime); + pPed->m_AnimationCache.speed = speed; + pPed->m_AnimationCache.progress = 0.0f; pPed->SetHasSyncedAnim(true); } diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 857aa910477..1bd697f2a0d 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -2247,6 +2247,8 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CClientEntity& Entity, const SS } } } + + Ped.m_AnimationCache.startTime = GetTimestamp(); } else { diff --git a/Client/mods/deathmatch/logic/rpc/CPedRPCs.cpp b/Client/mods/deathmatch/logic/rpc/CPedRPCs.cpp index f01e91f09db..7e40474fb35 100644 --- a/Client/mods/deathmatch/logic/rpc/CPedRPCs.cpp +++ b/Client/mods/deathmatch/logic/rpc/CPedRPCs.cpp @@ -276,6 +276,12 @@ void CPedRPCs::SetPedAnimation(CClientEntity* pSource, NetBitStreamInterface& bi pPed->RunNamedAnimation(pBlock, animName.c_str(), iTime, iBlend, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame); pPed->SetTaskToBeRestoredOnAnimEnd(bTaskToBeRestoredOnAnimEnd); pPed->SetTaskTypeToBeRestoredOnAnimEnd((eTaskType)TASK_SIMPLE_DUCK); + + pPed->m_AnimationCache.startTime = GetTimestamp(); + pPed->m_AnimationCache.speed = 1.0f; + pPed->m_AnimationCache.progress = 0.0f; + + pPed->SetHasSyncedAnim(true); } } } @@ -307,6 +313,7 @@ void CPedRPCs::SetPedAnimationProgress(CClientEntity* pSource, NetBitStreamInter if (pAnimAssociation) { pAnimAssociation->SetCurrentProgress(fProgress); + pPed->m_AnimationCache.progress = fProgress; } } } @@ -334,6 +341,7 @@ void CPedRPCs::SetPedAnimationSpeed(CClientEntity* pSource, NetBitStreamInterfac if (pAnimAssociation) { pAnimAssociation->SetCurrentSpeed(fSpeed); + pPed->m_AnimationCache.speed = fSpeed; } } } diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index 4e8440afa80..67ec9057429 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -115,7 +115,7 @@ struct SPlayerAnimData int blendTime{250}; bool taskToBeRestoredOnAnimEnd{false}; - std::int64_t startedTick{0}; + std::int64_t startTime{0}; float progress{0.0f}; float speed{1.0f}; diff --git a/Server/mods/deathmatch/logic/CPedSync.cpp b/Server/mods/deathmatch/logic/CPedSync.cpp index 13dfad1c434..42952266cf1 100644 --- a/Server/mods/deathmatch/logic/CPedSync.cpp +++ b/Server/mods/deathmatch/logic/CPedSync.cpp @@ -82,7 +82,7 @@ void CPedSync::OverrideSyncer(CPed* pPed, CPlayer* pPlayer, bool bPersist) void CPedSync::UpdateAllSyncer() { - auto currentTick = GetTickCount64_(); + auto currentTimestamp = GetTimestamp(); // Update all the ped's sync states for (auto iter = m_pPedManager->IterBegin(); iter != m_pPedManager->IterEnd(); iter++) @@ -91,7 +91,7 @@ void CPedSync::UpdateAllSyncer() const SPlayerAnimData& animData = (*iter)->GetAnimationData(); if (animData.IsAnimating()) { - float deltaTime = currentTick - animData.startedTick; + float deltaTime = static_cast(currentTimestamp - animData.startTime); if (!animData.freezeLastFrame && animData.time > 0 && deltaTime >= animData.time) (*iter)->SetAnimationData({}); } diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index dd3038f7418..4fcb00e84e4 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -1005,9 +1005,8 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(animData.blendTime); BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); - // Write elapsed time & speed - float elapsedTime = GetTickCount64_() - animData.startedTick; - BitStream.Write(elapsedTime); + // Write start time & speed + BitStream.Write(static_cast(animData.startTime)); BitStream.Write(animData.speed); } } diff --git a/Shared/sdk/SharedUtil.Time.h b/Shared/sdk/SharedUtil.Time.h index 244a60f5807..284a91a9af9 100644 --- a/Shared/sdk/SharedUtil.Time.h +++ b/Shared/sdk/SharedUtil.Time.h @@ -41,6 +41,8 @@ namespace SharedUtil // double GetSecondCount(); + std::int64_t GetTimestamp(); + // // Get the time as a sortable string. // Set bDate to include the date, bMs to include milliseconds diff --git a/Shared/sdk/SharedUtil.Time.hpp b/Shared/sdk/SharedUtil.Time.hpp index f84ae8e1fe6..7bcdc2859e9 100644 --- a/Shared/sdk/SharedUtil.Time.hpp +++ b/Shared/sdk/SharedUtil.Time.hpp @@ -86,6 +86,15 @@ double SharedUtil::GetSecondCount() return GetTickCount64_() * (1 / 1000.0); } +// +// Returns a timestamp in ms +// +std::int64_t SharedUtil::GetTimestamp() +{ + auto now = std::chrono::system_clock::now(); + return std::chrono::duration_cast(now.time_since_epoch()).count(); +} + // // Get the time as a sortable string. // Set bDate to include the date, bMs to include milliseconds From 0a903aa1122daf95d29e7e0025d2d57e0217ffc7 Mon Sep 17 00:00:00 2001 From: FileEX Date: Fri, 6 Jun 2025 21:11:17 +0200 Subject: [PATCH 6/6] Fix IsRunningAnimation condition --- Client/mods/deathmatch/logic/CClientPed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CClientPed.cpp b/Client/mods/deathmatch/logic/CClientPed.cpp index 45f13651891..94a64d8c6ab 100644 --- a/Client/mods/deathmatch/logic/CClientPed.cpp +++ b/Client/mods/deathmatch/logic/CClientPed.cpp @@ -5733,7 +5733,7 @@ bool CClientPed::IsRunningAnimation() } return false; } - return (m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame && m_pAnimationBlock); + return (m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame) && m_pAnimationBlock; } bool CClientPed::IsAnimationInProgress()