From 50ae0f8351d0b704700dd5a95dce2ef194fd7554 Mon Sep 17 00:00:00 2001 From: Fryguy Date: Wed, 25 Jun 2025 16:21:52 -0400 Subject: [PATCH] [Corpses] Add corpse entity variables to DB (#4911) * [Corpses] Add corpse entity variables to DB - When corpses have entity variables add/remove/modified it will update the database accordingly. - Corpse loading will pull the database values if they exist. * Updates per review * Update version.h --------- Co-authored-by: Chris Miles --- common/database/database_update_manifest.cpp | 12 +++++ .../base/base_character_corpses_repository.h | 12 +++++ .../character_corpses_repository.h | 15 +++++++ common/version.h | 2 +- zone/corpse.cpp | 44 +++++++++++++++++++ zone/corpse.h | 2 + zone/zonedb.h | 1 + 7 files changed, 87 insertions(+), 1 deletion(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 5a3a8f683..05984036e 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -7121,6 +7121,18 @@ ADD COLUMN `first_login` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `xtargets`; .sql = R"( ALTER TABLE player_event_logs ROW_FORMAT=COMPRESSED; CREATE INDEX idx_event_type_char_id ON player_event_logs (event_type_id, character_id); +)", + .content_schema_update = false + }, + ManifestEntry{ + .version = 9325, + .description = "2025_06_10_character_corpses_entity_variables.sql", + .check = "SHOW COLUMNS FROM `character_corpses` LIKE 'entity_variables'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `character_corpses` +ADD COLUMN `entity_variables` TEXT DEFAULT NULL AFTER `rezzable`; )", .content_schema_update = false }, diff --git a/common/repositories/base/base_character_corpses_repository.h b/common/repositories/base/base_character_corpses_repository.h index de6aebca9..2c61799ea 100644 --- a/common/repositories/base/base_character_corpses_repository.h +++ b/common/repositories/base/base_character_corpses_repository.h @@ -70,6 +70,7 @@ public: uint32_t gm_exp; uint32_t killed_by; uint8_t rezzable; + std::string entity_variables; }; static std::string PrimaryKey() @@ -131,6 +132,7 @@ public: "gm_exp", "killed_by", "rezzable", + "entity_variables", }; } @@ -188,6 +190,7 @@ public: "gm_exp", "killed_by", "rezzable", + "entity_variables", }; } @@ -279,6 +282,7 @@ public: e.gm_exp = 0; e.killed_by = 0; e.rezzable = 0; + e.entity_variables = ""; return e; } @@ -366,6 +370,7 @@ public: e.gm_exp = row[48] ? static_cast(strtoul(row[48], nullptr, 10)) : 0; e.killed_by = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; e.rezzable = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; + e.entity_variables = row[51] ? row[51] : ""; return e; } @@ -449,6 +454,7 @@ public: v.push_back(columns[48] + " = " + std::to_string(e.gm_exp)); v.push_back(columns[49] + " = " + std::to_string(e.killed_by)); v.push_back(columns[50] + " = " + std::to_string(e.rezzable)); + v.push_back(columns[51] + " = '" + Strings::Escape(e.entity_variables) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -521,6 +527,7 @@ public: v.push_back(std::to_string(e.gm_exp)); v.push_back(std::to_string(e.killed_by)); v.push_back(std::to_string(e.rezzable)); + v.push_back("'" + Strings::Escape(e.entity_variables) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -601,6 +608,7 @@ public: v.push_back(std::to_string(e.gm_exp)); v.push_back(std::to_string(e.killed_by)); v.push_back(std::to_string(e.rezzable)); + v.push_back("'" + Strings::Escape(e.entity_variables) + "'"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -685,6 +693,7 @@ public: e.gm_exp = row[48] ? static_cast(strtoul(row[48], nullptr, 10)) : 0; e.killed_by = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; e.rezzable = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; + e.entity_variables = row[51] ? row[51] : ""; all_entries.push_back(e); } @@ -760,6 +769,7 @@ public: e.gm_exp = row[48] ? static_cast(strtoul(row[48], nullptr, 10)) : 0; e.killed_by = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; e.rezzable = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; + e.entity_variables = row[51] ? row[51] : ""; all_entries.push_back(e); } @@ -885,6 +895,7 @@ public: v.push_back(std::to_string(e.gm_exp)); v.push_back(std::to_string(e.killed_by)); v.push_back(std::to_string(e.rezzable)); + v.push_back("'" + Strings::Escape(e.entity_variables) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -958,6 +969,7 @@ public: v.push_back(std::to_string(e.gm_exp)); v.push_back(std::to_string(e.killed_by)); v.push_back(std::to_string(e.rezzable)); + v.push_back("'" + Strings::Escape(e.entity_variables) + "'"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/character_corpses_repository.h b/common/repositories/character_corpses_repository.h index 5bb95c3c3..0e60949fb 100644 --- a/common/repositories/character_corpses_repository.h +++ b/common/repositories/character_corpses_repository.h @@ -231,6 +231,21 @@ public: return UpdateOne(db, corpse); } + + static int UpdateEntityVariables(Database& db, uint32 corpse_id, const std::string& json_string) + { + auto results = db.QueryDatabase( + fmt::format( + "UPDATE `{}` SET `entity_variables` = '{}' WHERE `{}` = {}", + TableName(), + Strings::Escape(json_string), + PrimaryKey(), + corpse_id + ) + ); + + return results.Success() ? results.RowsAffected() : 0; + } }; #endif //EQEMU_CHARACTER_CORPSES_REPOSITORY_H diff --git a/common/version.h b/common/version.h index 9e0614f12..f1309b328 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 9324 +#define CURRENT_BINARY_DATABASE_VERSION 9325 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #define CUSTOM_BINARY_DATABASE_VERSION 0 diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 742c26c27..f0b04a8f5 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -32,7 +32,9 @@ #include "../common/repositories/character_corpse_items_repository.h" #include #include "queryserv.h" +#include "../common/json/json.hpp" +using json = nlohmann::json; extern EntityList entity_list; extern Zone *zone; @@ -674,6 +676,21 @@ bool Corpse::Save() ce.drakkin_tattoo = drakkin_tattoo; ce.drakkin_details = drakkin_details; + { + json j; + for (const auto& kv : m_EntityVariables) { + j[kv.first] = kv.second; + } + + if (!j.empty()) { + ce.entity_variables = j.dump(); + } else { + ce.entity_variables = "{}"; + } + + LogCorpses("Corpse entity_variables: %s", ce.entity_variables.c_str()); + } + for (auto &item: m_item_list) { CharacterCorpseItemEntry e; @@ -2428,9 +2445,36 @@ Corpse *Corpse::LoadCharacterCorpse( c->m_become_npc = false; c->m_consented_guild_id = cc.guild_consent_id; + try { + if (Strings::IsValidJson(cc.entity_variables)) { + json j = json::parse(cc.entity_variables); + for (auto& el : j.items()) { + c->SetEntityVariable(el.key(), el.value().get()); + } + } + } catch (const std::exception& ex) { + LogError("Failed to parse entity_variables JSON for corpse ID %u: %s", cc.id, ex.what()); + } + c->IsRezzed(cc.is_rezzed); c->UpdateEquipmentLight(); return c; } + +void Corpse::SyncEntityVariablesToCorpseDB() +{ + if (!m_is_player_corpse || m_corpse_db_id == 0) { + return; + } + + json j; + for (const auto& [key, value] : m_EntityVariables) { + j[key] = value; + } + + std::string serialized = j.dump(); + + CharacterCorpsesRepository::UpdateEntityVariables(database, m_corpse_db_id, serialized); +} diff --git a/zone/corpse.h b/zone/corpse.h index 7b647ed8d..65c461950 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -244,6 +244,8 @@ public: const glm::vec4 &position ); + void SyncEntityVariablesToCorpseDB(); + protected: void MoveItemToCorpse(Client *client, EQ::ItemInstance *inst, int16 equipSlot, std::list &removedList); diff --git a/zone/zonedb.h b/zone/zonedb.h index e399b74b5..cda0a5ab2 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -336,6 +336,7 @@ struct CharacterCorpseEntry uint32 drakkin_tattoo; uint32 drakkin_details; std::vector items; + std::string entity_variables; }; namespace BeastlordPetData {