From 30c39194a36a8b059c66513b4465c8add518afee Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 11 Mar 2025 01:14:09 -0500 Subject: [PATCH] [Zone] Zone State Improvements (Continued) (#4768) * [Zone] Zone State Improvements (Continued) * Ignore a few events when we resume from suspend * Add is_zone field * Update database_update_manifest.cpp * Update database_update_manifest.cpp * Update database_update_manifest.cpp * Update zone_save_state.cpp * Update zone_save_state.cpp * Add Zone Variables * Update methods * Update zone_save_state.cpp * Update zone_save_state.cpp --------- Co-authored-by: Kinglykrab --- common/database/database_update_manifest.cpp | 14 +- .../base/base_zone_state_spawns_repository.h | 204 +++++++++--------- common/version.h | 2 +- zone/lua_zone.cpp | 54 ++++- zone/lua_zone.h | 6 + zone/perl_zone.cpp | 43 ++++ zone/quest_parser_collection.cpp | 6 + zone/zone.cpp | 67 ++++++ zone/zone.h | 9 + zone/zone_save_state.cpp | 75 ++++++- 10 files changed, 377 insertions(+), 103 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 7a33a03c7..8c3abaa55 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6941,7 +6941,7 @@ CREATE TABLE `character_pet_name` ( ManifestEntry{ .version = 9310, .description = "2025_03_7_expand_horse_def.sql", - .check = "SHOW COLUMNS FROM `horses` LIKE `helmtexture", + .check = "SHOW COLUMNS FROM `horses` LIKE 'helmtexture'", .condition = "missing", .match = "TINYINT(2)", .sql = R"( @@ -6950,6 +6950,18 @@ ALTER TABLE `horses` )", .content_schema_update = true }, + ManifestEntry{ + .version = 9311, + .description = "2025_03_09_add_zone_state_is_zone_field.sql", + .check = "SHOW COLUMNS FROM `zone_state_spawns` LIKE 'is_zone'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `zone_state_spawns` + ADD COLUMN `is_zone` tinyint(11) NULL DEFAULT 0 AFTER `is_corpse`; +)", + .content_schema_update = false + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/repositories/base/base_zone_state_spawns_repository.h b/common/repositories/base/base_zone_state_spawns_repository.h index 067d2fea8..d77e11b47 100644 --- a/common/repositories/base/base_zone_state_spawns_repository.h +++ b/common/repositories/base/base_zone_state_spawns_repository.h @@ -23,6 +23,7 @@ public: uint32_t zone_id; uint32_t instance_id; int8_t is_corpse; + int8_t is_zone; int32_t decay_in_seconds; uint32_t npc_id; uint32_t spawn2_id; @@ -61,6 +62,7 @@ public: "zone_id", "instance_id", "is_corpse", + "is_zone", "decay_in_seconds", "npc_id", "spawn2_id", @@ -95,6 +97,7 @@ public: "zone_id", "instance_id", "is_corpse", + "is_zone", "decay_in_seconds", "npc_id", "spawn2_id", @@ -163,6 +166,7 @@ public: e.zone_id = 0; e.instance_id = 0; e.is_corpse = 0; + e.is_zone = 0; e.decay_in_seconds = 0; e.npc_id = 0; e.spawn2_id = 0; @@ -227,30 +231,31 @@ public: e.zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.instance_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.is_corpse = row[3] ? static_cast(atoi(row[3])) : 0; - e.decay_in_seconds = row[4] ? static_cast(atoi(row[4])) : 0; - e.npc_id = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.spawn2_id = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.spawngroup_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.x = row[8] ? strtof(row[8], nullptr) : 0; - e.y = row[9] ? strtof(row[9], nullptr) : 0; - e.z = row[10] ? strtof(row[10], nullptr) : 0; - e.heading = row[11] ? strtof(row[11], nullptr) : 0; - e.respawn_time = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.variance = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.grid = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.current_waypoint = row[15] ? static_cast(atoi(row[15])) : 0; - e.path_when_zone_idle = row[16] ? static_cast(atoi(row[16])) : 0; - e.condition_id = row[17] ? static_cast(strtoul(row[17], nullptr, 10)) : 0; - e.condition_min_value = row[18] ? static_cast(atoi(row[18])) : 0; - e.enabled = row[19] ? static_cast(atoi(row[19])) : 1; - e.anim = row[20] ? static_cast(strtoul(row[20], nullptr, 10)) : 0; - e.loot_data = row[21] ? row[21] : ""; - e.entity_variables = row[22] ? row[22] : ""; - e.buffs = row[23] ? row[23] : ""; - e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0; - e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0; - e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0; - e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10); + e.is_zone = row[4] ? static_cast(atoi(row[4])) : 0; + e.decay_in_seconds = row[5] ? static_cast(atoi(row[5])) : 0; + e.npc_id = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.spawn2_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.spawngroup_id = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.x = row[9] ? strtof(row[9], nullptr) : 0; + e.y = row[10] ? strtof(row[10], nullptr) : 0; + e.z = row[11] ? strtof(row[11], nullptr) : 0; + e.heading = row[12] ? strtof(row[12], nullptr) : 0; + e.respawn_time = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.variance = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.grid = row[15] ? static_cast(strtoul(row[15], nullptr, 10)) : 0; + e.current_waypoint = row[16] ? static_cast(atoi(row[16])) : 0; + e.path_when_zone_idle = row[17] ? static_cast(atoi(row[17])) : 0; + e.condition_id = row[18] ? static_cast(strtoul(row[18], nullptr, 10)) : 0; + e.condition_min_value = row[19] ? static_cast(atoi(row[19])) : 0; + e.enabled = row[20] ? static_cast(atoi(row[20])) : 1; + e.anim = row[21] ? static_cast(strtoul(row[21], nullptr, 10)) : 0; + e.loot_data = row[22] ? row[22] : ""; + e.entity_variables = row[23] ? row[23] : ""; + e.buffs = row[24] ? row[24] : ""; + e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0; + e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0; + e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0; + e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10); return e; } @@ -287,30 +292,31 @@ public: v.push_back(columns[1] + " = " + std::to_string(e.zone_id)); v.push_back(columns[2] + " = " + std::to_string(e.instance_id)); v.push_back(columns[3] + " = " + std::to_string(e.is_corpse)); - v.push_back(columns[4] + " = " + std::to_string(e.decay_in_seconds)); - v.push_back(columns[5] + " = " + std::to_string(e.npc_id)); - v.push_back(columns[6] + " = " + std::to_string(e.spawn2_id)); - v.push_back(columns[7] + " = " + std::to_string(e.spawngroup_id)); - v.push_back(columns[8] + " = " + std::to_string(e.x)); - v.push_back(columns[9] + " = " + std::to_string(e.y)); - v.push_back(columns[10] + " = " + std::to_string(e.z)); - v.push_back(columns[11] + " = " + std::to_string(e.heading)); - v.push_back(columns[12] + " = " + std::to_string(e.respawn_time)); - v.push_back(columns[13] + " = " + std::to_string(e.variance)); - v.push_back(columns[14] + " = " + std::to_string(e.grid)); - v.push_back(columns[15] + " = " + std::to_string(e.current_waypoint)); - v.push_back(columns[16] + " = " + std::to_string(e.path_when_zone_idle)); - v.push_back(columns[17] + " = " + std::to_string(e.condition_id)); - v.push_back(columns[18] + " = " + std::to_string(e.condition_min_value)); - v.push_back(columns[19] + " = " + std::to_string(e.enabled)); - v.push_back(columns[20] + " = " + std::to_string(e.anim)); - v.push_back(columns[21] + " = '" + Strings::Escape(e.loot_data) + "'"); - v.push_back(columns[22] + " = '" + Strings::Escape(e.entity_variables) + "'"); - v.push_back(columns[23] + " = '" + Strings::Escape(e.buffs) + "'"); - v.push_back(columns[24] + " = " + std::to_string(e.hp)); - v.push_back(columns[25] + " = " + std::to_string(e.mana)); - v.push_back(columns[26] + " = " + std::to_string(e.endurance)); - v.push_back(columns[27] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")"); + v.push_back(columns[4] + " = " + std::to_string(e.is_zone)); + v.push_back(columns[5] + " = " + std::to_string(e.decay_in_seconds)); + v.push_back(columns[6] + " = " + std::to_string(e.npc_id)); + v.push_back(columns[7] + " = " + std::to_string(e.spawn2_id)); + v.push_back(columns[8] + " = " + std::to_string(e.spawngroup_id)); + v.push_back(columns[9] + " = " + std::to_string(e.x)); + v.push_back(columns[10] + " = " + std::to_string(e.y)); + v.push_back(columns[11] + " = " + std::to_string(e.z)); + v.push_back(columns[12] + " = " + std::to_string(e.heading)); + v.push_back(columns[13] + " = " + std::to_string(e.respawn_time)); + v.push_back(columns[14] + " = " + std::to_string(e.variance)); + v.push_back(columns[15] + " = " + std::to_string(e.grid)); + v.push_back(columns[16] + " = " + std::to_string(e.current_waypoint)); + v.push_back(columns[17] + " = " + std::to_string(e.path_when_zone_idle)); + v.push_back(columns[18] + " = " + std::to_string(e.condition_id)); + v.push_back(columns[19] + " = " + std::to_string(e.condition_min_value)); + v.push_back(columns[20] + " = " + std::to_string(e.enabled)); + v.push_back(columns[21] + " = " + std::to_string(e.anim)); + v.push_back(columns[22] + " = '" + Strings::Escape(e.loot_data) + "'"); + v.push_back(columns[23] + " = '" + Strings::Escape(e.entity_variables) + "'"); + v.push_back(columns[24] + " = '" + Strings::Escape(e.buffs) + "'"); + v.push_back(columns[25] + " = " + std::to_string(e.hp)); + v.push_back(columns[26] + " = " + std::to_string(e.mana)); + v.push_back(columns[27] + " = " + std::to_string(e.endurance)); + v.push_back(columns[28] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")"); auto results = db.QueryDatabase( fmt::format( @@ -336,6 +342,7 @@ public: v.push_back(std::to_string(e.zone_id)); v.push_back(std::to_string(e.instance_id)); v.push_back(std::to_string(e.is_corpse)); + v.push_back(std::to_string(e.is_zone)); v.push_back(std::to_string(e.decay_in_seconds)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.spawn2_id)); @@ -393,6 +400,7 @@ public: v.push_back(std::to_string(e.zone_id)); v.push_back(std::to_string(e.instance_id)); v.push_back(std::to_string(e.is_corpse)); + v.push_back(std::to_string(e.is_zone)); v.push_back(std::to_string(e.decay_in_seconds)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.spawn2_id)); @@ -454,30 +462,31 @@ public: e.zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.instance_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.is_corpse = row[3] ? static_cast(atoi(row[3])) : 0; - e.decay_in_seconds = row[4] ? static_cast(atoi(row[4])) : 0; - e.npc_id = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.spawn2_id = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.spawngroup_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.x = row[8] ? strtof(row[8], nullptr) : 0; - e.y = row[9] ? strtof(row[9], nullptr) : 0; - e.z = row[10] ? strtof(row[10], nullptr) : 0; - e.heading = row[11] ? strtof(row[11], nullptr) : 0; - e.respawn_time = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.variance = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.grid = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.current_waypoint = row[15] ? static_cast(atoi(row[15])) : 0; - e.path_when_zone_idle = row[16] ? static_cast(atoi(row[16])) : 0; - e.condition_id = row[17] ? static_cast(strtoul(row[17], nullptr, 10)) : 0; - e.condition_min_value = row[18] ? static_cast(atoi(row[18])) : 0; - e.enabled = row[19] ? static_cast(atoi(row[19])) : 1; - e.anim = row[20] ? static_cast(strtoul(row[20], nullptr, 10)) : 0; - e.loot_data = row[21] ? row[21] : ""; - e.entity_variables = row[22] ? row[22] : ""; - e.buffs = row[23] ? row[23] : ""; - e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0; - e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0; - e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0; - e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10); + e.is_zone = row[4] ? static_cast(atoi(row[4])) : 0; + e.decay_in_seconds = row[5] ? static_cast(atoi(row[5])) : 0; + e.npc_id = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.spawn2_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.spawngroup_id = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.x = row[9] ? strtof(row[9], nullptr) : 0; + e.y = row[10] ? strtof(row[10], nullptr) : 0; + e.z = row[11] ? strtof(row[11], nullptr) : 0; + e.heading = row[12] ? strtof(row[12], nullptr) : 0; + e.respawn_time = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.variance = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.grid = row[15] ? static_cast(strtoul(row[15], nullptr, 10)) : 0; + e.current_waypoint = row[16] ? static_cast(atoi(row[16])) : 0; + e.path_when_zone_idle = row[17] ? static_cast(atoi(row[17])) : 0; + e.condition_id = row[18] ? static_cast(strtoul(row[18], nullptr, 10)) : 0; + e.condition_min_value = row[19] ? static_cast(atoi(row[19])) : 0; + e.enabled = row[20] ? static_cast(atoi(row[20])) : 1; + e.anim = row[21] ? static_cast(strtoul(row[21], nullptr, 10)) : 0; + e.loot_data = row[22] ? row[22] : ""; + e.entity_variables = row[23] ? row[23] : ""; + e.buffs = row[24] ? row[24] : ""; + e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0; + e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0; + e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0; + e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10); all_entries.push_back(e); } @@ -506,30 +515,31 @@ public: e.zone_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.instance_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.is_corpse = row[3] ? static_cast(atoi(row[3])) : 0; - e.decay_in_seconds = row[4] ? static_cast(atoi(row[4])) : 0; - e.npc_id = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.spawn2_id = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.spawngroup_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.x = row[8] ? strtof(row[8], nullptr) : 0; - e.y = row[9] ? strtof(row[9], nullptr) : 0; - e.z = row[10] ? strtof(row[10], nullptr) : 0; - e.heading = row[11] ? strtof(row[11], nullptr) : 0; - e.respawn_time = row[12] ? static_cast(strtoul(row[12], nullptr, 10)) : 0; - e.variance = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.grid = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.current_waypoint = row[15] ? static_cast(atoi(row[15])) : 0; - e.path_when_zone_idle = row[16] ? static_cast(atoi(row[16])) : 0; - e.condition_id = row[17] ? static_cast(strtoul(row[17], nullptr, 10)) : 0; - e.condition_min_value = row[18] ? static_cast(atoi(row[18])) : 0; - e.enabled = row[19] ? static_cast(atoi(row[19])) : 1; - e.anim = row[20] ? static_cast(strtoul(row[20], nullptr, 10)) : 0; - e.loot_data = row[21] ? row[21] : ""; - e.entity_variables = row[22] ? row[22] : ""; - e.buffs = row[23] ? row[23] : ""; - e.hp = row[24] ? strtoll(row[24], nullptr, 10) : 0; - e.mana = row[25] ? strtoll(row[25], nullptr, 10) : 0; - e.endurance = row[26] ? strtoll(row[26], nullptr, 10) : 0; - e.created_at = strtoll(row[27] ? row[27] : "-1", nullptr, 10); + e.is_zone = row[4] ? static_cast(atoi(row[4])) : 0; + e.decay_in_seconds = row[5] ? static_cast(atoi(row[5])) : 0; + e.npc_id = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.spawn2_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.spawngroup_id = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.x = row[9] ? strtof(row[9], nullptr) : 0; + e.y = row[10] ? strtof(row[10], nullptr) : 0; + e.z = row[11] ? strtof(row[11], nullptr) : 0; + e.heading = row[12] ? strtof(row[12], nullptr) : 0; + e.respawn_time = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.variance = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.grid = row[15] ? static_cast(strtoul(row[15], nullptr, 10)) : 0; + e.current_waypoint = row[16] ? static_cast(atoi(row[16])) : 0; + e.path_when_zone_idle = row[17] ? static_cast(atoi(row[17])) : 0; + e.condition_id = row[18] ? static_cast(strtoul(row[18], nullptr, 10)) : 0; + e.condition_min_value = row[19] ? static_cast(atoi(row[19])) : 0; + e.enabled = row[20] ? static_cast(atoi(row[20])) : 1; + e.anim = row[21] ? static_cast(strtoul(row[21], nullptr, 10)) : 0; + e.loot_data = row[22] ? row[22] : ""; + e.entity_variables = row[23] ? row[23] : ""; + e.buffs = row[24] ? row[24] : ""; + e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0; + e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0; + e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0; + e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10); all_entries.push_back(e); } @@ -608,6 +618,7 @@ public: v.push_back(std::to_string(e.zone_id)); v.push_back(std::to_string(e.instance_id)); v.push_back(std::to_string(e.is_corpse)); + v.push_back(std::to_string(e.is_zone)); v.push_back(std::to_string(e.decay_in_seconds)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.spawn2_id)); @@ -658,6 +669,7 @@ public: v.push_back(std::to_string(e.zone_id)); v.push_back(std::to_string(e.instance_id)); v.push_back(std::to_string(e.is_corpse)); + v.push_back(std::to_string(e.is_zone)); v.push_back(std::to_string(e.decay_in_seconds)); v.push_back(std::to_string(e.npc_id)); v.push_back(std::to_string(e.spawn2_id)); diff --git a/common/version.h b/common/version.h index dcf2f2fcb..d12d5e129 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9310 +#define CURRENT_BINARY_DATABASE_VERSION 9311 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #endif diff --git a/zone/lua_zone.cpp b/zone/lua_zone.cpp index a0c0b8146..904e46a15 100644 --- a/zone/lua_zone.cpp +++ b/zone/lua_zone.cpp @@ -727,6 +727,52 @@ std::string Lua_Zone::GetBucketRemaining(const std::string& bucket_name) return self->GetBucketRemaining(bucket_name); } +void Lua_Zone::ClearVariables() +{ + Lua_Safe_Call_Void(); + self->ClearVariables(); +} + +bool Lua_Zone::DeleteVariable(const std::string& variable_name) +{ + Lua_Safe_Call_Bool(); + return self->DeleteVariable(variable_name); +} + +std::string Lua_Zone::GetVariable(const std::string& variable_name) +{ + Lua_Safe_Call_String(); + return self->GetVariable(variable_name); +} + +luabind::object Lua_Zone::GetVariables(lua_State* L) +{ + auto t = luabind::newtable(L); + if (d_) { + auto self = reinterpret_cast(d_); + auto l = self->GetVariables(); + int i = 1; + for (const auto& v : l) { + t[i] = v; + i++; + } + } + + return t; +} + +void Lua_Zone::SetVariable(const std::string& variable_name, const std::string& variable_value) +{ + Lua_Safe_Call_Void(); + self->SetVariable(variable_name, variable_value); +} + +bool Lua_Zone::VariableExists(const std::string& variable_name) +{ + Lua_Safe_Call_Bool(); + return self->VariableExists(variable_name); +} + luabind::scope lua_register_zone() { return luabind::class_("Zones") .def(luabind::constructor<>()) @@ -737,7 +783,9 @@ luabind::scope lua_register_zone() { .def("CanDoCombat", &Lua_Zone::CanDoCombat) .def("CanLevitate", &Lua_Zone::CanLevitate) .def("ClearSpawnTimers", &Lua_Zone::ClearSpawnTimers) + .def("ClearVariables", &Lua_Zone::ClearVariables) .def("DeleteBucket", (void(Lua_Zone::*)(const std::string&))&Lua_Zone::DeleteBucket) + .def("DeleteVariable", &Lua_Zone::DeleteVariable) .def("Depop", (void(Lua_Zone::*)(void))&Lua_Zone::Depop) .def("Depop", (void(Lua_Zone::*)(bool))&Lua_Zone::Depop) .def("Despawn", &Lua_Zone::Despawn) @@ -819,6 +867,8 @@ luabind::scope lua_register_zone() { .def("GetZoneType", &Lua_Zone::GetZoneType) .def("GetUnderworld", &Lua_Zone::GetUnderworld) .def("GetUnderworldTeleportIndex", &Lua_Zone::GetUnderworldTeleportIndex) + .def("GetVariable", &Lua_Zone::GetVariable) + .def("GetVariables", &Lua_Zone::GetVariables) .def("GetWalkSpeed", &Lua_Zone::GetWalkSpeed) .def("GetZoneZType", &Lua_Zone::GetZoneZType) .def("GetZoneTotalBlockedSpells", &Lua_Zone::GetZoneTotalBlockedSpells) @@ -849,6 +899,8 @@ luabind::scope lua_register_zone() { .def("SetInstanceTimer", &Lua_Zone::SetInstanceTimer) .def("SetInstanceTimeRemaining", &Lua_Zone::SetInstanceTimeRemaining) .def("SetIsHotzone", &Lua_Zone::SetIsHotzone) - .def("ShowZoneGlobalLoot", &Lua_Zone::ShowZoneGlobalLoot); + .def("SetVariable", &Lua_Zone::SetVariable) + .def("ShowZoneGlobalLoot", &Lua_Zone::ShowZoneGlobalLoot) + .def("VariableExists", &Lua_Zone::VariableExists); } diff --git a/zone/lua_zone.h b/zone/lua_zone.h index d069227a2..fa10fb649 100644 --- a/zone/lua_zone.h +++ b/zone/lua_zone.h @@ -141,6 +141,12 @@ public: void SetInstanceTimeRemaining(uint32 time_remaining); void SetIsHotzone(bool is_hotzone); void ShowZoneGlobalLoot(Lua_Client c); + void ClearVariables(); + bool DeleteVariable(const std::string& variable_name); + std::string GetVariable(const std::string& variable_name); + luabind::object GetVariables(lua_State* L); + void SetVariable(const std::string& variable_name, const std::string& variable_value); + bool VariableExists(const std::string& variable_name); // data buckets void SetBucket(const std::string& bucket_name, const std::string& bucket_value); diff --git a/zone/perl_zone.cpp b/zone/perl_zone.cpp index 960ea90de..497f9dff2 100644 --- a/zone/perl_zone.cpp +++ b/zone/perl_zone.cpp @@ -561,6 +561,43 @@ std::string Perl_Zone_GetBucketRemaining(Zone* self, const std::string bucket_na return self->GetBucketRemaining(bucket_name); } +void Perl_Zone_ClearVariables(Zone* self) +{ + self->ClearVariables(); +} + +bool Perl_Zone_DeleteVariable(Zone* self, const std::string variable_name) +{ + return self->DeleteVariable(variable_name); +} + +std::string Perl_Zone_GetVariable(Zone* self, const std::string variable_name) +{ + return self->GetVariable(variable_name); +} + +perl::array Perl_Zone_GetVariables(Zone* self) +{ + perl::array a; + + const auto& l = self->GetVariables(); + for (const auto& v : l) { + a.push_back(v); + } + + return a; +} + +void Perl_Zone_SetVariable(Zone* self, const std::string variable_name, const std::string variable_value) +{ + self->SetVariable(variable_name, variable_value); +} + +bool Perl_Zone_VariableExists(Zone* self, const std::string variable_name) +{ + return self->VariableExists(variable_name); +} + void perl_register_zone() { perl::interpreter perl(PERL_GET_THX); @@ -573,7 +610,9 @@ void perl_register_zone() package.add("CanDoCombat", &Perl_Zone_CanDoCombat); package.add("CanLevitate", &Perl_Zone_CanLevitate); package.add("ClearSpawnTimers", &Perl_Zone_ClearSpawnTimers); + package.add("ClearVariables", &Perl_Zone_ClearVariables); package.add("DeleteBucket", &Perl_Zone_DeleteBucket); + package.add("DeleteVariable", &Perl_Zone_DeleteVariable); package.add("Depop", (void(*)(Zone*))&Perl_Zone_Depop); package.add("Depop", (void(*)(Zone*, bool))&Perl_Zone_Depop); package.add("Despawn", &Perl_Zone_Despawn); @@ -655,6 +694,8 @@ void perl_register_zone() package.add("GetZoneType", &Perl_Zone_GetZoneType); package.add("GetUnderworld", &Perl_Zone_GetUnderworld); package.add("GetUnderworldTeleportIndex", &Perl_Zone_GetUnderworldTeleportIndex); + package.add("GetVariable", &Perl_Zone_GetVariable); + package.add("GetVariables", &Perl_Zone_GetVariables); package.add("GetWalkSpeed", &Perl_Zone_GetWalkSpeed); package.add("GetZoneZType", &Perl_Zone_GetZoneZType); package.add("GetZoneTotalBlockedSpells", &Perl_Zone_GetZoneTotalBlockedSpells); @@ -685,7 +726,9 @@ void perl_register_zone() package.add("SetInstanceTimer", &Perl_Zone_SetInstanceTimer); package.add("SetInstanceTimeRemaining", &Perl_Zone_SetInstanceTimeRemaining); package.add("SetIsHotzone", &Perl_Zone_SetIsHotzone); + package.add("SetVariable", &Perl_Zone_SetVariable); package.add("ShowZoneGlobalLoot", &Perl_Zone_ShowZoneGlobalLoot); + package.add("VariableExists", &Perl_Zone_VariableExists); } #endif //EMBPERL_XS_CLASSES diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index ba9b5745b..9ab597324 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -435,6 +435,12 @@ int QuestParserCollection::EventNPC( std::vector* extra_pointers ) { + if (npc->IsResumedFromZoneSuspend()) { + if (event_id == EVENT_DEATH_COMPLETE || event_id == EVENT_DEATH) { + return 0; + } + } + const int local_return = EventNPCLocal(event_id, npc, init, data, extra_data, extra_pointers); const int global_return = EventNPCGlobal(event_id, npc, init, data, extra_data, extra_pointers); const int default_return = DispatchEventNPC(event_id, npc, init, data, extra_data, extra_pointers); diff --git a/zone/zone.cpp b/zone/zone.cpp index a62473266..8e0cfef7e 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -3218,5 +3218,72 @@ void Zone::DisableRespawnTimers() } } +void Zone::ClearVariables() +{ + m_zone_variables.clear(); +} + +bool Zone::DeleteVariable(const std::string& variable_name) +{ + if (m_zone_variables.empty() || variable_name.empty()) { + return false; + } + + auto v = m_zone_variables.find(variable_name); + if (v == m_zone_variables.end()) { + return false; + } + + m_zone_variables.erase(v); + + return true; +} + +std::string Zone::GetVariable(const std::string& variable_name) +{ + if (m_zone_variables.empty() || variable_name.empty()) { + return std::string(); + } + + const auto& v = m_zone_variables.find(variable_name); + + return v != m_zone_variables.end() ? v->second : std::string(); +} + +std::vector Zone::GetVariables() +{ + std::vector l; + + if (m_zone_variables.empty()) { + return l; + } + + l.reserve(m_zone_variables.size()); + + for (const auto& v : m_zone_variables) { + l.emplace_back(v.first); + } + + return l; +} + +void Zone::SetVariable(const std::string& variable_name, const std::string& variable_value) +{ + if (variable_name.empty()) { + return; + } + + m_zone_variables[variable_name] = variable_value; +} + +bool Zone::VariableExists(const std::string& variable_name) +{ + if (m_zone_variables.empty() || variable_name.empty()) { + return false; + } + + return m_zone_variables.find(variable_name) != m_zone_variables.end(); +} + #include "zone_save_state.cpp" #include "zone_loot.cpp" diff --git a/zone/zone.h b/zone/zone.h index 2e2bee4b7..36e78ec8e 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -197,6 +197,13 @@ public: int32 MobsAggroCount() { return aggroedmobs; } DynamicZone *GetDynamicZone(); + void ClearVariables(); + bool DeleteVariable(const std::string& variable_name); + std::string GetVariable(const std::string& variable_name); + std::vector GetVariables(); + void SetVariable(const std::string& variable_name, const std::string& variable_value); + bool VariableExists(const std::string& variable_name); + IPathfinder *pathing; std::vector npc_emote_list; LinkedList spawn2_list; @@ -244,6 +251,8 @@ public: std::vector discovered_items; + std::map m_zone_variables; + time_t weather_timer; Timer spawn2_timer; Timer hot_reload_timer; diff --git a/zone/zone_save_state.cpp b/zone/zone_save_state.cpp index 925ec522f..729c4b3d3 100644 --- a/zone/zone_save_state.cpp +++ b/zone/zone_save_state.cpp @@ -291,6 +291,55 @@ inline void LoadNPCState(Zone *zone, NPC *n, ZoneStateSpawnsRepository::ZoneStat n->SetResumedFromZoneSuspend(true); } +inline std::string GetZoneVariablesSerialized(Zone *z) +{ + std::map variables; + + for (const auto &k: z->GetVariables()) { + variables[k] = z->GetVariable(k); + } + + try { + std::ostringstream os; + { + cereal::JSONOutputArchiveSingleLine archive(os); + archive(variables); + } + return os.str(); + } + catch (const std::exception &e) { + LogZoneState("Failed to serialize variables for zone [{}]", e.what()); + return ""; + } + + return ""; +} + +inline void LoadZoneVariables(Zone *z, const std::string &variables) +{ + if (!Strings::IsValidJson(variables)) { + LogZoneState("Invalid JSON data for zone [{}]", variables); + return; + } + + std::map deserialized_map; + try { + std::istringstream is(variables); + { + cereal::JSONInputArchive archive(is); + archive(deserialized_map); + } + } + catch (const std::exception &e) { + LogZoneState("Failed to load zone variables [{}]", e.what()); + return; + } + + for (const auto &[key, value]: deserialized_map) { + z->SetVariable(key, value); + } +} + bool Zone::LoadZoneState( std::unordered_map spawn_times, std::vector disabled_spawns @@ -315,11 +364,12 @@ bool Zone::LoadZoneState( zone->Process(); for (auto &s: spawn_states) { - if (s.spawngroup_id == 0) { + if (s.is_zone) { + LoadZoneVariables(zone, s.entity_variables); continue; } - if (s.is_corpse) { + if (s.spawngroup_id == 0 || s.is_corpse || s.is_zone) { continue; } @@ -374,7 +424,7 @@ bool Zone::LoadZoneState( // dynamic spawns, quest spawns, triggers etc. for (auto &s: spawn_states) { - if (s.spawngroup_id > 0) { + if (s.spawngroup_id > 0 || s.is_zone) { continue; } @@ -524,7 +574,13 @@ void Zone::SaveZoneState() bool ignore_npcs = n.second->GetSpawnGroupId() > 0 || n.second->GetNPCTypeID() < 100 || - n.second->HasOwner(); + n.second->GetNPCTypeID() == 500 || // Trap::CreateHiddenTrigger + n.second->IsAura() || + n.second->IsBot() || + n.second->IsMerc() || + n.second->IsTrap() || + n.second->GetSwarmOwner() || + n.second->IsPet(); if (ignore_npcs) { continue; } @@ -560,6 +616,17 @@ void Zone::SaveZoneState() spawns.emplace_back(s); } + // zone state variables + if (!GetVariables().empty()) { + ZoneStateSpawnsRepository::ZoneStateSpawns z{}; + z.zone_id = GetZoneID(); + z.instance_id = GetInstanceID(); + z.is_zone = 1; + z.entity_variables = GetZoneVariablesSerialized(this); + + spawns.emplace_back(z); + } + ZoneStateSpawnsRepository::DeleteWhere( database, fmt::format(