From b2d9de8d965d0203c44321d9462050cab78947b9 Mon Sep 17 00:00:00 2001 From: hg <4683435+hgtw@users.noreply.github.com> Date: Sun, 31 Mar 2024 23:49:13 -0400 Subject: [PATCH] [Quests] Avoid Player and Bot quests in unloaded zone (#4232) If a mob has a target when a zone is shutdown it will crash while trying to dispatch EVENT_TARGET_CHANGE when the Mob destructor cleans up hatelists if a quest interface isn't loaded for the type (in this case no bot scripts): zone.exe!fmt::v10::format(fmt::v10::basic_format_string fmt={...}, const std::string & ={...}, const char * && =0x0000000000000000) Line 2835 C++ > zone.exe!QuestParserCollection::GetQIByBotQuest(std::string & filename={...}) Line 1138 C++ zone.exe!QuestParserCollection::BotHasQuestSubLocal(QuestEventID event_id=EVENT_TARGET_CHANGE) Line 353 C++ zone.exe!QuestParserCollection::BotHasQuestSub(QuestEventID event_id=EVENT_TARGET_CHANGE) Line 389 C++ zone.exe!Mob::SetTarget(Mob * mob=0x0000000000000000) Line 5431 C++ zone.exe!NPC::SetTarget(Mob * mob=0x0000000000000000) Line 575 C++ zone.exe!Mob::RemoveFromHateList(Mob * mob=0x000001bfbdc66040) Line 4894 C++ zone.exe!EntityList::RemoveFromTargets(Mob * mob=0x000001bfbdc66040, bool RemoveFromXTargets=true) Line 1530 C++ zone.exe!Mob::~Mob() Line 547 C++ zone.exe!NPC::~NPC() Line 537 C++ zone.exe!NPC::`scalar deleting destructor'(unsigned int) C++ zone.exe!EntityList::RemoveAllMobs() Line 2678 C++ zone.exe!EntityList::Clear() Line 3090 C++ zone.exe!Zone::~Zone() Line 1103 C++ zone.exe!Zone::`scalar deleting destructor'(unsigned int) C++ zone.exe!Zone::Shutdown(bool quiet=false) Line 928 C++ This is caused by the Zone destructor deleting short_name before calling entity_list.Clear(). With an unloaded quest interface BotHasQuestSubLocal calls GetQIByBotQuest which gets a null zone->GetShortName() and crashes while formatting strings. The immediate regressing commit for this crash is because a check for zone->IsLoaded() was removed in 74f1eac4 with others that were removed to fix a regression by #4025. GetQIByBotQuest and GetQIByPlayerQuest always had this check and should have remained for them. This restores the zone->IsLoaded() checks for GetQIByBotQuest/PlayerQuest. The other functions cannot have that check added until the other issues mentioned in #4149 are addressed. --- zone/quest_parser_collection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index 3d0b258f6..d8804a7d0 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -871,7 +871,7 @@ QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::strin QuestInterface* QuestParserCollection::GetQIByPlayerQuest(std::string& filename) { - if (!zone) { + if (!zone || !zone->IsLoaded()) { return nullptr; } @@ -1125,7 +1125,7 @@ QuestInterface* QuestParserCollection::GetQIByEncounterQuest(std::string encount QuestInterface* QuestParserCollection::GetQIByBotQuest(std::string& filename) { - if (!zone) { + if (!zone || !zone->IsLoaded()) { return nullptr; }