Skip to content
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

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

Merged
merged 18 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 15 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
5 changes: 5 additions & 0 deletions Client/mods/deathmatch/logic/CClientPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,8 @@ 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; };
FileEX marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -788,4 +790,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;
FileEX marked this conversation as resolved.
Show resolved Hide resolved
};
33 changes: 33 additions & 0 deletions Client/mods/deathmatch/logic/CPacketHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3990,6 +3990,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 @@ -2232,6 +2232,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
41 changes: 41 additions & 0 deletions Server/mods/deathmatch/logic/CPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,41 @@ 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};

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){};
FileEX marked this conversation as resolved.
Show resolved Hide resolved
};

class CWeapon
{
public:
Expand Down Expand Up @@ -278,6 +313,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 +356,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
3 changes: 3 additions & 0 deletions Server/mods/deathmatch/logic/CPedSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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 @@ -4329,8 +4329,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 @@ -4343,6 +4341,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, GetTickCount64_()});

BitStream.pBitStream->WriteString<unsigned char>(blockName);
BitStream.pBitStream->WriteString<unsigned char>(animName);
BitStream.pBitStream->Write(iTime);
Expand All @@ -4357,9 +4358,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 @@ -4381,14 +4385,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 @@ -4409,6 +4416,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
36 changes: 36 additions & 0 deletions Server/mods/deathmatch/logic/packets/CEntityAddPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,42 @@ 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.blockName.empty() && !animData.animName.empty();
FileEX marked this conversation as resolved.
Show resolved Hide resolved

// Is animation still running?
float deltaTime = GetTickCount64_() - animData.startedTick;
if (!animData.freezeLastFrame && animData.time > 0 && deltaTime >= animData.time)
{
animRunning = false;
pPed->SetAnimationData({});
FileEX marked this conversation as resolved.
Show resolved Hide resolved
}

BitStream.WriteBit(animRunning);

if (animRunning)
{
BitStream.WriteString(animData.blockName);
FileEX marked this conversation as resolved.
Show resolved Hide resolved
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
BitStream.Write((deltaTime / animData.time) * animData.speed);
BitStream.Write(animData.speed);
}
}

break;
}

Expand Down
4 changes: 4 additions & 0 deletions Shared/sdk/net/bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,10 @@ enum class eBitStreamVersion : unsigned short
// 2024-06-16
PedSync_Revision,

// Ped animations synchronization
// 2024-06-29
AnimationsSync,

FileEX marked this conversation as resolved.
Show resolved Hide resolved
// Add "extendedwatercannons" to setWorldSpecialPropertyEnabled
// 2024-06-30
WorldSpecialProperty_TunnelWeatherBlend,
Expand Down
Loading