Skip to content

Commit b32eafc

Browse files
authored
Synchronize ped animations for new players (PR #3520, Fixes #467)
1 parent c0b47ea commit b32eafc

File tree

9 files changed

+137
-5
lines changed

9 files changed

+137
-5
lines changed

Client/mods/deathmatch/logic/CClientPed.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,9 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule
552552

553553
std::unique_ptr<CAnimBlendAssociation> GetAnimAssociation(CAnimBlendHierarchySAInterface* pHierarchyInterface);
554554

555+
void SetHasSyncedAnim(bool synced) noexcept { m_hasSyncedAnim = synced; }
556+
bool HasSyncedAnim() const noexcept { return m_hasSyncedAnim; }
557+
555558
protected:
556559
// This constructor is for peds managed by a player. These are unknown to the ped manager.
557560
CClientPed(CClientManager* pManager, unsigned long ulModelID, ElementID ID, bool bIsLocalPlayer);
@@ -789,4 +792,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule
789792
CClientPed* m_pGettingJackedBy; // The ped that is jacking us
790793

791794
std::shared_ptr<CClientModel> m_clientModel;
795+
796+
bool m_hasSyncedAnim{};
797+
bool m_animationOverridedByClient{};
792798
};

Client/mods/deathmatch/logic/CPacketHandler.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3995,6 +3995,39 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream)
39953995
// Collisions
39963996
pPed->SetUsesCollision(bCollisonsEnabled);
39973997

3998+
// Animation
3999+
if (bitStream.Can(eBitStreamVersion::AnimationsSync))
4000+
{
4001+
// Contains animation data?
4002+
if (bitStream.ReadBit())
4003+
{
4004+
std::string blockName, animName;
4005+
int time, blendTime;
4006+
bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore;
4007+
float progress, speed;
4008+
4009+
// Read data
4010+
bitStream.ReadString(blockName);
4011+
bitStream.ReadString(animName);
4012+
bitStream.Read(time);
4013+
bitStream.ReadBit(looped);
4014+
bitStream.ReadBit(updatePosition);
4015+
bitStream.ReadBit(interruptable);
4016+
bitStream.ReadBit(freezeLastFrame);
4017+
bitStream.Read(blendTime);
4018+
bitStream.ReadBit(taskRestore);
4019+
bitStream.Read(progress);
4020+
bitStream.Read(speed);
4021+
4022+
// Run anim
4023+
CStaticFunctionDefinitions::SetPedAnimation(*pPed, blockName, animName.c_str(), time, blendTime, looped, updatePosition, interruptable, freezeLastFrame);
4024+
CStaticFunctionDefinitions::SetPedAnimationProgress(*pPed, animName, progress);
4025+
CStaticFunctionDefinitions::SetPedAnimationSpeed(*pPed, animName, speed);
4026+
4027+
pPed->SetHasSyncedAnim(true);
4028+
}
4029+
}
4030+
39984031
break;
39994032
}
40004033

Client/mods/deathmatch/logic/CPedSync.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ void CPedSync::WritePedInformation(NetBitStreamInterface* pBitStream, CClientPed
307307
ucFlags |= 0x20;
308308
if (pPed->IsInWater() != pPed->m_LastSyncedData->bIsInWater)
309309
ucFlags |= 0x40;
310+
if (pPed->HasSyncedAnim() && (!pPed->IsRunningAnimation() || pPed->m_animationOverridedByClient))
311+
ucFlags |= 0x80;
310312

311313
// Do we really have to sync this ped?
312314
if (ucFlags == 0)
@@ -395,4 +397,11 @@ void CPedSync::WritePedInformation(NetBitStreamInterface* pBitStream, CClientPed
395397
pBitStream->WriteBit(pPed->IsInWater());
396398
pPed->m_LastSyncedData->bIsInWater = pPed->IsInWater();
397399
}
400+
401+
// The animation has been overwritten or interrupted by the client
402+
if (ucFlags & 0x80 && pBitStream->Can(eBitStreamVersion::AnimationsSync))
403+
{
404+
pPed->SetHasSyncedAnim(false);
405+
pPed->m_animationOverridedByClient = false;
406+
}
398407
}

Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,10 @@ int CLuaPedDefs::SetPedAnimation(lua_State* luaVM)
22482248
}
22492249

