Skip to content

Commit f025ec1

Browse files
authored
Fix synced animations progress (PR #3923)
Addendum for commit b32eafc from PR #3520
1 parent 72606a8 commit f025ec1

File tree

5 files changed

+104
-70
lines changed

5 files changed

+104
-70
lines changed

Client/mods/deathmatch/logic/CClientPed.cpp

Lines changed: 79 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ using std::vector;
3838
extern CClientGame* g_pClientGame;
3939

4040
#ifndef M_PI
41-
#define M_PI 3.14159265358979323846
41+
#define M_PI 3.14159265358979323846
4242
#endif
4343

44-
#define INVALID_VALUE 0xFFFFFFFF
44+
#define INVALID_VALUE 0xFFFFFFFF
4545

4646
#define PED_INTERPOLATION_WARP_THRESHOLD 5 // Minimal threshold
4747
#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
11661166
for (; iter != listEnd; iter++)
11671167
{
11681168
pTempVehicle = *iter;
1169-
1169+
11701170
if (pTempVehicle->IsLocalEntity() != localVehicles)
11711171
continue;
11721172

@@ -2885,11 +2885,7 @@ void CClientPed::StreamedInPulse(bool bDoStandardPulses)
28852885

28862886
m_bRequestedAnimation = false;
28872887

2888-
// Copy our name incase it gets deleted
2889-
SString strAnimName = m_AnimationCache.strName;
2890-
// Run our animation
2891-
RunNamedAnimation(m_pAnimationBlock, strAnimName, m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop,
2892-
m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame);
2888+
RunAnimationFromCache();
28932889
}
28942890
}
28952891

@@ -3400,8 +3396,8 @@ void CClientPed::Interpolate()
34003396
{
34013397
// We're not at the end?
34023398
if (ulCurrentTime < m_ulEndRotationTime)
3403-
{
3404-
const float fDelta = GetOffsetRadians(m_fBeginRotation, m_fTargetRotationA);
3399+
{
3400+
const float fDelta = GetOffsetRadians(m_fBeginRotation, m_fTargetRotationA);
34053401

34063402
// Hack for the wrap-around (the edge seems to be varying...)
34073403
if (fDelta < -M_PI || fDelta > M_PI)
@@ -3411,7 +3407,7 @@ void CClientPed::Interpolate()
34113407
}
34123408
else
34133409
{
3414-
// Interpolate the player rotation
3410+
// Interpolate the player rotation
34153411
const float fDeltaTime = float(m_ulEndRotationTime - m_ulBeginRotationTime);
34163412
const float fCameraDelta = GetOffsetRadians(m_fBeginCameraRotation, m_fTargetCameraRotation);
34173413
const float fProgress = float(ulCurrentTime - m_ulBeginRotationTime);
@@ -3676,18 +3672,15 @@ void CClientPed::_CreateModel()
36763672
Kill(WEAPONTYPE_UNARMED, 0, false, true);
36773673
}
36783674

3679-
// Are we still playing a looped animation?
3680-
if (m_AnimationCache.bLoop && m_pAnimationBlock)
3675+
// Are we still playing animation?
3676+
if ((m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame || m_AnimationCache.progressWaitForStreamIn) && m_pAnimationBlock)
36813677
{
36823678
if (m_bisCurrentAnimationCustom)
36833679
{
36843680
m_bisNextAnimationCustom = true;
36853681
}
3686-
// Copy our anim name incase it gets deleted
3687-
SString strAnimName = m_AnimationCache.strName;
3688-
// Run our animation
3689-
RunNamedAnimation(m_pAnimationBlock, strAnimName, m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop,
3690-
m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame);
3682+
3683+
RunAnimationFromCache();
36913684
}
36923685

36933686
// Set the voice that corresponds to our model
@@ -3939,7 +3932,7 @@ void CClientPed::_ChangeModel()
39393932
// So make sure clothes geometry is built now...
39403933
m_pClothes->AddAllToModel();
39413934
m_pPlayerPed->RebuildPlayer();
3942-
}
3935+
}
39433936

39443937
// Remove reference to the old model we used (Flag extra GTA reference to be removed as well)
39453938
pLoadedModel->RemoveRef(true);
@@ -3958,18 +3951,14 @@ void CClientPed::_ChangeModel()
39583951
m_bDontChangeRadio = false;
39593952

