mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 15:57:13 +00:00
fix(destroyable): resurrect only a dead player that isn't already resurrecting
RequestResurrect resurrected whoever sent it, so a living player could reset their health and imagination, and repeated requests stacked respawns. The client's destroyable resurrects only when smashed and not resurrecting (LWODestroyableComponent::SendMessage 0x00d29e40); live, all 17 requests in the captures came after the player's Die and before its Resurrect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1391,10 +1391,12 @@ void DestroyableComponent::Resurrect(Entity& entityRef) {
|
||||
// Add a half second in case someone decides to cheat and move during the death animation
|
||||
// and just make sure the client has time to be ready.
|
||||
constexpr float respawnTime = 3.66700005531311f + 0.5f;
|
||||
if (auto* const destroyable = entity->GetComponent<DestroyableComponent>()) destroyable->m_IsResurrecting = true;
|
||||
entity->AddCallbackTimer(respawnTime, [=]() {
|
||||
GameMessages::PlayerResurrectionFinished msg;
|
||||
entity->NotifyPlayerResurrectionFinished(msg);
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent) destroyableComponent->m_IsResurrecting = false;
|
||||
|
||||
if (destroyableComponent != nullptr && entity->GetLOT() == 1) {
|
||||
destroyableComponent->SetIsDead(false);
|
||||
|
||||
@@ -471,6 +471,9 @@ public:
|
||||
*/
|
||||
static void Resurrect(Entity& entity);
|
||||
|
||||
// Whether a resurrection has started and not finished (the client's isResurrecting)
|
||||
bool GetIsResurrecting() const { return m_IsResurrecting; }
|
||||
|
||||
/**
|
||||
* Push or Pop a layer of status immunity to this entity
|
||||
*/
|
||||
@@ -634,6 +637,9 @@ private:
|
||||
*/
|
||||
bool m_IsDead;
|
||||
|
||||
// A resurrection has started (Resurrect) and its respawn animation hasn't finished
|
||||
bool m_IsResurrecting = false;
|
||||
|
||||
/**
|
||||
* Whether this entity is smashed. Unused, here for serialization
|
||||
*/
|
||||
|
||||
@@ -112,7 +112,11 @@ namespace GameMessages {
|
||||
return true;
|
||||
}
|
||||
|
||||
// LWODestroyableComponent::SendMessage (0x00d29e40): only an object that is dead and not already resurrecting is
|
||||
// resurrected (live: all 17 requests in the captures came after the player's Die and before its Resurrect)
|
||||
void RequestResurrect::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
||||
const auto* const destroyable = entity.GetComponent<DestroyableComponent>();
|
||||
if (!destroyable || !destroyable->GetIsDead() || destroyable->GetIsResurrecting()) return;
|
||||
DestroyableComponent::Resurrect(entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -718,3 +718,16 @@ TEST_F(DestroyableTest, DamageReductionsAddUp) {
|
||||
destroyableComponent->RemoveDamageReduction(10);
|
||||
EXPECT_EQ(destroyableComponent->GetDamageReduction(), 0);
|
||||
}
|
||||
|
||||
// The client resurrects only a dead object that isn't already resurrecting; a RequestResurrect from a living player
|
||||
// does nothing
|
||||
TEST_F(DestroyableTest, RequestResurrectOnlyWhenDead) {
|
||||
GameMessages::RequestResurrect request;
|
||||
destroyableComponent->SetIsDead(false);
|
||||
request.Handle(*baseEntity, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
EXPECT_FALSE(destroyableComponent->GetIsResurrecting());
|
||||
|
||||
destroyableComponent->SetIsDead(true);
|
||||
request.Handle(*baseEntity, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
EXPECT_TRUE(destroyableComponent->GetIsResurrecting());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user