From 33b40e83b7b0815effbd312bf608cda381b32aff Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:37:08 -0500 Subject: [PATCH] [Database] Consolidate Starting Items Table (#3723) * [Database] Consolidate Starting Items Table # Notes - Convert `class`, `deityId`, `race`, and `zoneid` columns to `|` separated columns. - Consolidates up to 15 rows per item down to a singular row. - Allows ease of use for operators. - Entire process is automated and creates a backup of pre-existing table. * Update shareddb.cpp * Unnecessary. --- common/database/database_update_manifest.cpp | 67 ++++++++++- .../base/base_starting_items_repository.h | 112 +++++++++--------- common/shareddb.cpp | 93 ++++++++++----- common/version.h | 2 +- 4 files changed, 184 insertions(+), 90 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 6121515a6..41fc6e338 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5019,17 +5019,72 @@ ALTER TABLE `spawn2` DROP COLUMN `enabled`; )" }, ManifestEntry{ - .version = 9242, - .description = "2023_11_7_mintime_maxtime_spawnentry.sql", - .check = "SHOW COLUMNS FROM `spawnentry` LIKE 'min_time'", - .condition = "empty", - .match = "", - .sql = R"( + .version = 9242, + .description = "2023_11_7_mintime_maxtime_spawnentry.sql", + .check = "SHOW COLUMNS FROM `spawnentry` LIKE 'min_time'", + .condition = "empty", + .match = "", + .sql = R"( ALTER TABLE `spawnentry` ADD COLUMN `min_time` smallint(4) NOT NULL DEFAULT 0 AFTER `condition_value_filter`, ADD COLUMN `max_time` smallint(4) NOT NULL DEFAULT 0 AFTER `min_time`; )" }, + ManifestEntry{ + .version = 9243, + .description = "2023_11_27_starting_items_revamp.sql", + .check = "SHOW COLUMNS FROM `starting_items` LIKE 'race_list'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `starting_items_backup_9243` LIKE `starting_items`; +INSERT INTO `starting_items_backup_9243` SELECT * FROM `starting_items`; + +CREATE TABLE `starting_items_new` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `race_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, + `class_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, + `deity_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, + `zone_id_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL, + `item_id` int(11) UNSIGNED NOT NULL DEFAULT 0, + `item_charges` tinyint(3) UNSIGNED NOT NULL DEFAULT 1, + `gm` mediumint(3) UNSIGNED NOT NULL DEFAULT 0, + `slot` mediumint(9) NOT NULL DEFAULT -1, + `min_expansion` tinyint(4) NOT NULL DEFAULT -1, + `max_expansion` tinyint(4) NOT NULL DEFAULT -1, + `content_flags` varchar(100) NULL, + `content_flags_disabled` varchar(100) NULL, + PRIMARY KEY (`id`) +); + +INSERT INTO +`starting_items_new` +( + SELECT + 0 AS `id`, + GROUP_CONCAT(DISTINCT `class` ORDER BY class ASC SEPARATOR '|') AS `class_list`, + GROUP_CONCAT(DISTINCT `race` ORDER BY race ASC SEPARATOR '|') AS `race_list`, + GROUP_CONCAT(DISTINCT `deityid` ORDER BY deityid ASC SEPARATOR '|') AS `deity_list`, + GROUP_CONCAT(DISTINCT `zoneid` ORDER BY zoneid ASC SEPARATOR '|') AS `zone_list`, + `itemid`, + `item_charges`, + `gm`, + `slot`, + `min_expansion`, + `max_expansion`, + `content_flags`, + `content_flags_disabled ` + FROM + `starting_items` + GROUP BY + `itemid` +); + +DROP TABLE `starting_items`; +RENAME TABLE `starting_items_new` TO `starting_items`; +)" + + } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/repositories/base/base_starting_items_repository.h b/common/repositories/base/base_starting_items_repository.h index 33711b462..10d62387e 100644 --- a/common/repositories/base/base_starting_items_repository.h +++ b/common/repositories/base/base_starting_items_repository.h @@ -16,17 +16,18 @@ #include "../../strings.h" #include + class BaseStartingItemsRepository { public: struct StartingItems { uint32_t id; - int32_t race; - int32_t class_; - int32_t deityid; - int32_t zoneid; - int32_t itemid; + std::string race_list; + std::string class_list; + std::string deity_list; + std::string zone_id_list; + uint32_t item_id; uint8_t item_charges; - int8_t gm; + uint8_t gm; int32_t slot; int8_t min_expansion; int8_t max_expansion; @@ -43,11 +44,11 @@ public: { return { "id", - "race", - "`class`", - "deityid", - "zoneid", - "itemid", + "race_list", + "class_list", + "deity_list", + "zone_id_list", + "item_id", "item_charges", "gm", "slot", @@ -62,11 +63,11 @@ public: { return { "id", - "race", - "`class`", - "deityid", - "zoneid", - "itemid", + "race_list", + "class_list", + "deity_list", + "zone_id_list", + "item_id", "item_charges", "gm", "slot", @@ -115,11 +116,11 @@ public: StartingItems e{}; e.id = 0; - e.race = 0; - e.class_ = 0; - e.deityid = 0; - e.zoneid = 0; - e.itemid = 0; + e.race_list = ""; + e.class_list = ""; + e.deity_list = ""; + e.zone_id_list = ""; + e.item_id = 0; e.item_charges = 1; e.gm = 0; e.slot = -1; @@ -152,8 +153,9 @@ public: { auto results = db.QueryDatabase( fmt::format( - "{} WHERE id = {} LIMIT 1", + "{} WHERE {} = {} LIMIT 1", BaseSelect(), + PrimaryKey(), starting_items_id ) ); @@ -163,13 +165,13 @@ public: StartingItems e{}; e.id = static_cast(strtoul(row[0], nullptr, 10)); - e.race = static_cast(atoi(row[1])); - e.class_ = static_cast(atoi(row[2])); - e.deityid = static_cast(atoi(row[3])); - e.zoneid = static_cast(atoi(row[4])); - e.itemid = static_cast(atoi(row[5])); + e.race_list = row[1] ? row[1] : ""; + e.class_list = row[2] ? row[2] : ""; + e.deity_list = row[3] ? row[3] : ""; + e.zone_id_list = row[4] ? row[4] : ""; + e.item_id = static_cast(strtoul(row[5], nullptr, 10)); e.item_charges = static_cast(strtoul(row[6], nullptr, 10)); - e.gm = static_cast(atoi(row[7])); + e.gm = static_cast(strtoul(row[7], nullptr, 10)); e.slot = static_cast(atoi(row[8])); e.min_expansion = static_cast(atoi(row[9])); e.max_expansion = static_cast(atoi(row[10])); @@ -208,11 +210,11 @@ public: auto columns = Columns(); - v.push_back(columns[1] + " = " + std::to_string(e.race)); - v.push_back(columns[2] + " = " + std::to_string(e.class_)); - v.push_back(columns[3] + " = " + std::to_string(e.deityid)); - v.push_back(columns[4] + " = " + std::to_string(e.zoneid)); - v.push_back(columns[5] + " = " + std::to_string(e.itemid)); + v.push_back(columns[1] + " = '" + Strings::Escape(e.race_list) + "'"); + v.push_back(columns[2] + " = '" + Strings::Escape(e.class_list) + "'"); + v.push_back(columns[3] + " = '" + Strings::Escape(e.deity_list) + "'"); + v.push_back(columns[4] + " = '" + Strings::Escape(e.zone_id_list) + "'"); + v.push_back(columns[5] + " = " + std::to_string(e.item_id)); v.push_back(columns[6] + " = " + std::to_string(e.item_charges)); v.push_back(columns[7] + " = " + std::to_string(e.gm)); v.push_back(columns[8] + " = " + std::to_string(e.slot)); @@ -242,11 +244,11 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.race)); - v.push_back(std::to_string(e.class_)); - v.push_back(std::to_string(e.deityid)); - v.push_back(std::to_string(e.zoneid)); - v.push_back(std::to_string(e.itemid)); + v.push_back("'" + Strings::Escape(e.race_list) + "'"); + v.push_back("'" + Strings::Escape(e.class_list) + "'"); + v.push_back("'" + Strings::Escape(e.deity_list) + "'"); + v.push_back("'" + Strings::Escape(e.zone_id_list) + "'"); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.item_charges)); v.push_back(std::to_string(e.gm)); v.push_back(std::to_string(e.slot)); @@ -284,11 +286,11 @@ public: std::vector v; v.push_back(std::to_string(e.id)); - v.push_back(std::to_string(e.race)); - v.push_back(std::to_string(e.class_)); - v.push_back(std::to_string(e.deityid)); - v.push_back(std::to_string(e.zoneid)); - v.push_back(std::to_string(e.itemid)); + v.push_back("'" + Strings::Escape(e.race_list) + "'"); + v.push_back("'" + Strings::Escape(e.class_list) + "'"); + v.push_back("'" + Strings::Escape(e.deity_list) + "'"); + v.push_back("'" + Strings::Escape(e.zone_id_list) + "'"); + v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.item_charges)); v.push_back(std::to_string(e.gm)); v.push_back(std::to_string(e.slot)); @@ -330,13 +332,13 @@ public: StartingItems e{}; e.id = static_cast(strtoul(row[0], nullptr, 10)); - e.race = static_cast(atoi(row[1])); - e.class_ = static_cast(atoi(row[2])); - e.deityid = static_cast(atoi(row[3])); - e.zoneid = static_cast(atoi(row[4])); - e.itemid = static_cast(atoi(row[5])); + e.race_list = row[1] ? row[1] : ""; + e.class_list = row[2] ? row[2] : ""; + e.deity_list = row[3] ? row[3] : ""; + e.zone_id_list = row[4] ? row[4] : ""; + e.item_id = static_cast(strtoul(row[5], nullptr, 10)); e.item_charges = static_cast(strtoul(row[6], nullptr, 10)); - e.gm = static_cast(atoi(row[7])); + e.gm = static_cast(strtoul(row[7], nullptr, 10)); e.slot = static_cast(atoi(row[8])); e.min_expansion = static_cast(atoi(row[9])); e.max_expansion = static_cast(atoi(row[10])); @@ -367,13 +369,13 @@ public: StartingItems e{}; e.id = static_cast(strtoul(row[0], nullptr, 10)); - e.race = static_cast(atoi(row[1])); - e.class_ = static_cast(atoi(row[2])); - e.deityid = static_cast(atoi(row[3])); - e.zoneid = static_cast(atoi(row[4])); - e.itemid = static_cast(atoi(row[5])); + e.race_list = row[1] ? row[1] : ""; + e.class_list = row[2] ? row[2] : ""; + e.deity_list = row[3] ? row[3] : ""; + e.zone_id_list = row[4] ? row[4] : ""; + e.item_id = static_cast(strtoul(row[5], nullptr, 10)); e.item_charges = static_cast(strtoul(row[6], nullptr, 10)); - e.gm = static_cast(atoi(row[7])); + e.gm = static_cast(strtoul(row[7], nullptr, 10)); e.slot = static_cast(atoi(row[8])); e.min_expansion = static_cast(atoi(row[9])); e.max_expansion = static_cast(atoi(row[10])); diff --git a/common/shareddb.cpp b/common/shareddb.cpp index a5bed3f85..0e37e3917 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -41,6 +41,7 @@ #include "repositories/criteria/content_filter_criteria.h" #include "repositories/account_repository.h" #include "repositories/faction_association_repository.h" +#include "repositories/starting_items_repository.h" #include "path_manager.h" #include "repositories/loottable_repository.h" @@ -446,45 +447,81 @@ bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { return true; } -bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, EQ::InventoryProfile* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) { +bool SharedDatabase::SetStartingItems( + PlayerProfile_Struct *pp, + EQ::InventoryProfile *inv, + uint32 si_race, + uint32 si_class, + uint32 si_deity, + uint32 si_current_zone, + char *si_name, + int admin_level +) +{ + const EQ::ItemData *item_data; - const EQ::ItemData *myitem; + const auto &l = StartingItemsRepository::All(*this); - const std::string query = StringFormat( - "SELECT itemid, item_charges, slot FROM starting_items " - "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " - "(deityid = %i or deityid = 0) AND (zoneid = %i or zoneid = 0) AND " - "gm <= %i %s ORDER BY id", - si_race, - si_class, - si_deity, - si_current_zone, - admin_level, - ContentFilterCriteria::apply().c_str() - ); - - auto results = QueryDatabase(query); - if (!results.Success()) { + if (l.empty()) { return false; } + std::vector v; - for (auto& row = results.begin(); row != results.end(); ++row) { - const int32 itemid = Strings::ToInt(row[0]); - const int32 charges = Strings::ToInt(row[1]); - int32 slot = Strings::ToInt(row[2]); - myitem = GetItem(itemid); + for (const auto &e : l) { + const auto &classes = Strings::Split(e.class_list, "|"); + const auto &deities = Strings::Split(e.deity_list, "|"); + const auto &races = Strings::Split(e.race_list, "|"); + const auto &zones = Strings::Split(e.zone_id_list, "|"); - if(!myitem) + const std::string &all = std::to_string(0); + + if (classes[0] != all) { + if (!Strings::Contains(classes, std::to_string(si_class))) { + continue; + } + } + + if (deities[0] != all) { + if (!Strings::Contains(deities, std::to_string(si_deity))) { + continue; + } + } + + if (races[0] != all) { + if (!Strings::Contains(races, std::to_string(si_race))) { + continue; + } + } + + if (zones[0] != all) { + if (!Strings::Contains(zones, std::to_string(si_current_zone))) { + continue; + } + } + + v.emplace_back(e); + } + + for (const auto &e : v) { + const uint32 item_id = e.item_id; + const uint8 item_charges = e.item_charges; + int32 slot = e.slot; + + item_data = GetItem(item_id); + + if (!item_data) { continue; + } - const EQ::ItemInstance* myinst = CreateBaseItem(myitem, charges); + const auto *inst = CreateBaseItem(item_data, item_charges); - if(slot < 0) - slot = inv->FindFreeSlot(0, 0); + if (slot < EQ::invslot::slotCharm) { + slot = inv->FindFreeSlot(false, false); + } - inv->PutItem(slot, *myinst); - safe_delete(myinst); + inv->PutItem(slot, *inst); + safe_delete(inst); } return true; diff --git a/common/version.h b/common/version.h index 5263ac503..b955d45ad 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 9242 +#define CURRENT_BINARY_DATABASE_VERSION 9243 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9040