22502250
pPed->SetTaskToBeRestoredOnAnimEnd(bTaskToBeRestoredOnAnimEnd);
2251+
2252+
if (pPed->HasSyncedAnim())
2253+
pPed->m_animationOverridedByClient = true;
2254+
22512255
lua_pushboolean(luaVM, true);
22522256
return 1;
22532257
}

Server/mods/deathmatch/logic/CPed.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ enum eBone
103103
BONE_RIGHTFOOT
104104
};
105105

106+
struct SPlayerAnimData
107+
{
108+
std::string blockName{};
109+
std::string animName{};
110+
int time{-1};
111+
bool loop{true};
112+
bool updatePosition{true};
113+
bool interruptable{true};
114+
bool freezeLastFrame{true};
115+
int blendTime{250};
116+
bool taskToBeRestoredOnAnimEnd{false};
117+
118+
std::int64_t startedTick{0};
119+
120+
float progress{0.0f};
121+
float speed{1.0f};
122+
123+
bool IsAnimating() const noexcept { return !blockName.empty() && !animName.empty(); }
124+
};
125+
106126
class CWeapon
107127
{
108128
public:
@@ -278,6 +298,11 @@ class CPed : public CElement
278298
std::vector<CPlayer*>::const_iterator NearPlayersIterBegin() { return m_nearPlayersList.begin(); }
279299
std::vector<CPlayer*>::const_iterator NearPlayersIterEnd() { return m_nearPlayersList.end(); }
280300

301+
const SPlayerAnimData& GetAnimationData() const noexcept { return m_animData; };
302+
void SetAnimationData(const SPlayerAnimData& animData) { m_animData = animData; };
303+
void SetAnimationProgress(float progress) { m_animData.progress = progress; };
304+
void SetAnimationSpeed(float speed) { m_animData.speed = speed; };
305+
281306
protected:
282307
bool ReadSpecialData(const int iLine) override;
283308

@@ -316,6 +341,7 @@ class CPed : public CElement
316341
bool m_bFrozen;
317342
bool m_bStealthAiming;
318343
CVehicle* m_pJackingVehicle;
344+
SPlayerAnimData m_animData{};
319345

320346
CVehicle* m_pVehicle;
321347
unsigned int m_uiVehicleSeat;

Server/mods/deathmatch/logic/CPedSync.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,20 @@ void CPedSync::OverrideSyncer(CPed* pPed, CPlayer* pPlayer, bool bPersist)
8282

8383
void CPedSync::UpdateAllSyncer()
8484
{
85+
auto currentTick = GetTickCount64_();
86+
8587
// Update all the ped's sync states
8688
for (auto iter = m_pPedManager->IterBegin(); iter != m_pPedManager->IterEnd(); iter++)
8789
{
90+
// Has the duration of the ped's animation already elapsed?
91+
const SPlayerAnimData& animData = (*iter)->GetAnimationData();
92+
if (animData.IsAnimating())
93+
{
94+
float deltaTime = currentTick - animData.startedTick;
95+
if (!animData.freezeLastFrame && animData.time > 0 && deltaTime >= animData.time)
96+
(*iter)->SetAnimationData({});
97+
}
98+
8899
// It is a ped, yet not a player
89100
if (IS_PED(*iter) && !IS_PLAYER(*iter))
90101
UpdateSyncer(*iter);
@@ -272,6 +283,9 @@ void CPedSync::Packet_PedSync(CPedSyncPacket& Packet)
272283
if (Data.ucFlags & 0x40)
273284
pPed->SetInWater(Data.bIsInWater);
274285

286+
if (Data.ucFlags & 0x80)
287+
pPed->SetAnimationData({});
288+
275289
// Is it time to sync to everyone
276290
bool bDoFarSync = llTickCountNow - pPed->GetLastFarSyncTick() >= g_TickRateSettings.iPedFarSync;
277291

Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4353,8 +4353,6 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri
43534353
CPed* pPed = static_cast<CPed*>(pElement);
43544354
if (pPed->IsSpawned())
43554355
{
4356-
// TODO: save their animation?
4357-
43584356
// Tell the players
43594357
CBitStream BitStream;
43604358
if (!blockName.empty() && !animName.empty())
@@ -4367,6 +4365,9 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri
43674365
if (pPed->IsChoking())
43684366
pPed->SetChoking(false);
43694367

4368+
// Store anim data
4369+
pPed->SetAnimationData(SPlayerAnimData{blockName, animName, iTime, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame, iBlend, bTaskToBeRestoredOnAnimEnd, GetTickCount64_()});
4370+
43704371
BitStream.pBitStream->WriteString<unsigned char>(blockName);
43714372
BitStream.pBitStream->WriteString<unsigned char>(animName);
43724373
BitStream.pBitStream->Write(iTime);
@@ -4381,9 +4382,12 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri
43814382
{
43824383
// Inform them to kill the current animation instead
43834384
BitStream.pBitStream->Write((unsigned char)0);
4385+
4386+
// Clear anim data
4387+
pPed->SetAnimationData({});
43844388
}
4385-
m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION, *BitStream.pBitStream));
43864389

4390+
m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION, *BitStream.pBitStream));
43874391
return true;
43884392
}
43894393
}
@@ -4405,14 +4409,17 @@ bool CStaticFunctionDefinitions::SetPedAnimationProgress(CElement* pElement, con
44054409
{
44064410
BitStream.pBitStream->WriteString<unsigned char>(animName);
44074411
BitStream.pBitStream->Write(fProgress);
4412+
4413+
pPed->SetAnimationProgress(fProgress);
44084414
}
44094415
else
44104416
{
44114417
// Inform them to kill the current animation instead
44124418
BitStream.pBitStream->Write((unsigned char)0);
4419+
pPed->SetAnimationData({});
44134420
}
4414-
m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION_PROGRESS, *BitStream.pBitStream));
44154421

