Skip to content

Fix anim progress & speed #4245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 50 additions & 27 deletions Client/mods/deathmatch/logic/CClientPed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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<float>(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<CAnimBlock>& pBlock, const char* szAnimName, int iTime, int iBlend, bool bLoop, bool bUpdatePosition,
Expand Down Expand Up @@ -5816,10 +5837,6 @@ void CClientPed::RunNamedAnimation(std::unique_ptr<CAnimBlock>& 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()
Expand Down Expand Up @@ -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<float>(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()
Expand Down
27 changes: 16 additions & 11 deletions Client/mods/deathmatch/logic/CClientPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -456,13 +456,18 @@ 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<CAnimBlock>& 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);
void KillAnimation();
std::unique_ptr<CAnimBlock> GetAnimationBlock();
const SAnimationCache& GetAnimationCache() const noexcept { return m_AnimationCache; }
void RunAnimationFromCache();
void UpdateAnimationProgressAndSpeed();

bool IsUsingGun();

Expand Down
12 changes: 6 additions & 6 deletions Client/mods/deathmatch/logic/CPacketHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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<std::int64_t>(startTime);
pPed->m_AnimationCache.speed = speed;
pPed->m_AnimationCache.progress = 0.0f;

pPed->SetHasSyncedAnim(true);
}
Expand Down
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2247,6 +2247,8 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CClientEntity& Entity, const SS
}
}
}

Ped.m_AnimationCache.startTime = GetTimestamp();
}
else
{
Expand Down
8 changes: 8 additions & 0 deletions Client/mods/deathmatch/logic/rpc/CPedRPCs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -307,6 +313,7 @@ void CPedRPCs::SetPedAnimationProgress(CClientEntity* pSource, NetBitStreamInter
if (pAnimAssociation)
{
pAnimAssociation->SetCurrentProgress(fProgress);
pPed->m_AnimationCache.progress = fProgress;
}
}
}
Expand Down Expand Up @@ -334,6 +341,7 @@ void CPedRPCs::SetPedAnimationSpeed(CClientEntity* pSource, NetBitStreamInterfac
if (pAnimAssociation)
{
pAnimAssociation->SetCurrentSpeed(fSpeed);
pPed->m_AnimationCache.speed = fSpeed;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Server/mods/deathmatch/logic/CPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
4 changes: 2 additions & 2 deletions Server/mods/deathmatch/logic/CPedSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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++)
Expand All @@ -91,7 +91,7 @@ void CPedSync::UpdateAllSyncer()
const SPlayerAnimData& animData = (*iter)->GetAnimationData();
if (animData.IsAnimating())
{
float deltaTime = currentTick - animData.startedTick;
float deltaTime = static_cast<float>(currentTimestamp - animData.startTime);
if (!animData.freezeLastFrame && animData.time > 0 && deltaTime >= animData.time)
(*iter)->SetAnimationData({});
}
Expand Down
5 changes: 2 additions & 3 deletions Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<double>(animData.startTime));
BitStream.Write(animData.speed);
}
}
Expand Down
2 changes: 2 additions & 0 deletions Shared/sdk/SharedUtil.Time.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions Shared/sdk/SharedUtil.Time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::chrono::milliseconds>(now.time_since_epoch()).count();
}

//
// Get the time as a sortable string.
// Set bDate to include the date, bMs to include milliseconds
Expand Down
Loading