mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-29 16:07:14 +00:00
fix(combat): Die never claims a client death
Live sent client_death=false on every Die, players included (19,428 in the live captures, 452 of them player deaths); DLU sent true for players. The client only uses the flag to match a death it already predicted locally (LWODestroyableComponent::msgDie): with it set, a Die that arrives while a post-death respawn is pending is dropped. Check: die to an enemy, to falling and to a quickbuild trap; the death animation, coin loss and respawn prompt all still appear, and other players see you die. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -746,7 +746,9 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
|||||||
|
|
||||||
GameMessages::Die die;
|
GameMessages::Die die;
|
||||||
die.target = m_Parent->GetObjectID();
|
die.target = m_Parent->GetObjectID();
|
||||||
die.bClientDeath = isPlayer;
|
// Live never set client_death, players included (19,428 Die in the live captures). The client only uses it to
|
||||||
|
// match a death it already predicted locally (LWODestroyableComponent::msgDie).
|
||||||
|
die.bClientDeath = false;
|
||||||
die.bSpawnLoot = true;
|
die.bSpawnLoot = true;
|
||||||
die.deathType = deathType;
|
die.deathType = deathType;
|
||||||
die.killType = killType;
|
die.killType = killType;
|
||||||
|
|||||||
@@ -6,6 +6,32 @@
|
|||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
#include "eStateChangeType.h"
|
#include "eStateChangeType.h"
|
||||||
|
#include "Character.h"
|
||||||
|
#include "CharacterComponent.h"
|
||||||
|
#include "CombatMessages.h"
|
||||||
|
#include "PacketTestUtils.h"
|
||||||
|
#include "dCommonVars.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// The first game message with this ID among captured packets, read into T.
|
||||||
|
template<typename T>
|
||||||
|
std::optional<T> FindGameMessage(const std::vector<CapturedPacket>& packets, const MessageType::Game id) {
|
||||||
|
for (const auto& packet : packets) {
|
||||||
|
RakNet::BitStream bitStream(const_cast<unsigned char*>(packet.bytes.data()), packet.bytes.size(), false);
|
||||||
|
bitStream.IgnoreBytes(8); // CLIENT GAME_MSG header
|
||||||
|
LWOOBJID target{};
|
||||||
|
MessageType::Game messageID{};
|
||||||
|
if (!bitStream.Read(target) || !bitStream.Read(messageID) || messageID != id) continue;
|
||||||
|
T msg;
|
||||||
|
if (!msg.Deserialize(bitStream)) continue;
|
||||||
|
msg.target = target;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DestroyableTest : public GameDependenciesTest {
|
class DestroyableTest : public GameDependenciesTest {
|
||||||
protected:
|
protected:
|
||||||
@@ -555,3 +581,38 @@ TEST_F(DestroyableTest, DestroyableComponentDamageCooldownTest) {
|
|||||||
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.0f);
|
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.0f);
|
||||||
ASSERT_FALSE(destroyableComponent->IsCooldownImmune());
|
ASSERT_FALSE(destroyableComponent->IsCooldownImmune());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Live Die never sets client_death (19,428 live Die, 452 of them players).
|
||||||
|
TEST_F(DestroyableTest, DieDoesNotClaimAClientDeath) {
|
||||||
|
destroyableComponent->SetMaxArmor(0.0f);
|
||||||
|
const auto sent = PacketTestUtils::Capture([&] { destroyableComponent->Damage(UINT32_MAX, LWOOBJID_EMPTY); });
|
||||||
|
const auto die = FindGameMessage<GameMessages::Die>(sent, MessageType::Game::DIE);
|
||||||
|
ASSERT_TRUE(die.has_value());
|
||||||
|
EXPECT_EQ(die->target, baseEntity->GetObjectID());
|
||||||
|
EXPECT_FALSE(die->bClientDeath);
|
||||||
|
EXPECT_TRUE(die->bSpawnLoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DestroyableTest, PlayerDieDoesNotClaimAClientDeath) {
|
||||||
|
auto playerInfo = info;
|
||||||
|
playerInfo.lot = 1;
|
||||||
|
Entity player(0x1000000000000001LL, playerInfo);
|
||||||
|
Character character(1, nullptr);
|
||||||
|
player.SetCharacter(&character);
|
||||||
|
character.SetEntity(&player);
|
||||||
|
SystemAddress address;
|
||||||
|
address.binaryAddress = 0x0100007f;
|
||||||
|
address.port = 2003;
|
||||||
|
player.AddComponent<CharacterComponent>(-1, &character, address);
|
||||||
|
auto* destroyable = player.AddComponent<DestroyableComponent>(-1);
|
||||||
|
destroyable->SetMaxHealth(4.0f);
|
||||||
|
destroyable->SetHealth(4);
|
||||||
|
ASSERT_TRUE(player.IsPlayer());
|
||||||
|
|
||||||
|
const auto sent = PacketTestUtils::Capture([&] { destroyable->Smash(LWOOBJID_EMPTY); });
|
||||||
|
const auto die = FindGameMessage<GameMessages::Die>(sent, MessageType::Game::DIE);
|
||||||
|
ASSERT_TRUE(die.has_value());
|
||||||
|
EXPECT_EQ(die->target, player.GetObjectID());
|
||||||
|
EXPECT_FALSE(die->bClientDeath);
|
||||||
|
player.SetCharacter(nullptr);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user