Skip to content

Fix #467 Ped animations don't sync for new players #3520

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 18 commits into from
Jan 3, 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
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
Loading