Skip to content

Commit

Permalink
Synchronize ped animations for new players (PR #3520, Fixes #467)
Browse files Browse the repository at this point in the history
  • Loading branch information
FileEX authored Jan 3, 2025
1 parent c0b47ea commit b32eafc
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 5 deletions.
6 changes: 6 additions & 0 deletions Client/mods/deathmatch/logic/CClientPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,9 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule

std::unique_ptr<CAnimBlendAssociation> 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);
Expand Down Expand Up @@ -789,4 +792,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule
CClientPed* m_pGettingJackedBy; // The ped that is jacking us

std::shared_ptr<CClientModel> m_clientModel;

bool m_hasSyncedAnim{};
bool m_animationOverridedByClient{};
};
33 changes: 33 additions & 0 deletions Client/mods/deathmatch/logic/CPacketHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3995,6 +3995,39 @@ void CPacketHandler::Packet_EntityAdd(NetBitStreamInterface& bitStream)
// Collisions
pPed->SetUsesCollision(bCollisonsEnabled);

// Animation
if (bitStream.Can(eBitStreamVersion::AnimationsSync))
{
// Contains animation data?
if (bitStream.ReadBit())
{
std::string blockName, animName;
int time, blendTime;
bool looped, updatePosition, interruptable, freezeLastFrame, taskRestore;
float progress, speed;

// Read data
bitStream.ReadString(blockName);
bitStream.ReadString(animName);
bitStream.Read(time);
bitStream.ReadBit(looped);
bitStream.ReadBit(updatePosition);
bitStream.ReadBit(interruptable);
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);
}
}

break;
}

Expand Down
9 changes: 9 additions & 0 deletions Client/mods/deathmatch/logic/CPedSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2248,6 +2248,10 @@ int CLuaPedDefs::SetPedAnimation(lua_State* luaVM)
}

pPed->SetTaskToBeRestoredOnAnimEnd(bTaskToBeRestoredOnAnimEnd);

if (pPed->HasSyncedAnim())
pPed->m_animationOverridedByClient = true;

lua_pushboolean(luaVM, true);
return 1;
}
Expand Down
26 changes: 26 additions & 0 deletions Server/mods/deathmatch/logic/CPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@ enum eBone
BONE_RIGHTFOOT
};

struct SPlayerAnimData
{
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};

std::int64_t startedTick{0};

float progress{0.0f};
float speed{1.0f};

bool IsAnimating() const noexcept { return !blockName.empty() && !animName.empty(); }
};

class CWeapon
{
public:
Expand Down Expand Up @@ -278,6 +298,11 @@ class CPed : public CElement
std::vector<CPlayer*>::const_iterator NearPlayersIterBegin() { return m_nearPlayersList.begin(); }
std::vector<CPlayer*>::const_iterator NearPlayersIterEnd() { return m_nearPlayersList.end(); }

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;

Expand Down Expand Up @@ -316,6 +341,7 @@ class CPed : public CElement
bool m_bFrozen;
bool m_bStealthAiming;
CVehicle* m_pJackingVehicle;
SPlayerAnimData m_animData{};

CVehicle* m_pVehicle;
unsigned int m_uiVehicleSeat;
Expand Down
14 changes: 14 additions & 0 deletions Server/mods/deathmatch/logic/CPedSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -272,6 +283,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;

Expand Down
16 changes: 12 additions & 4 deletions Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4353,8 +4353,6 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri
CPed* pPed = static_cast<CPed*>(pElement);
if (pPed->IsSpawned())
{
// TODO: save their animation?

// Tell the players
CBitStream BitStream;
if (!blockName.empty() && !animName.empty())
Expand All @@ -4367,6 +4365,9 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CElement* pElement, const SStri
if (pPed->IsChoking())
pPed->SetChoking(false);

// Store anim data
pPed->SetAnimationData(SPlayerAnimData{blockName, animName, iTime, bLoop, bUpdatePosition, bInterruptable, bFreezeLastFrame, iBlend, bTaskToBeRestoredOnAnimEnd, GetTickCount64_()});

BitStream.pBitStream->WriteString<unsigned char>(blockName);
BitStream.pBitStream->WriteString<unsigned char>(animName);
BitStream.pBitStream->Write(iTime);
Expand All @@ -4381,9 +4382,12 @@ 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));

m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, SET_PED_ANIMATION, *BitStream.pBitStream));
return true;
}
}
Expand All @@ -4405,14 +4409,17 @@ bool CStaticFunctionDefinitions::SetPedAnimationProgress(CElement* pElement, con
{
BitStream.pBitStream->WriteString<unsigned char>(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;
}
}
Expand All @@ -4433,6 +4440,7 @@ bool CStaticFunctionDefinitions::SetPedAnimationSpeed(CElement* pElement, const
BitStream.pBitStream->WriteString<unsigned char>(animName);
BitStream.pBitStream->Write(fSpeed);

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

return true;
Expand Down
28 changes: 28 additions & 0 deletions Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,34 @@ bool CEntityAddPacket::Write(NetBitStreamInterface& BitStream) const
BitStream.Write(currentWeaponSlot);
}

// Animation
if (BitStream.Can(eBitStreamVersion::AnimationsSync))
{
const SPlayerAnimData& animData = pPed->GetAnimationData();

// Contains animation data?
bool animRunning = animData.IsAnimating();
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);

// Write progress & speed
float deltaTime = GetTickCount64_() - animData.startedTick;
BitStream.Write((deltaTime / animData.time) * animData.speed);
BitStream.Write(animData.speed);
}
}

break;
}

Expand Down
6 changes: 5 additions & 1 deletion Shared/sdk/net/bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,11 @@ enum class eBitStreamVersion : unsigned short
// Add "spawnFlyingComponent" to setVehiclePanelState
// 2024-12-31
SetVehiclePanelState_SpawnFlyingComponent,


// 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,
Expand Down

0 comments on commit b32eafc

Please sign in to comment.