39603953
// Are we still playing a looped animation?
3961-
if (m_AnimationCache.bLoop && m_pAnimationBlock)
3954+
if ((m_AnimationCache.bLoop || m_AnimationCache.bFreezeLastFrame || m_AnimationCache.progressWaitForStreamIn) && m_pAnimationBlock)
39623955
{
39633956
if (m_bisCurrentAnimationCustom)
39643957
{
39653958
m_bisNextAnimationCustom = true;
39663959
}
39673960

3968-
// Copy our anim name incase it gets deleted
3969-
SString strAnimName = m_AnimationCache.strName;
3970-
// Run our animation
3971-
RunNamedAnimation(m_pAnimationBlock, strAnimName, m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop,
3972-
m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame);
3961+
RunAnimationFromCache();
39733962
}
39743963

39753964
// Set the voice that corresponds to the new model
@@ -5260,7 +5249,7 @@ void CClientPed::Respawn(CVector* pvecPosition, bool bRestoreState, bool bCamera
52605249
// Restore the camera's interior whether we're restoring player states or not
52615250
g_pGame->GetWorld()->SetCurrentArea(ucCameraInterior);
52625251

5263-
// Reset goggle effect
5252+
// Reset goggle effect
52645253
g_pMultiplayer->SetNightVisionEnabled(bOldNightVision, false);
52655254
g_pMultiplayer->SetThermalVisionEnabled(bOldThermalVision, false);
52665255

@@ -5794,6 +5783,10 @@ void CClientPed::RunNamedAnimation(std::unique_ptr<CAnimBlock>& pBlock, const ch
57945783
m_AnimationCache.bUpdatePosition = bUpdatePosition;
57955784
m_AnimationCache.bInterruptable = bInterruptable;
57965785
m_AnimationCache.bFreezeLastFrame = bFreezeLastFrame;
5786+
m_AnimationCache.progress = 0.0f;
5787+
m_AnimationCache.speed = 1.0f;
5788+
m_AnimationCache.progressWaitForStreamIn = false;
5789+
m_AnimationCache.elapsedTime = 0.0f;
57975790
}
57985791

57995792
void CClientPed::KillAnimation()
@@ -5827,6 +5820,46 @@ std::unique_ptr<CAnimBlock> CClientPed::GetAnimationBlock()
58275820
return nullptr;
58285821
}
58295822

