From dca892e2581bb2ee07655b60d77019c168cc2894 Mon Sep 17 00:00:00 2001 From: zimp-wow <66187107+zimp-wow@users.noreply.github.com> Date: Wed, 9 Apr 2025 20:55:56 -0500 Subject: [PATCH] [Bugfix] Prevent final shutdown from persisting incomplete state. (#4849) * [Bugfix] Prevent final shutdown from persisting incomplete state. * [Bugfix] Change shutdown parameters to member variable. --- zone/cli/tests/zone_state.cpp | 72 +++++++++++++++++++++++++++-------- zone/main.cpp | 1 + zone/zone.cpp | 5 ++- zone/zone.h | 4 ++ 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/zone/cli/tests/zone_state.cpp b/zone/cli/tests/zone_state.cpp index 62ada2f5f..b527f5a4f 100644 --- a/zone/cli/tests/zone_state.cpp +++ b/zone/cli/tests/zone_state.cpp @@ -38,7 +38,11 @@ inline void SetupStateZone() SetupZone("soldungb"); zone->Process(); // depop the zone controller - entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->Depop(); + auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID); + if (controller != nullptr) { + controller->Depop(); + } + entity_list.MobProcess(); // process the depop } @@ -352,8 +356,12 @@ inline void TestSpawns() npc->Death(npc, npc->GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand); } - bool condition = (int) entity_list.GetNPCList().size() == 0 && (int) entity_list.GetCorpseList().size() == 115; - RunTest("Spawns > All NPC's killed (0 NPCs) (115 Corpses)", true, condition); + bool condition = (int) entity_list.GetNPCList().size() == 0 && (int) entity_list.GetCorpseList().size() == entries; + RunTest( + fmt::format("Spawns > All NPC's killed (0 NPCs) ([{}] Corpses)", entries), + true, + condition + ); std::vector spawn2_ids = {}; @@ -377,11 +385,15 @@ inline void TestSpawns() zone->Shutdown(); SetupStateZone(); - condition = (int) entity_list.GetNPCList().size() == 0 && (int) entity_list.GetCorpseList().size() == 115; + condition = (int) entity_list.GetNPCList().size() == 0 && (int) entity_list.GetCorpseList().size() == entries; if (!condition) { PrintEntityCounts(); } - RunTest("Spawns > After restore (0 NPCs) (115 Corpses)", true, condition); + RunTest( + fmt::format("Spawns > After restore (0 NPCs) ([{}] Corpses)", entries), + true, + condition + ); for (auto &e: entity_list.GetCorpseList()) { auto c = e.second; @@ -405,11 +417,15 @@ inline void TestSpawns() zone->Process(); - condition = (int) entity_list.GetNPCList().size() == 115 && (int) entity_list.GetCorpseList().size() == 115; + condition = (int) entity_list.GetNPCList().size() == entries && (int) entity_list.GetCorpseList().size() == entries; if (!condition) { PrintEntityCounts(); } - RunTest("Spawns > After respawn (115 NPCs) (115 Corpses)", true, condition); + RunTest( + fmt::format("Spawns > After respawn ([{}] NPCs) ([{}] Corpses)", entries, entries), + true, + condition + ); for (auto &c: entity_list.GetCorpseList()) { c.second->DepopNPCCorpse(); @@ -417,11 +433,15 @@ inline void TestSpawns() entity_list.CorpseProcess(); - condition = (int) entity_list.GetNPCList().size() == 115 && (int) entity_list.GetCorpseList().size() == 0; + condition = (int) entity_list.GetNPCList().size() == entries && (int) entity_list.GetCorpseList().size() == 0; if (!condition) { PrintEntityCounts(); } - RunTest("Spawns > After respawn (115 NPCs) (0 Corpses)", true, condition); + RunTest( + fmt::format("Spawns > After respawn ([{}] NPCs) (0 Corpses)", entries), + true, + condition + ); // lets set NPC's up with a predictable loottable for testing uint32_t loottable_id = SeedLootTable(); @@ -462,20 +482,28 @@ inline void TestSpawns() npc->Death(npc, npc->GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand); } - condition = (int) entity_list.GetNPCList().size() == 105 && (int) entity_list.GetCorpseList().size() == 10; + condition = (int) entity_list.GetNPCList().size() == (entries - 10) && (int) entity_list.GetCorpseList().size() == 10; if (!condition) { PrintEntityCounts(); } - RunTest("Spawns > Kill 10 NPC's before save/restore (105 NPCs) (10 Corpses)", true, condition); + RunTest( + fmt::format("Spawns > Kill 10 NPC's before save/restore ([{}] NPCs) (10 Corpses)", (entries - 10)), + true, + condition + ); zone->Shutdown(); SetupStateZone(); - condition = (int) entity_list.GetNPCList().size() == 105 && (int) entity_list.GetCorpseList().size() == 10; + condition = (int) entity_list.GetNPCList().size() == (entries - 10) && (int) entity_list.GetCorpseList().size() == 10; if (!condition) { PrintEntityCounts(); } - RunTest("Spawns > After restore (105 NPCs) (10 Corpses)", true, condition); + RunTest( + fmt::format("Spawns > After restore ([{}] NPCs) (10 Corpses)", (entries - 10)), + true, + condition + ); // validate that all corpses and npc's have cloak of flames bool test_failed = false; @@ -572,6 +600,14 @@ inline void TestSpawns() false ); + int max_respawn = 0; + const auto& l = RespawnTimesRepository::All(database); + for (const auto& e : l) { + if (e.duration > max_respawn) { + max_respawn = e.duration; + } + } + entity_list.MobProcess(); zone->Process(); @@ -587,16 +623,20 @@ inline void TestSpawns() npc->SetEntityVariable("previously_spawned", "true"); } - Timer::RollForward(302401); // longest respawn time in zone + Timer::RollForward(max_respawn); // longest respawn time in zone zone->Process(); entity_list.MobProcess(); // processing depops - condition = (int) entity_list.GetNPCList().size() == 115 && (int) entity_list.GetCorpseList().size() == 10; + condition = (int) entity_list.GetNPCList().size() == entries && (int) entity_list.GetCorpseList().size() == 10; if (!condition) { PrintEntityCounts(); PrintZoneNpcs(); } - RunTest("Spawns > After respawn, ensure we have expected entity counts (115 NPCs) (10 Corpses)", true, condition); + RunTest( + fmt::format("Spawns > After respawn, ensure we have expected entity counts ([{}] NPCs) (10 Corpses)", entries), + true, + condition + ); entity_list.MobProcess(); // processing depops diff --git a/zone/main.cpp b/zone/main.cpp index 1697a342f..0f9abef3f 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -678,6 +678,7 @@ int main(int argc, char **argv) safe_delete(Config); if (zone != 0) { + zone->SetSaveZoneState(false); zone->Shutdown(true); } //Fix for Linux world server problem. diff --git a/zone/zone.cpp b/zone/zone.cpp index c448bf418..448373156 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -887,7 +887,7 @@ void Zone::Shutdown(bool quiet) c.second->WorldKick(); } - if (RuleB(Zone, StateSavingOnShutdown) && zone && zone->IsLoaded()) { + if (m_save_zone_state && RuleB(Zone, StateSavingOnShutdown) && zone && zone->IsLoaded()) { SaveZoneState(); } @@ -1010,6 +1010,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) SetIdleWhenEmpty(true); SetSecondsBeforeIdle(60); + SetSaveZoneState(false); if (database.GetServerType() == 1) { pvpzone = true; @@ -1204,6 +1205,8 @@ bool Zone::Init(bool is_static) { } content_db.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion()); + SetSaveZoneState(true); + database.LoadCharacterCorpses(zoneid, instanceid); content_db.LoadTraps(short_name, GetInstanceVersion()); diff --git a/zone/zone.h b/zone/zone.h index fc8d2d0d6..d7de16f9b 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -353,6 +353,9 @@ public: uint32 GetInstanceTimeRemaining() const; void SetInstanceTimeRemaining(uint32 instance_time_remaining); + inline bool GetSaveZoneState() const { return m_save_zone_state; } + inline void SetSaveZoneState(bool save_state) { m_save_zone_state = save_state; } + /** * GMSay Callback for LogSys * @@ -516,6 +519,7 @@ private: uint32 m_last_ucss_update; bool m_idle_when_empty; uint32 m_seconds_before_idle; + bool m_save_zone_state; GlobalLootManager m_global_loot; LinkedList client_auth_list;