From 2cf44485ce57813d50cd519b87fcd5119737aa87 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 29 Jun 2024 02:06:05 +0200 Subject: [PATCH 1/8] Fix old bug --- .../mods/deathmatch/logic/CPacketHandler.cpp | 23 +++++++++++ Server/mods/deathmatch/logic/CPed.h | 40 +++++++++++++++++++ .../logic/CStaticFunctionDefinitions.cpp | 8 +++- .../logic/packets/CEntityAddPacket.cpp | 19 +++++++++ Shared/sdk/net/bitstream.h | 4 ++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index dd95351ed60..982706bc373 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -3965,6 +3965,29 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) // Collisions pPed->SetUsesCollision(bCollisonsEnabled); + // Animation + if (bitStream.Can(eBitStreamVersion::AnimationsSync)) + { + std::string blockName, animName; + if (bitStream.ReadString(blockName) && bitStream.ReadString(animName)) + { + int time, blendTime; + bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore; + + // Read data + bitStream.Read(time); + bitStream.ReadBit(looped); + bitStream.ReadBit(updatePosition); + bitStream.ReadBit(interruptable); + bitStream.ReadBit(freezeLastFrame); + bitStream.Read(blendTime); + bitStream.ReadBit(taskRestore); + + // Run anim + CStaticFunctionDefinitions::SetPedAnimation(*pPed, blockName, animName.c_str(), time, blendTime, looped, updatePosition, interruptable, freezeLastFrame); + } + } + break; } diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index 47e70e71891..492d6047de6 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -103,6 +103,42 @@ enum eBone BONE_RIGHTFOOT }; +struct SPlayerAnimData +{ + std::string blockName; + std::string animName; + int time; + bool loop; + bool updatePosition; + bool interruptable; + bool freezeLastFrame; + int blendTime; + bool taskToBeRestoredOnAnimEnd; + + SPlayerAnimData() + : blockName(""), + animName(""), + time(-1), + loop(true), + updatePosition(true), + interruptable(true), + freezeLastFrame(true), + blendTime(250), + taskToBeRestoredOnAnimEnd(false){}; + + SPlayerAnimData(const std::string& block, const std::string& anim, int time, bool loop, bool updatePos, bool interrupt, bool freeze, int blend, + bool taskRestore) + : blockName(block), + animName(anim), + time(time), + loop(loop), + updatePosition(updatePos), + interruptable(interrupt), + freezeLastFrame(freeze), + blendTime(blend), + taskToBeRestoredOnAnimEnd(taskRestore){}; +}; + class CWeapon { public: @@ -278,6 +314,9 @@ class CPed : public CElement std::vector::const_iterator NearPlayersIterBegin() { return m_nearPlayersList.begin(); } std::vector::const_iterator NearPlayersIterEnd() { return m_nearPlayersList.end(); } + const SPlayerAnimData& GetAnimationData() const noexcept { return m_pAnimData; }; + void SetAnimationData(SPlayerAnimData animData) noexcept { m_pAnimData = animData; }; + protected: bool ReadSpecialData(const int iLine) override; @@ -316,6 +355,7 @@ class CPed : public CElement bool m_bFrozen; bool m_bStealthAiming; CVehicle* m_pJackingVehicle; + SPlayerAnimData m_pAnimData; CVehicle* m_pVehicle; unsigned int m_uiVehicleSeat; diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 188bf1519c7..22bd3ec408d 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -4337,8 +4337,6 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri CPed* pPed = static_cast(pElement); if (pPed->IsSpawned()) { - // TODO: save their animation? - // Tell the players CBitStream BitStream; if (!blockName.empty() && !animName.empty()) @@ -4351,6 +4349,9 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri if (pPed->IsChoking()) pPed->SetChoking(false); + // Store anim data + pPed->SetAnimationData({blockName, animName, iTime, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame, iBlend, bTaskToBeRestoredOnAnimEnd}); + BitStream.pBitStream->WriteString(blockName); BitStream.pBitStream->WriteString(animName); BitStream.pBitStream->Write(iTime); @@ -4365,6 +4366,9 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri { // Inform them to kill the current animation instead BitStream.pBitStream->Write((unsigned char)0); + + // Clear anim data + pPed->SetAnimationData({}); } m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION, *BitStream.pBitStream)); diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index ff568968641..0a8a0662765 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -959,6 +959,25 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(currentWeaponSlot); } + // Aniamtion + if (BitStream.Can(eBitStreamVersion::AnimationsSync)) + { + const SPlayerAnimData& animData = pPed->GetAnimationData(); + + if (!animData.blockName.empty() && !animData.animName.empty()) + { + BitStream.WriteString(animData.blockName); + BitStream.WriteString(animData.animName); + BitStream.Write(animData.time); + BitStream.WriteBit(animData.loop); + BitStream.WriteBit(animData.updatePosition); + BitStream.WriteBit(animData.interruptable); + BitStream.WriteBit(animData.freezeLastFrame); + BitStream.Write(animData.blendTime); + BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); + } + } + break; } diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index 9c32320d537..598887c0669 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -556,6 +556,10 @@ enum class eBitStreamVersion : unsigned short // 2024-06-16 PedSync_Revision, + // Ped animations synchronization + // 2024-06-29 + AnimationsSync, + // This allows us to automatically increment the BitStreamVersion when things are added to this enum. // Make sure you only add things above this comment. Next, From b5fb38ef678dd5c8ea57d7e80654e328522704ba Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 29 Jun 2024 11:59:21 +0200 Subject: [PATCH 2/8] Reviews --- Server/mods/deathmatch/logic/CPed.h | 37 +++++++++++------------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index 492d6047de6..7857aa3b1c9 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -105,26 +105,17 @@ enum eBone struct SPlayerAnimData { - std::string blockName; - std::string animName; - int time; - bool loop; - bool updatePosition; - bool interruptable; - bool freezeLastFrame; - int blendTime; - bool taskToBeRestoredOnAnimEnd; - - SPlayerAnimData() - : blockName(""), - animName(""), - time(-1), - loop(true), - updatePosition(true), - interruptable(true), - freezeLastFrame(true), - blendTime(250), - taskToBeRestoredOnAnimEnd(false){}; + std::string blockName{""}; + std::string animName{""}; + int time{-1}; + bool loop{true}; + bool updatePosition{true}; + bool interruptable{true}; + bool freezeLastFrame{true}; + int blendTime{250}; + bool taskToBeRestoredOnAnimEnd{false}; + + SPlayerAnimData() = default; SPlayerAnimData(const std::string& block, const std::string& anim, int time, bool loop, bool updatePos, bool interrupt, bool freeze, int blend, bool taskRestore) @@ -314,8 +305,8 @@ class CPed : public CElement std::vector::const_iterator NearPlayersIterBegin() { return m_nearPlayersList.begin(); } std::vector::const_iterator NearPlayersIterEnd() { return m_nearPlayersList.end(); } - const SPlayerAnimData& GetAnimationData() const noexcept { return m_pAnimData; }; - void SetAnimationData(SPlayerAnimData animData) noexcept { m_pAnimData = animData; }; + const SPlayerAnimData& GetAnimationData() const noexcept { return m_animData; }; + void SetAnimationData(const SPlayerAnimData& animData) noexcept { m_animData = animData; }; protected: bool ReadSpecialData(const int iLine) override; @@ -355,7 +346,7 @@ class CPed : public CElement bool m_bFrozen; bool m_bStealthAiming; CVehicle* m_pJackingVehicle; - SPlayerAnimData m_pAnimData; + SPlayerAnimData m_animData; CVehicle* m_pVehicle; unsigned int m_uiVehicleSeat; From 622b630e712ff67338ca8bd8548230d4efef5234 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 29 Jun 2024 19:16:11 +0200 Subject: [PATCH 3/8] Review --- .../mods/deathmatch/logic/CPacketHandler.cpp | 7 +++-- Server/mods/deathmatch/logic/CPed.h | 6 ++--- .../logic/packets/CEntityAddPacket.cpp | 26 +++++++++---------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 982706bc373..a9213c4031c 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -3968,13 +3968,16 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) // Animation if (bitStream.Can(eBitStreamVersion::AnimationsSync)) { - std::string blockName, animName; - if (bitStream.ReadString(blockName) && bitStream.ReadString(animName)) + // Contains animation data? + if (bitStream.ReadBit()) { + std::string blockName, animName; int time, blendTime; bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore; // Read data + bitStream.ReadString(blockName); + bitStream.ReadString(animName); bitStream.Read(time); bitStream.ReadBit(looped); bitStream.ReadBit(updatePosition); diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index 7857aa3b1c9..ea6595d2700 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -105,8 +105,8 @@ enum eBone struct SPlayerAnimData { - std::string blockName{""}; - std::string animName{""}; + std::string blockName{}; + std::string animName{}; int time{-1}; bool loop{true}; bool updatePosition{true}; @@ -306,7 +306,7 @@ class CPed : public CElement std::vector::const_iterator NearPlayersIterEnd() { return m_nearPlayersList.end(); } const SPlayerAnimData& GetAnimationData() const noexcept { return m_animData; }; - void SetAnimationData(const SPlayerAnimData& animData) noexcept { m_animData = animData; }; + void SetAnimationData(const SPlayerAnimData& animData) { m_animData = animData; }; protected: bool ReadSpecialData(const int iLine) override; diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index 0a8a0662765..929da4bb489 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -959,23 +959,23 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const BitStream.Write(currentWeaponSlot); } - // Aniamtion + // Animation if (BitStream.Can(eBitStreamVersion::AnimationsSync)) { const SPlayerAnimData& animData = pPed->GetAnimationData(); - if (!animData.blockName.empty() && !animData.animName.empty()) - { - BitStream.WriteString(animData.blockName); - BitStream.WriteString(animData.animName); - BitStream.Write(animData.time); - BitStream.WriteBit(animData.loop); - BitStream.WriteBit(animData.updatePosition); - BitStream.WriteBit(animData.interruptable); - BitStream.WriteBit(animData.freezeLastFrame); - BitStream.Write(animData.blendTime); - BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); - } + // Contains animation data? + BitStream.WriteBit(!animData.blockName.empty() && !animData.animName.empty()); + + BitStream.WriteString(animData.blockName); + BitStream.WriteString(animData.animName); + BitStream.Write(animData.time); + BitStream.WriteBit(animData.loop); + BitStream.WriteBit(animData.updatePosition); + BitStream.WriteBit(animData.interruptable); + BitStream.WriteBit(animData.freezeLastFrame); + BitStream.Write(animData.blendTime); + BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); } break; From 89a4954b885ec7ac2399d1bf7a8375c1b4af0862 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sat, 29 Jun 2024 19:22:24 +0200 Subject: [PATCH 4/8] Condition --- .../logic/packets/CEntityAddPacket.cpp | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index 929da4bb489..67e50af228d 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -965,17 +965,21 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const const SPlayerAnimData& animData = pPed->GetAnimationData(); // Contains animation data? - BitStream.WriteBit(!animData.blockName.empty() && !animData.animName.empty()); - - BitStream.WriteString(animData.blockName); - BitStream.WriteString(animData.animName); - BitStream.Write(animData.time); - BitStream.WriteBit(animData.loop); - BitStream.WriteBit(animData.updatePosition); - BitStream.WriteBit(animData.interruptable); - BitStream.WriteBit(animData.freezeLastFrame); - BitStream.Write(animData.blendTime); - BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); + bool animRunning = !animData.blockName.empty() && !animData.animName.empty(); + BitStream.WriteBit(animRunning); + + if (animRunning) + { + BitStream.WriteString(animData.blockName); + BitStream.WriteString(animData.animName); + BitStream.Write(animData.time); + BitStream.WriteBit(animData.loop); + BitStream.WriteBit(animData.updatePosition); + BitStream.WriteBit(animData.interruptable); + BitStream.WriteBit(animData.freezeLastFrame); + BitStream.Write(animData.blendTime); + BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); + } } break; From d9a8a2d5c8998e88de428e6af6f30d7407b55930 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sun, 14 Jul 2024 21:44:26 +0200 Subject: [PATCH 5/8] Validation mechanism --- Client/mods/deathmatch/logic/CClientPed.h | 55 ++++++++++++------- .../mods/deathmatch/logic/CPacketHandler.cpp | 7 +++ Client/mods/deathmatch/logic/CPedSync.cpp | 9 +++ .../deathmatch/logic/luadefs/CLuaPedDefs.cpp | 4 ++ Server/mods/deathmatch/logic/CPed.h | 18 ++++-- Server/mods/deathmatch/logic/CPedSync.cpp | 3 + .../logic/CStaticFunctionDefinitions.cpp | 10 +++- .../logic/packets/CEntityAddPacket.cpp | 13 +++++ 8 files changed, 91 insertions(+), 28 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index 688dba501fb..2eca1f1d4eb 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -85,6 +85,34 @@ enum eDeathAnims DEATH_ANIM_TORSO = 20, }; + +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; + } + + bool operator!=(const SAnimationCache& other) const + { + return strName != other.strName || iTime != other.iTime || bLoop != other.bLoop || bUpdatePosition != other.bUpdatePosition || + bInterruptable != other.bInterruptable || bFreezeLastFrame != other.bFreezeLastFrame || iBlend != other.iBlend; + } +}; + struct SDelayedSyncData { unsigned long ulTime; @@ -107,6 +135,7 @@ struct SLastSyncedPedData float fRotation; bool bOnFire; bool bIsInWater; + SAnimationCache animCache; }; struct SRestoreWeaponItem @@ -125,27 +154,6 @@ struct SReplacedAnimation CAnimBlendHierarchySAInterface* pAnimationHierarchy; }; -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; - } -}; - class CClientObject; // To hide the ugly "pointer truncation from DWORD* to unsigned long warning @@ -553,6 +561,8 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule std::unique_ptr GetAnimAssociation(CAnimBlendHierarchySAInterface* pHierarchyInterface); + void SetHasSyncedAnim(bool synced) noexcept { m_hasSyncedAnim = synced; }; + bool HasSyncedAnim() const noexcept { return m_hasSyncedAnim; }; protected: // This constructor is for peds managed by a player. These are unknown to the ped manager. CClientPed(CClientManager* pManager, unsigned long ulModelID, ElementID ID, bool bIsLocalPlayer); @@ -788,4 +798,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule CClientPed* m_pGettingJackedBy; // The ped that is jacking us std::shared_ptr m_clientModel; + + bool m_hasSyncedAnim; + bool m_animationOverridedByClient; }; diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index a9213c4031c..e22704f7a4b 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -3974,6 +3974,7 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) std::string blockName, animName; int time, blendTime; bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore; + float progress, speed; // Read data bitStream.ReadString(blockName); @@ -3985,9 +3986,15 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) bitStream.ReadBit(freezeLastFrame); bitStream.Read(blendTime); bitStream.ReadBit(taskRestore); + bitStream.Read(progress); + bitStream.Read(speed); // Run anim CStaticFunctionDefinitions::SetPedAnimation(*pPed, blockName, animName.c_str(), time, blendTime, looped, updatePosition, interruptable, freezeLastFrame); + CStaticFunctionDefinitions::SetPedAnimationProgress(*pPed, animName, progress); + CStaticFunctionDefinitions::SetPedAnimationSpeed(*pPed, animName, speed); + + pPed->SetHasSyncedAnim(true); } } diff --git a/Client/mods/deathmatch/logic/CPedSync.cpp b/Client/mods/deathmatch/logic/CPedSync.cpp index 8461ac1a877..a0e3b6b9ca5 100644 --- a/Client/mods/deathmatch/logic/CPedSync.cpp +++ b/Client/mods/deathmatch/logic/CPedSync.cpp @@ -307,6 +307,8 @@ void CPedSync::WritePedInformation(NetBitStreamInterface* pBitStream, CClientPed ucFlags |= 0x20; if (pPed->IsInWater() != pPed->m_LastSyncedData->bIsInWater) ucFlags |= 0x40; + if (pPed->HasSyncedAnim() && (!pPed->IsRunningAnimation() || pPed->m_animationOverridedByClient)) + ucFlags |= 0x80; // Do we really have to sync this ped? if (ucFlags == 0) @@ -395,4 +397,11 @@ void CPedSync::WritePedInformation(NetBitStreamInterface* pBitStream, CClientPed pBitStream->WriteBit(pPed->IsInWater()); pPed->m_LastSyncedData->bIsInWater = pPed->IsInWater(); } + + // The animation has been overwritten or interrupted by the client + if (ucFlags & 0x80 && pBitStream->Can(eBitStreamVersion::AnimationsSync)) + { + pPed->SetHasSyncedAnim(false); + pPed->m_animationOverridedByClient = false; + } } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp index 63c37110646..471bb211151 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp @@ -2203,6 +2203,10 @@ int CLuaPedDefs::SetPedAnimation(lua_State* luaVM) } pPed->SetTaskToBeRestoredOnAnimEnd(bTaskToBeRestoredOnAnimEnd); + + if (pPed->HasSyncedAnim()) + pPed->m_animationOverridedByClient = true; + lua_pushboolean(luaVM, true); return 1; } diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index ea6595d2700..a7441910577 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -105,8 +105,8 @@ enum eBone struct SPlayerAnimData { - std::string blockName{}; - std::string animName{}; + std::string blockName; + std::string animName; int time{-1}; bool loop{true}; bool updatePosition{true}; @@ -115,10 +115,15 @@ struct SPlayerAnimData int blendTime{250}; bool taskToBeRestoredOnAnimEnd{false}; + std::int64_t startedTick{0}; + + float progress{0.0f}; + float speed{1.0f}; + SPlayerAnimData() = default; SPlayerAnimData(const std::string& block, const std::string& anim, int time, bool loop, bool updatePos, bool interrupt, bool freeze, int blend, - bool taskRestore) + bool taskRestore, std::int64_t tick) : blockName(block), animName(anim), time(time), @@ -127,7 +132,10 @@ struct SPlayerAnimData interruptable(interrupt), freezeLastFrame(freeze), blendTime(blend), - taskToBeRestoredOnAnimEnd(taskRestore){}; + taskToBeRestoredOnAnimEnd(taskRestore), + startedTick(tick), + progress(0.0f), + speed(1.0f){}; }; class CWeapon @@ -307,6 +315,8 @@ class CPed : public CElement const SPlayerAnimData& GetAnimationData() const noexcept { return m_animData; }; void SetAnimationData(const SPlayerAnimData& animData) { m_animData = animData; }; + void SetAnimationProgress(float progress) { m_animData.progress = progress; }; + void SetAnimationSpeed(float speed) { m_animData.speed = speed; }; protected: bool ReadSpecialData(const int iLine) override; diff --git a/Server/mods/deathmatch/logic/CPedSync.cpp b/Server/mods/deathmatch/logic/CPedSync.cpp index cb2f9e417b7..b9936afb898 100644 --- a/Server/mods/deathmatch/logic/CPedSync.cpp +++ b/Server/mods/deathmatch/logic/CPedSync.cpp @@ -272,6 +272,9 @@ void CPedSync::Packet_PedSync(CPedSyncPacket& Packet) if (Data.ucFlags & 0x40) pPed->SetInWater(Data.bIsInWater); + if (Data.ucFlags & 0x80) + pPed->SetAnimationData({}); + // Is it time to sync to everyone bool bDoFarSync = llTickCountNow - pPed->GetLastFarSyncTick() >= g_TickRateSettings.iPedFarSync; diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 0674cb2b338..2e845706a0d 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -4350,7 +4350,7 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri pPed->SetChoking(false); // Store anim data - pPed->SetAnimationData({blockName, animName, iTime, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame, iBlend, bTaskToBeRestoredOnAnimEnd}); + pPed->SetAnimationData({blockName, animName, iTime, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame, iBlend, bTaskToBeRestoredOnAnimEnd, GetTickCount64_()}); BitStream.pBitStream->WriteString(blockName); BitStream.pBitStream->WriteString(animName); @@ -4370,8 +4370,8 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri // Clear anim data pPed->SetAnimationData({}); } - m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION, *BitStream.pBitStream)); + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION, *BitStream.pBitStream)); return true; } } @@ -4393,14 +4393,17 @@ bool CStaticFunctionDefinitions::SetPedAnimationProgress(CElement* pElement, con { BitStream.pBitStream->WriteString(animName); BitStream.pBitStream->Write(fProgress); + + pPed->SetAnimationProgress(fProgress); } else { // Inform them to kill the current animation instead BitStream.pBitStream->Write((unsigned char)0); + pPed->SetAnimationData({}); } - m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION_PROGRESS, *BitStream.pBitStream)); + m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION_PROGRESS, *BitStream.pBitStream)); return true; } } @@ -4421,6 +4424,7 @@ bool CStaticFunctionDefinitions::SetPedAnimationSpeed(CElement* pElement, const BitStream.pBitStream->WriteString(animName); BitStream.pBitStream->Write(fSpeed); + pPed->SetAnimationSpeed(fSpeed); m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION_SPEED, *BitStream.pBitStream)); return true; diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index 67e50af228d..b39b45702ad 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -966,6 +966,15 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const // Contains animation data? bool animRunning = !animData.blockName.empty() && !animData.animName.empty(); + + // Is animation still running? + float deltaTime = GetTickCount64_() - animData.startedTick; + if (!animData.freezeLastFrame && animData.time > 0 && deltaTime >= animData.time) + { + animRunning = false; + pPed->SetAnimationData({}); + } + BitStream.WriteBit(animRunning); if (animRunning) @@ -979,6 +988,10 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const BitStream.WriteBit(animData.freezeLastFrame); BitStream.Write(animData.blendTime); BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); + + // Write progress & speed + BitStream.Write((deltaTime / animData.time) * animData.speed); + BitStream.Write(animData.speed); } } From 7f14f9872a077e128c3d89c1f3a48995d61502ba Mon Sep 17 00:00:00 2001 From: FileEX Date: Sun, 14 Jul 2024 21:51:02 +0200 Subject: [PATCH 6/8] Update CClientPed.h --- Client/mods/deathmatch/logic/CClientPed.h | 50 ++++++++++------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index 2eca1f1d4eb..79e466ef06e 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -85,34 +85,6 @@ enum eDeathAnims DEATH_ANIM_TORSO = 20, }; - -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; - } - - bool operator!=(const SAnimationCache& other) const - { - return strName != other.strName || iTime != other.iTime || bLoop != other.bLoop || bUpdatePosition != other.bUpdatePosition || - bInterruptable != other.bInterruptable || bFreezeLastFrame != other.bFreezeLastFrame || iBlend != other.iBlend; - } -}; - struct SDelayedSyncData { unsigned long ulTime; @@ -135,7 +107,6 @@ struct SLastSyncedPedData float fRotation; bool bOnFire; bool bIsInWater; - SAnimationCache animCache; }; struct SRestoreWeaponItem @@ -154,6 +125,27 @@ struct SReplacedAnimation CAnimBlendHierarchySAInterface* pAnimationHierarchy; }; +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; + } +}; + class CClientObject; // To hide the ugly "pointer truncation from DWORD* to unsigned long warning From 7ec0b05d18fb094fb110a232fc1a45da1bc2dbb9 Mon Sep 17 00:00:00 2001 From: FileEX Date: Sun, 14 Jul 2024 21:53:53 +0200 Subject: [PATCH 7/8] Fix indention --- Client/mods/deathmatch/logic/CPacketHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index f39e4ac2d0c..5c5ccee300b 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -3975,7 +3975,7 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream) std::string blockName, animName; int time, blendTime; bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore; - float progress, speed; + float progress, speed; // Read data bitStream.ReadString(blockName); From 9ed80c8e1f8044f06653117041e2ac3780a12cca Mon Sep 17 00:00:00 2001 From: FileEX Date: Thu, 2 Jan 2025 00:47:31 +0100 Subject: [PATCH 8/8] Review --- Client/mods/deathmatch/logic/CClientPed.h | 9 ++++---- Server/mods/deathmatch/logic/CPed.h | 23 ++++--------------- Server/mods/deathmatch/logic/CPedSync.cpp | 11 +++++++++ .../logic/CStaticFunctionDefinitions.cpp | 2 +- .../logic/packets/CEntityAddPacket.cpp | 12 ++-------- Shared/sdk/net/bitstream.h | 8 +++---- 6 files changed, 27 insertions(+), 38 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index 7d87e1a193c..65df08efff7 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -552,8 +552,9 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule std::unique_ptr GetAnimAssociation(CAnimBlendHierarchySAInterface* pHierarchyInterface); - void SetHasSyncedAnim(bool synced) noexcept { m_hasSyncedAnim = synced; }; - bool HasSyncedAnim() const noexcept { return m_hasSyncedAnim; }; + void SetHasSyncedAnim(bool synced) noexcept { m_hasSyncedAnim = synced; } + bool HasSyncedAnim() const noexcept { return m_hasSyncedAnim; } + protected: // This constructor is for peds managed by a player. These are unknown to the ped manager. CClientPed(CClientManager* pManager, unsigned long ulModelID, ElementID ID, bool bIsLocalPlayer); @@ -792,6 +793,6 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule std::shared_ptr m_clientModel; - bool m_hasSyncedAnim; - bool m_animationOverridedByClient; + bool m_hasSyncedAnim{}; + bool m_animationOverridedByClient{}; }; diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index c4de0bf11c5..daa5c8e0c03 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -105,8 +105,8 @@ enum eBone struct SPlayerAnimData { - std::string blockName; - std::string animName; + std::string blockName{}; + std::string animName{}; int time{-1}; bool loop{true}; bool updatePosition{true}; @@ -120,22 +120,7 @@ struct SPlayerAnimData float progress{0.0f}; float speed{1.0f}; - SPlayerAnimData() = default; - - SPlayerAnimData(const std::string& block, const std::string& anim, int time, bool loop, bool updatePos, bool interrupt, bool freeze, int blend, - bool taskRestore, std::int64_t tick) - : blockName(block), - animName(anim), - time(time), - loop(loop), - updatePosition(updatePos), - interruptable(interrupt), - freezeLastFrame(freeze), - blendTime(blend), - taskToBeRestoredOnAnimEnd(taskRestore), - startedTick(tick), - progress(0.0f), - speed(1.0f){}; + bool IsAnimating() const noexcept { return !blockName.empty() && !animName.empty(); } }; class CWeapon @@ -356,7 +341,7 @@ class CPed : public CElement bool m_bFrozen; bool m_bStealthAiming; CVehicle* m_pJackingVehicle; - SPlayerAnimData m_animData; + SPlayerAnimData m_animData{}; CVehicle* m_pVehicle; unsigned int m_uiVehicleSeat; diff --git a/Server/mods/deathmatch/logic/CPedSync.cpp b/Server/mods/deathmatch/logic/CPedSync.cpp index b9936afb898..f911ab027c0 100644 --- a/Server/mods/deathmatch/logic/CPedSync.cpp +++ b/Server/mods/deathmatch/logic/CPedSync.cpp @@ -82,9 +82,20 @@ void CPedSync::OverrideSyncer(CPed* pPed, CPlayer* pPlayer, bool bPersist) void CPedSync::UpdateAllSyncer() { + auto currentTick = GetTickCount64_(); + // Update all the ped's sync states for (auto iter = m_pPedManager->IterBegin(); iter != m_pPedManager->IterEnd(); iter++) { + // Has the duration of the ped's animation already elapsed? + const SPlayerAnimData& animData = (*iter)->GetAnimationData(); + if (animData.IsAnimating()) + { + float deltaTime = currentTick - animData.startedTick; + if (!animData.freezeLastFrame && animData.time > 0 && deltaTime >= animData.time) + (*iter)->SetAnimationData({}); + } + // It is a ped, yet not a player if (IS_PED(*iter) && !IS_PLAYER(*iter)) UpdateSyncer(*iter); diff --git a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index dd2467da6b4..b2db8fe9e82 100644 --- a/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -4366,7 +4366,7 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri pPed->SetChoking(false); // Store anim data - pPed->SetAnimationData({blockName, animName, iTime, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame, iBlend, bTaskToBeRestoredOnAnimEnd, GetTickCount64_()}); + pPed->SetAnimationData(SPlayerAnimData{blockName, animName, iTime, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame, iBlend, bTaskToBeRestoredOnAnimEnd, GetTickCount64_()}); BitStream.pBitStream->WriteString(blockName); BitStream.pBitStream->WriteString(animName); diff --git a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp index a8a036ff6f1..6a77404f59a 100644 --- a/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp @@ -983,16 +983,7 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const const SPlayerAnimData& animData = pPed->GetAnimationData(); // Contains animation data? - bool animRunning = !animData.blockName.empty() && !animData.animName.empty(); - - // Is animation still running? - float deltaTime = GetTickCount64_() - animData.startedTick; - if (!animData.freezeLastFrame && animData.time > 0 && deltaTime >= animData.time) - { - animRunning = false; - pPed->SetAnimationData({}); - } - + bool animRunning = animData.IsAnimating(); BitStream.WriteBit(animRunning); if (animRunning) @@ -1008,6 +999,7 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const BitStream.WriteBit(animData.taskToBeRestoredOnAnimEnd); // Write progress & speed + float deltaTime = GetTickCount64_() - animData.startedTick; BitStream.Write((deltaTime / animData.time) * animData.speed); BitStream.Write(animData.speed); } diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index 0538d59972c..aa442ba1b65 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -556,10 +556,6 @@ enum class eBitStreamVersion : unsigned short // 2024-06-16 PedSync_Revision, - // Ped animations synchronization - // 2024-06-29 - AnimationsSync, - // Add "extendedwatercannons" to setWorldSpecialPropertyEnabled // 2024-06-30 WorldSpecialProperty_TunnelWeatherBlend, @@ -596,6 +592,10 @@ enum class eBitStreamVersion : unsigned short // 2024-30-12 SetElementOnFire, + // Ped animations synchronization + // 2025-01-01 + AnimationsSync, + // This allows us to automatically increment the BitStreamVersion when things are added to this enum. // Make sure you only add things above this comment. Next,