5823+
void CClientPed::RunAnimationFromCache()
5824+
{
5825+
if (!m_pAnimationBlock)
5826+
return;
5827+
5828+
bool needCalcProgress = m_AnimationCache.progressWaitForStreamIn;
5829+
float elapsedTime = m_AnimationCache.elapsedTime;
5830+
5831+
// Copy our name incase it gets deleted
5832+
std::string animName = m_AnimationCache.strName;
5833+
5834+
// Run our animation
5835+
RunNamedAnimation(m_pAnimationBlock, animName.c_str(), m_AnimationCache.iTime, m_AnimationCache.iBlend, m_AnimationCache.bLoop, m_AnimationCache.bUpdatePosition, m_AnimationCache.bInterruptable, m_AnimationCache.bFreezeLastFrame);
5836+
5837+
auto animAssoc = g_pGame->GetAnimManager()->RpAnimBlendClumpGetAssociation(GetClump(), animName.c_str());
5838+
if (!animAssoc)
5839+
return;
5840+
5841+
// If the anim is synced from the server side, we need to calculate the progress
5842+
float progress = m_AnimationCache.progress;
5843+
if (needCalcProgress)
5844+
{
5845+
float animLength = animAssoc->GetLength();
5846+
5847+
if (m_AnimationCache.bFreezeLastFrame) // time and loop is ignored if freezeLastFrame is true
5848+
progress = (elapsedTime / animLength) * m_AnimationCache.speed;
5849+
else
5850+
{
5851+
if (m_AnimationCache.bLoop)
5852+
progress = std::fmod(elapsedTime * m_AnimationCache.speed, animLength) / animLength;
5853+
else
5854+
// For non-looped animations, limit duration to animLength if time exceeds it
5855+
progress = (elapsedTime / (m_AnimationCache.iTime <= animLength ? m_AnimationCache.iTime : animLength)) * m_AnimationCache.speed;
5856+
}
5857+
}
5858+
5859+
animAssoc->SetCurrentProgress(std::clamp(progress, 0.0f, 1.0f));
5860+
animAssoc->SetCurrentSpeed(m_AnimationCache.speed);
5861+
}
5862+
58305863
void CClientPed::PostWeaponFire()
58315864
{
58325865
m_ulLastTimeFired = CClientTime::GetTime();
@@ -5919,7 +5952,7 @@ bool CClientPed::SetOnFire(bool bIsOnFire)
59195952
{
59205953
if (m_pPlayerPed)
59215954
return m_pPlayerPed->SetOnFire(bIsOnFire);
5922-
5955+
59235956
m_bIsOnFire = bIsOnFire;
59245957
return true;
59255958
}
@@ -6296,9 +6329,9 @@ void CClientPed::HandleWaitingForGroundToLoad()
62966329
{
62976330
// If not near any MTA objects, then don't bother waiting
62986331
SetFrozenWaitingForGroundToLoad(false);
6299-
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6332+
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63006333
OutputDebugLine("[AsyncLoading] FreezeUntilCollisionLoaded - Early stop");
6301-
#endif
6334+
#endif
63026335
return;
63036336
}
63046337

@@ -6319,29 +6352,29 @@ void CClientPed::HandleWaitingForGroundToLoad()
63196352
bool bASync = g_pGame->IsASyncLoadingEnabled();
63206353
bool bMTAObjLimit = pObjectManager->IsObjectLimitReached();
63216354
bool bHasModel = GetModelInfo() != NULL;
6322-
#ifndef ASYNC_LOADING_DEBUG_OUTPUTA
6355+
#ifndef ASYNC_LOADING_DEBUG_OUTPUTA
63236356
bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded(vecPosition, fUseRadius, m_usDimension);
6324-
#else
6357+
#else
63256358
SString strAround;
63266359
bool bMTALoaded = pObjectManager->ObjectsAroundPointLoaded(vecPosition, fUseRadius, m_usDimension, &strAround);
6327-
#endif
6360+
#endif
63286361

6329-
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6362+
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63306363
SString status = SString(
63316364
"%2.2f,%2.2f,%2.2f bASync:%d bHasModel:%d bMTALoaded:%d bMTAObjLimit:%d m_fGroundCheckTolerance:%2.2f m_fObjectsAroundTolerance:%2.2f "
63326365
"fUseRadius:%2.1f",
63336366
vecPosition.fX, vecPosition.fY, vecPosition.fZ, bASync, bHasModel, bMTALoaded, bMTAObjLimit, m_fGroundCheckTolerance, m_fObjectsAroundTolerance,
63346367
fUseRadius);
6335-
#endif
6368+
#endif
63366369

63376370
// See if ground is ready
63386371
if ((!bHasModel || !bMTALoaded) && m_fObjectsAroundTolerance < 1.f)
63396372
{
63406373
m_fGroundCheckTolerance = 0.f;
63416374
m_fObjectsAroundTolerance = std::min(1.f, m_fObjectsAroundTolerance + 0.01f);
6342-
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6375+
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63436376
status += (" FreezeUntilCollisionLoaded - wait");
6344-
#endif
6377+
#endif
63456378
}
63466379
else
63476380
{
@@ -6354,24 +6387,24 @@ void CClientPed::HandleWaitingForGroundToLoad()
63546387
if (fUseDist > -0.2f && fUseDist < 1.5f)
63556388
SetFrozenWaitingForGroundToLoad(false);
63566389

6357-
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6390+
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63586391
status += (SString(" GetDistanceFromGround: fDist:%2.2f fUseDist:%2.2f", fDist, fUseDist));
6359-
#endif
6392+
#endif
63606393

63616394
// Stop waiting after 3 frames, if the object limit has not been reached. (bASync should always be false here)
63626395
if (m_fGroundCheckTolerance > 0.03f && !bMTAObjLimit && !bASync)
63636396
SetFrozenWaitingForGroundToLoad(false);
63646397
}
63656398

6366-
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
6399+
#ifdef ASYNC_LOADING_DEBUG_OUTPUTA
63676400
OutputDebugLine(SStringX("[AsyncLoading] ")++ status);
63686401
g_pCore->GetGraphics()->DrawString(10, 220, -1, 1, status);
63696402

63706403
std::vector<SString> lineList;
63716404
strAround.Split("\n", lineList);
63726405
for (unsigned int i = 0; i < lineList.size(); i++)
63736406
g_pCore->GetGraphics()->DrawString(10, 230 + i * 10, -1, 1, lineList[i]);
6374-
#endif
6407+
#endif
63756408
}
63766409

63776410
//
@@ -6658,7 +6691,6 @@ bool CClientPed::ExitVehicle()
66586691
return false;
66596692
}
66606693

