From 0d218fc5c70d8ef017ec86b74441d3c66c9cc2f0 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 26 Oct 2024 20:59:15 -0700 Subject: [PATCH] fix: PetComponent crashing due to nullptr access Resolves an issue where item is null but is accessed but not doing that code and instead consulting the EntityManager for a valid Entity, alongside nullifying the m_Owner objectID should the pet be destroyed and timer still exist. Update PetComponent.cpp Add nullptr check Add back timer Update PetComponent.cpp speculative fix for a different crash Why are we accessing something before checking if its null --- dGame/dComponents/PetComponent.cpp | 35 +++++++++++++++--------------- dGame/dComponents/PetComponent.h | 2 +- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 073c09e17..2f0df1854 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -795,8 +795,6 @@ void PetComponent::Wander() { } void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { - AddDrainImaginationTimer(item, fromTaming); - m_ItemId = item->GetId(); m_DatabaseId = item->GetSubKey(); @@ -807,6 +805,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { inventoryComponent->DespawnPet(); m_Owner = inventoryComponent->GetParent()->GetObjectID(); + AddDrainImaginationTimer(fromTaming); auto* owner = GetOwner(); @@ -859,17 +858,14 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { } } -void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { +void PetComponent::AddDrainImaginationTimer(bool fromTaming) { if (Game::config->GetValue("pets_take_imagination") != "1") return; - auto playerInventory = item->GetInventory(); - if (!playerInventory) return; - - auto playerInventoryComponent = playerInventory->GetComponent(); - if (!playerInventoryComponent) return; - - auto playerEntity = playerInventoryComponent->GetParent(); - if (!playerEntity) return; + auto* playerEntity = Game::entityManager->GetEntity(m_Owner); + if (!playerEntity) { + LOG("owner was null or didnt exist!"); + return; + } auto playerDestroyableComponent = playerEntity->GetComponent(); if (!playerDestroyableComponent) return; @@ -878,12 +874,16 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (!fromTaming) playerDestroyableComponent->Imagine(-1); // Set this to a variable so when this is called back from the player the timer doesn't fire off. - m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { - if (!playerDestroyableComponent) { - LOG("No petComponent and/or no playerDestroyableComponent"); + m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [this]() { + const auto* owner = Game::entityManager->GetEntity(m_Owner); + if (!owner) { + LOG("owner was null or didnt exist!"); return; } + const auto* playerDestroyableComponent = owner->GetComponent(); + if (!playerDestroyableComponent) return; + // If we are out of imagination despawn the pet. if (playerDestroyableComponent->GetImagination() == 0) { this->Deactivate(); @@ -893,15 +893,13 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); } - this->AddDrainImaginationTimer(item); + this->AddDrainImaginationTimer(); }); } void PetComponent::Deactivate() { GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true); - GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress()); - activePets.erase(m_Owner); m_Parent->Kill(); @@ -910,6 +908,8 @@ void PetComponent::Deactivate() { if (owner == nullptr) return; + GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, owner->GetSystemAddress()); + GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress()); GameMessages::SendRegisterPetID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress()); @@ -1034,6 +1034,7 @@ Entity* PetComponent::GetParentEntity() const { } PetComponent::~PetComponent() { + m_Owner = LWOOBJID_EMPTY; } void PetComponent::SetPetNameForModeration(const std::string& petName) { diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 323330d2e..f02a9d302 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -205,7 +205,7 @@ class PetComponent final : public Component * * @param item The item that represents this pet in the inventory. */ - void AddDrainImaginationTimer(Item* item, bool fromTaming = false); + void AddDrainImaginationTimer(bool fromTaming = false); private: