Skip to content

Fix synced animations progress #3923

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

Merged
merged 4 commits into from
Jan 4, 2025
Merged
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
126 changes: 79 additions & 47 deletions Client/mods/deathmatch/logic/CClientPed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1166,7 +1166,7 @@ CClientVehicle* CClientPed::GetClosestEnterableVehicle(bool bGetPositionFromClos
for (; iter != listEnd; iter++)
{
pTempVehicle = *iter;

if (pTempVehicle->IsLocalEntity() != localVehicles)
continue;

Expand Down Expand Up @@ -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();
}
}

Expand Down Expand Up @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -5794,6 +5783,10 @@ 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 @@ -5827,6 +5820,46 @@ std::unique_ptr<CAnimBlock> 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->SetCurrentProgress(std::clamp(progress, 0.0f, 1.0f));
animAssoc->SetCurrentSpeed(m_AnimationCache.speed);
}

void CClientPed::PostWeaponFire()
{
m_ulLastTimeFired = CClientTime::GetTime();
Expand Down Expand Up @@ -5919,7 +5952,7 @@ bool CClientPed::SetOnFire(bool bIsOnFire)
{
if (m_pPlayerPed)
return m_pPlayerPed->SetOnFire(bIsOnFire);

m_bIsOnFire = bIsOnFire;
return true;
}
Expand Down Expand Up @@ -6296,9 +6329,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;
}

Expand All @@ -6319,29 +6352,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
{
Expand All @@ -6354,24 +6387,24 @@ 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);

std::vector<SString> lineList;
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
}

//
Expand Down Expand Up @@ -6658,7 +6691,6 @@ bool CClientPed::ExitVehicle()
return false;
}


// Check the server is compatible if we are a ped
if (!IsLocalPlayer() && !g_pNet->CanServerBitStream(eBitStreamVersion::PedEnterExit))
{
Expand Down Expand Up @@ -6789,7 +6821,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;
Expand All @@ -6816,7 +6848,7 @@ void CClientPed::UpdateVehicleInOut()

if (realVehicle)
return;

// Call the onClientVehicleExit event for the ped
CLuaArguments arguments;
arguments.PushElement(this); // player / ped
Expand All @@ -6843,7 +6875,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)
Expand Down Expand Up @@ -7047,7 +7079,7 @@ void CClientPed::UpdateVehicleInOut()
// If we aren't getting jacked
if (m_bIsGettingJacked)
return;

CClientVehicle* pVehicle = GetRealOccupiedVehicle();
CClientVehicle* pOccupiedVehicle = GetOccupiedVehicle();

Expand All @@ -7062,15 +7094,15 @@ 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?

// Tell the server
NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream();
if (!pBitStream)
return;

// Write the ped or player ID to it
if (g_pNet->CanServerBitStream(eBitStreamVersion::PedEnterExit))
{
Expand Down
29 changes: 12 additions & 17 deletions Client/mods/deathmatch/logic/CClientPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -466,6 +460,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule
void KillAnimation();
std::unique_ptr<CAnimBlock> GetAnimationBlock();
const SAnimationCache& GetAnimationCache() const noexcept { return m_AnimationCache; }
void RunAnimationFromCache();

bool IsUsingGun();

Expand Down
8 changes: 5 additions & 3 deletions Client/mods/deathmatch/logic/CPacketHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
Loading
Loading