6661-
66626694
// Check the server is compatible if we are a ped
66636695
if (!IsLocalPlayer() && !g_pNet->CanServerBitStream(eBitStreamVersion::PedEnterExit))
66646696
{
@@ -6789,7 +6821,7 @@ void CClientPed::UpdateVehicleInOut()
67896821
CClientVehicle* vehicle = GetRealOccupiedVehicle();
67906822
if (!vehicle)
67916823
return;
6792-
6824+
67936825
// Call the onClientVehicleEnter event for the ped
67946826
// Check if it is cancelled before allowing the ped to enter the vehicle
67956827
CLuaArguments arguments;
@@ -6816,7 +6848,7 @@ void CClientPed::UpdateVehicleInOut()
68166848

68176849
if (realVehicle)
68186850
return;
6819-
6851+
68206852
// Call the onClientVehicleExit event for the ped
68216853
CLuaArguments arguments;
68226854
arguments.PushElement(this); // player / ped
@@ -6843,7 +6875,7 @@ void CClientPed::UpdateVehicleInOut()
68436875
// If we aren't working on leaving the car (he's eiter finished or cancelled/failed leaving)
68446876
if (IsLeavingVehicle())
68456877
return;
6846-
6878+
68476879
// Are we outside the car?
68486880
CClientVehicle* pVehicle = GetRealOccupiedVehicle();
68496881
if (pVehicle)
@@ -7047,7 +7079,7 @@ void CClientPed::UpdateVehicleInOut()
70477079
// If we aren't getting jacked
70487080
if (m_bIsGettingJacked)
70497081
return;
7050-
7082+
70517083
CClientVehicle* pVehicle = GetRealOccupiedVehicle();
70527084
CClientVehicle* pOccupiedVehicle = GetOccupiedVehicle();
70537085

@@ -7062,15 +7094,15 @@ void CClientPed::UpdateVehicleInOut()
70627094
// Are we supposed to be in a vehicle? But aren't?
70637095
if (!pOccupiedVehicle || pVehicle || IsWarpInToVehicleRequired())
70647096
return;
7065-
7097+
70667098
// Jax: this happens when we try to warp into a streamed out vehicle, including when we use CClientVehicle::StreamInNow
70677099
// ..maybe we need a different way to detect bike falls?
70687100

70697101
// Tell the server
70707102
NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream();
70717103
if (!pBitStream)
70727104
return;
7073-
7105+
70747106
// Write the ped or player ID to it
70757107
if (g_pNet->CanServerBitStream(eBitStreamVersion::PedEnterExit))
70767108
{

Client/mods/deathmatch/logic/CClientPed.h

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -127,23 +127,17 @@ struct SReplacedAnimation
127127

128128
struct SAnimationCache
129129
{
130-
SString strName;
131-
int iTime;
132-
bool bLoop;
133-
bool bUpdatePosition;
134-
bool bInterruptable;
135-
bool bFreezeLastFrame;
136-
int iBlend;
137-
138-
SAnimationCache()
139-
{
140-
iTime = -1;
141-
bLoop = false;
142-
bUpdatePosition = false;
143-
bInterruptable = false;
144-
bFreezeLastFrame = true;
145-
iBlend = 250;
146-
}
130+
std::string strName;
131+
int iTime{-1};
132+
bool bLoop{false};
133+
bool bUpdatePosition{false};
134+
bool bInterruptable{false};
135+
bool bFreezeLastFrame{true};
136+
int iBlend{250};
137+
float progress{0.0f};
138+
float speed{1.0f};
139+
bool progressWaitForStreamIn{false}; // for sync anim only
140+
float elapsedTime{0.0f}; // for sync anim only
147141
};
148142

149143
class CClientObject;
@@ -466,6 +460,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule
466460
void KillAnimation();
467461
std::unique_ptr<CAnimBlock> GetAnimationBlock();
468462
const SAnimationCache& GetAnimationCache() const noexcept { return m_AnimationCache; }
463+
void RunAnimationFromCache();
469464

470465
bool IsUsingGun();
471466

Client/mods/deathmatch/logic/CPacketHandler.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4004,7 +4004,7 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream)
40044004
std::string blockName, animName;
40054005
int time, blendTime;
40064006
bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore;
4007-
float progress, speed;
4007+
float elapsedTime, speed;
40084008

40094009
// Read data
40104010
bitStream.ReadString(blockName);
@@ -4016,12 +4016,14 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream)
40164016
bitStream.ReadBit(freezeLastFrame);
40174017
bitStream.Read(blendTime);
40184018
bitStream.ReadBit(taskRestore);
4019-
bitStream.Read(progress);
4019+
bitStream.Read(elapsedTime);
40204020
bitStream.Read(speed);
40214021

40224022
// Run anim
40234023
CStaticFunctionDefinitions::SetPedAnimation(*pPed, blockName, animName.c_str(), time, blendTime, looped, updatePosition, interruptable, freezeLastFrame);
4024-
CStaticFunctionDefinitions::SetPedAnimationProgress(*pPed, animName, progress);
4024+
pPed->m_AnimationCache.progressWaitForStreamIn = true;
4025+
pPed->m_AnimationCache.elapsedTime = elapsedTime;
4026+
40254027
CStaticFunctionDefinitions::SetPedAnimationSpeed(*pPed, animName, speed);
40264028

40274029
pPed->SetHasSyncedAnim(true);

0 commit comments

Comments
 (0)