From a8fea95eabb4874244f2203be586e70c393d6e1f Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 8 Mar 2025 03:15:42 -0600 Subject: [PATCH] [Zone] State Save Improvements (#4765) * [Zone] State saving improvements * Make sure we load spawn enabled off of the state row * Update npc.h * Update spawn2.cpp * Update database_instances.cpp * Update database_instances.cpp --- common/database_instances.cpp | 8 +++++++- common/ruletypes.h | 2 ++ zone/npc.h | 28 ++++++++++++++++++++++++++++ zone/spawn2.cpp | 8 +++++++- zone/zone_save_state.cpp | 23 +++++++++++++++-------- 5 files changed, 59 insertions(+), 10 deletions(-) diff --git a/common/database_instances.cpp b/common/database_instances.cpp index 602ba930d..27422ed2c 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/repositories/spawn_condition_values_repository.h" #include "repositories/spawn2_disabled_repository.h" #include "repositories/data_buckets_repository.h" - +#include "repositories/zone_state_spawns_repository.h" #include "database.h" #include @@ -480,6 +480,9 @@ void Database::DeleteInstance(uint16 instance_id) DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id)); CharacterCorpsesRepository::BuryInstance(*this, instance_id); DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id)); + if (RuleB(Zone, StateSavingOnShutdown)) { + ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` = {}", instance_id)); + } } void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id) @@ -563,6 +566,9 @@ void Database::PurgeExpiredInstances() DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids)); Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids)); DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", imploded_instance_ids)); + if (RuleB(Zone, StateSavingOnShutdown)) { + ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` IN ({})", imploded_instance_ids)); + } } void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) diff --git a/common/ruletypes.h b/common/ruletypes.h index f3413e827..5010bc830 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -374,6 +374,8 @@ RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross z RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries") RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets") RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu") +RULE_BOOL(Zone, StateSaveEntityVariables, true, "Set to true if you want buffs to be saved on shutdown") +RULE_BOOL(Zone, StateSaveBuffs, true, "Set to true if you want buffs to be saved on shutdown") RULE_BOOL(Zone, StateSavingOnShutdown, true, "Set to true if you want zones to save state on shutdown (npcs, corpses, loot, entity variables, buffs etc.)") RULE_CATEGORY_END() diff --git a/zone/npc.h b/zone/npc.h index 9c9c31976..57da2830b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -608,6 +608,34 @@ public: inline void SetResumedFromZoneSuspend(bool state = true) { m_resumed_from_zone_suspend = state; } inline bool IsResumedFromZoneSuspend() { return m_resumed_from_zone_suspend; } + inline void LoadBuffsFromState(std::vector in_buffs) { + int i = 0; + for (auto &b: in_buffs) { + buffs[i].spellid = b.spellid; + buffs[i].casterlevel = b.casterlevel; + buffs[i].casterid = b.casterid; + strncpy(buffs[i].caster_name, b.caster_name, 64); + buffs[i].ticsremaining = b.ticsremaining; + buffs[i].counters = b.counters; + buffs[i].hit_number = b.hit_number; + buffs[i].melee_rune = b.melee_rune; + buffs[i].magic_rune = b.magic_rune; + buffs[i].dot_rune = b.dot_rune; + buffs[i].caston_x = b.caston_x; + buffs[i].caston_y = b.caston_y; + buffs[i].caston_z = b.caston_z; + buffs[i].ExtraDIChance = b.ExtraDIChance; + buffs[i].RootBreakChance = b.RootBreakChance; + buffs[i].instrument_mod = b.instrument_mod; + buffs[i].virus_spread_time = b.virus_spread_time; + buffs[i].persistant_buff = b.persistant_buff; + buffs[i].client = b.client; + buffs[i].UpdateClient = b.UpdateClient; + i++; + } + CalcBonuses(); + } + protected: void HandleRoambox(); diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 5ff22e1c6..86e7f2647 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -198,7 +198,13 @@ bool Spawn2::Process() { } //have the spawn group pick an NPC for us - uint32 npcid = currentnpcid && currentnpcid > 0 ? currentnpcid : spawn_group->GetNPCType(condition_value); + uint32 npcid = 0; + if (RuleB(Zone, StateSavingOnShutdown) && currentnpcid && currentnpcid > 0) { + npcid = currentnpcid; + } else { + npcid = spawn_group->GetNPCType(condition_value); + } + if (npcid == 0) { LogSpawns("Spawn2 [{}]: Spawn group [{}] did not yeild an NPC! not spawning", spawn2_id, spawngroup_id_); diff --git a/zone/zone_save_state.cpp b/zone/zone_save_state.cpp index 99c796ef5..925ec522f 100644 --- a/zone/zone_save_state.cpp +++ b/zone/zone_save_state.cpp @@ -171,6 +171,10 @@ inline std::string GetLootSerialized(Corpse *c) inline void LoadNPCEntityVariables(NPC *n, const std::string &entity_variables) { + if (!RuleB(Zone, StateSaveEntityVariables)) { + return; + } + if (!Strings::IsValidJson(entity_variables)) { LogZoneState("Invalid JSON data for NPC [{}]", n->GetNPCTypeID()); return; @@ -196,6 +200,10 @@ inline void LoadNPCEntityVariables(NPC *n, const std::string &entity_variables) inline void LoadNPCBuffs(NPC *n, const std::string &buffs) { + if (!RuleB(Zone, StateSaveBuffs)) { + return; + } + if (!Strings::IsValidJson(buffs)) { LogZoneState("Invalid JSON data for NPC [{}]", n->GetNPCTypeID()); return; @@ -214,10 +222,7 @@ inline void LoadNPCBuffs(NPC *n, const std::string &buffs) return; } - for (const auto &b: valid_buffs) { - // int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1, bool disable_buff_overwrite = false); - n->AddBuff(n, b.spellid, b.ticsremaining, b.casterlevel, false); - } + n->LoadBuffsFromState(valid_buffs); } inline std::vector GetLootdropIds(const std::vector &spawn_states) @@ -225,7 +230,8 @@ inline std::vector GetLootdropIds(const std::vector lootdrop_ids; - for (auto &s: spawn_states) { + + for (auto &s: spawn_states) { if (s.loot_data.empty()) { continue; } @@ -347,7 +353,7 @@ bool Zone::LoadZoneState( (bool) s.path_when_zone_idle, s.condition_id, s.condition_min_value, - spawn_enabled, + (s.enabled && spawn_enabled), (EmuAppearance) s.anim ); @@ -415,7 +421,8 @@ inline void SaveNPCState(NPC *n, ZoneStateSpawnsRepository::ZoneStateSpawns &s) { // entity variables std::map variables; - for (const auto &k: n->GetEntityVariables()) { + + for (const auto &k: n->GetEntityVariables()) { variables[k] = n->GetEntityVariable(k); } @@ -503,7 +510,7 @@ void Zone::SaveZoneState() s.created_at = std::time(nullptr); auto n = sp->GetNPC(); - if (n) { + if (n && entity_list.GetNPCByID(n->GetID())) { SaveNPCState(n, s); }