4422+
m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION_PROGRESS, *BitStream.pBitStream));
44164423
return true;
44174424
}
44184425
}
@@ -4433,6 +4440,7 @@ bool CStaticFunctionDefinitions::SetPedAnimationSpeed(CElement* pElement, const
44334440
BitStream.pBitStream->WriteString<unsigned char>(animName);
44344441
BitStream.pBitStream->Write(fSpeed);
44354442

4443+
pPed->SetAnimationSpeed(fSpeed);
44364444
m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION_SPEED, *BitStream.pBitStream));
44374445

44384446
return true;

Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,34 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const
977977
BitStream.Write(currentWeaponSlot);
978978
}
979979

980+
// Animation
981+
if (BitStream.Can(eBitStreamVersion::AnimationsSync))
982+
{
983+
const SPlayerAnimData& animData = pPed->GetAnimationData();
984+
985+
// Contains animation data?
986+
bool animRunning = animData.IsAnimating();
987+
BitStream.WriteBit(animRunning);
988+
989+
if (animRunning)
990+
{
991+
BitStream.WriteString(animData.blockName);
992+
BitStream.WriteString(animData.animName);
993+
BitStream.Write(animData.time);
994+
BitStream.WriteBit(animData.loop);
995+
BitStream.WriteBit(animData.updatePosition);
996+
BitStream.WriteBit(animData.interruptable);
997+
BitStream.WriteBit(animData.freezeLastFrame);
998+
BitStream.Write(animData.blendTime);
999+
BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd);
1000+
1001+
// Write progress & speed
1002+
float deltaTime = GetTickCount64_() - animData.startedTick;
1003+
BitStream.Write((deltaTime / animData.time) * animData.speed);
1004+
BitStream.Write(animData.speed);
1005+
}
1006+
}
1007+
9801008
break;
9811009
}
9821010

Shared/sdk/net/bitstream.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,11 @@ enum class eBitStreamVersion : unsigned short
595595
// Add "spawnFlyingComponent" to setVehiclePanelState
596596
// 2024-12-31
597597
SetVehiclePanelState_SpawnFlyingComponent,
598-
598+
599+
// Ped animations synchronization
600+
// 2025-01-01
601+
AnimationsSync,
602+
599603
// This allows us to automatically increment the BitStreamVersion when things are added to this enum.
600604
// Make sure you only add things above this comment.
601605
Next,

0 commit comments

Comments
 (0)