From c5dbd1a0c5fc33a0bd93e4e184e3cd08fed59d77 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Fri, 28 Mar 2025 21:51:10 -0300 Subject: [PATCH] Pass 1 - working hash generator and saving to inventory table --- common/database/database_update_manifest.cpp | 14 ++++ common/inventory_profile.cpp | 3 + common/item_instance.cpp | 39 +++++++--- common/item_instance.h | 60 +++++++++++++++- common/patches/rof2.cpp | 8 ++- .../base/base_inventory_repository.h | 72 ++++++++++++------- common/shareddb.cpp | 22 +++--- world/client.cpp | 2 +- zone/gm_commands/show/inventory.cpp | 4 +- 9 files changed, 171 insertions(+), 53 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 15dc4a27f..226127b2e 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -7183,6 +7183,20 @@ ALTER TABLE `character_parcels` ALTER TABLE `character_parcels_containers` ADD COLUMN `evolve_amount` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `quantity`; +)", + .content_schema_update = false + }, + ManifestEntry{ + .version = 9329, + .description = "2025_03_27_implement_item_unique_serial_number.sql", + .check = "SHOW COLUMNS FROM `inventory` LIKE 'serial_number'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `inventory` + DROP COLUMN `guid`, + ADD COLUMN `serial_number` VARCHAR(16) NULL DEFAULT NULL AFTER `ornament_hero_model`, + ADD UNIQUE INDEX `idx_serial_number` (`serial_number`); )", .content_schema_update = false }, diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 7017184d9..bbc69c1a7 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -252,6 +252,9 @@ int16 EQ::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst) return slot_id; } + // if (inst.GetSerialNumber2().empty()) { + // inst.GenerateUniqueSerialNumber(); + // } // Delegate to internal method return _PutItem(slot_id, inst.Clone()); } diff --git a/common/item_instance.cpp b/common/item_instance.cpp index bb048b1fb..c9d48e75c 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -18,14 +18,18 @@ #include "inventory_profile.h" #include "../common/data_verification.h" -//#include "classes.h" -//#include "global_define.h" -//#include "item_instance.h" -//#include "races.h" #include "rulesys.h" #include "shareddb.h" #include "strings.h" #include "evolving_items.h" +#include +#include +#include +#include +#include +#include +#include +#include //#include "../common/light_source.h" @@ -35,6 +39,7 @@ int32 next_item_serial_number = 1; std::unordered_set guids{}; +std::mutex EQ::UniqueHashGenerator::mtx; static inline int32 GetNextItemInstSerialNumber() { @@ -65,8 +70,8 @@ static inline int32 GetNextItemInstSerialNumber() // // class EQ::ItemInstance // -EQ::ItemInstance::ItemInstance(const ItemData* item, int16 charges) { - +EQ::ItemInstance::ItemInstance(const ItemData* item, int16 charges) +{ if (item) { m_item = new ItemData(*item); } @@ -84,8 +89,8 @@ EQ::ItemInstance::ItemInstance(const ItemData* item, int16 charges) { m_SerialNumber = GetNextItemInstSerialNumber(); } -EQ::ItemInstance::ItemInstance(SharedDatabase *db, uint32 item_id, int16 charges) { - +EQ::ItemInstance::ItemInstance(SharedDatabase *db, uint32 item_id, int16 charges) +{ m_item = db->GetItem(item_id); if (m_item) { @@ -149,9 +154,13 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy) m_custom_data[iter->first] = iter->second; } - m_SerialNumber = copy.m_SerialNumber; - m_custom_data = copy.m_custom_data; - m_timers = copy.m_timers; + m_SerialNumber = copy.m_SerialNumber; + m_custom_data = copy.m_custom_data; + m_timers = copy.m_timers; + + if (GetSerialNumber2().empty()) { + CreateSerialNumber2(); + } m_exp = copy.m_exp; m_evolveLvl = copy.m_evolveLvl; @@ -2001,3 +2010,11 @@ void EQ::ItemInstance::SetEvolveEquipped(const bool in) const GetTimers().at("evolve").Disable(); } + +std::string EQ::ItemInstance::GenerateUniqueSerialNumber() +{ + std::string unique_hash = UniqueHashGenerator::generate(); + + LogInventoryDetail("Generated an item serial number {}", unique_hash); + return unique_hash; +} \ No newline at end of file diff --git a/common/item_instance.h b/common/item_instance.h index 99edfb570..1f9b12ceb 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -23,6 +23,10 @@ #ifndef COMMON_ITEM_INSTANCE_H #define COMMON_ITEM_INSTANCE_H +#include +#include + + #include "evolving_items.h" @@ -228,8 +232,15 @@ namespace EQ std::string Serialize(int16 slot_id) const { InternalSerializedItem_Struct s; s.slot_id = slot_id; s.inst = (const void*)this; std::string ser; ser.assign((char*)&s, sizeof(InternalSerializedItem_Struct)); return ser; } void Serialize(OutBuffer& ob, int16 slot_id) const { InternalSerializedItem_Struct isi; isi.slot_id = slot_id; isi.inst = (const void*)this; ob.write((const char*)&isi, sizeof(isi)); } - inline int32 GetSerialNumber() const { return m_SerialNumber; } - inline void SetSerialNumber(int32 id) { m_SerialNumber = id; } + int32 GetSerialNumber() const { return m_SerialNumber; } + void SetSerialNumber(int32 id) { m_SerialNumber = id; } + const std::string &GetSerialNumber2() const { return m_serial_number2; } + void SetSerialNumber2(std::string sn) { m_serial_number2 = std::move(sn); } + + void CreateSerialNumber2() + { + m_serial_number2 = GenerateUniqueSerialNumber(); + } std::map& GetTimers() const { return m_timers; } void SetTimer(std::string name, uint32 time); @@ -346,7 +357,8 @@ namespace EQ std::map::const_iterator _cbegin() { return m_contents.cbegin(); } std::map::const_iterator _cend() { return m_contents.cend(); } - void _PutItem(uint8 index, ItemInstance* inst) { m_contents[index] = inst; } + void _PutItem(uint8 index, ItemInstance *inst) { m_contents[index] = inst; } + static std::string GenerateUniqueSerialNumber(); ItemInstTypes m_use_type{ItemInstNormal};// Usage type for item const ItemData * m_item{nullptr}; // Ptr to item data @@ -358,6 +370,7 @@ namespace EQ bool m_attuned{false}; int32 m_merchantcount{1};//number avaliable on the merchant, -1=unlimited int32 m_SerialNumber{0}; // Unique identifier for this instance of an item. Needed for Bazaar. + std::string m_serial_number2{}; // unique serial number across all zones/world TESTING March 2025 uint32 m_exp{0}; int8 m_evolveLvl{0}; ItemData * m_scaledItem{nullptr}; @@ -375,5 +388,46 @@ namespace EQ std::map m_custom_data {}; mutable std::map m_timers {}; }; + + class UniqueHashGenerator + { + private: + static constexpr char ALPHANUM[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + static constexpr size_t ALPHANUM_SIZE = sizeof(ALPHANUM) - 1; + static std::mutex mtx; + + // Generate machine-specific seed + static uint64_t getMachineSeed() + { + std::random_device rd; + return (static_cast(rd()) << 32) | rd(); + } + + public: + static std::string generate() + { + std::lock_guard lock(mtx); + + // Get current timestamp in nanoseconds + auto now = std::chrono::high_resolution_clock::now(); + auto nanos = std::chrono::duration_cast(now.time_since_epoch()).count(); + + // Combine timestamp with machine-specific data + static const uint64_t machine_seed = getMachineSeed(); + uint64_t seed = nanos ^ machine_seed ^ std::hash{}(std::this_thread::get_id()); + + // Generate random component + std::mt19937_64 rng(seed); + std::uniform_int_distribution dist(0, ALPHANUM_SIZE - 1); + + // Create 16-byte result + std::array result; + for (int i = 0; i < 16; ++i) { + result[i] = ALPHANUM[dist(rng)]; + } + + return std::string(result.begin(), result.end()); + } + }; } #endif /*COMMON_ITEM_INSTANCE_H*/ diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index b250a5de6..f0b7786f6 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -6446,7 +6446,13 @@ namespace RoF2 RoF2::structs::ItemSerializationHeader hdr; //sprintf(hdr.unknown000, "06e0002Y1W00"); - strn0cpy(hdr.unknown000, fmt::format("{:016}\0", inst->GetSerialNumber()).c_str(),sizeof(hdr.unknown000)); + // strn0cpy(hdr.unknown000, fmt::format("{:016}\0", inst->GetSerialNumber()).c_str(),sizeof(hdr.unknown000)); + strn0cpy( + hdr.unknown000, + inst->GetSerialNumber2().empty() ? "0000000000000000" : inst->GetSerialNumber2().c_str(), + sizeof(hdr.unknown000) + ); + hdr.unknown000[16] = '\0'; hdr.stacksize = 1; diff --git a/common/repositories/base/base_inventory_repository.h b/common/repositories/base/base_inventory_repository.h index 9a4b5122b..975fec324 100644 --- a/common/repositories/base/base_inventory_repository.h +++ b/common/repositories/base/base_inventory_repository.h @@ -32,10 +32,12 @@ public: uint32_t augment_six; uint8_t instnodrop; std::string custom_data; + uint32_t ornamenticon; + uint32_t ornamentidfile; uint32_t ornament_icon; uint32_t ornament_idfile; int32_t ornament_hero_model; - uint64_t guid; + std::string serial_number; }; static std::string PrimaryKey() @@ -59,10 +61,12 @@ public: "augment_six", "instnodrop", "custom_data", + "ornamenticon", + "ornamentidfile", "ornament_icon", "ornament_idfile", "ornament_hero_model", - "guid", + "serial_number", }; } @@ -82,10 +86,12 @@ public: "augment_six", "instnodrop", "custom_data", + "ornamenticon", + "ornamentidfile", "ornament_icon", "ornament_idfile", "ornament_hero_model", - "guid", + "serial_number", }; } @@ -139,10 +145,12 @@ public: e.augment_six = 0; e.instnodrop = 0; e.custom_data = ""; + e.ornamenticon = 0; + e.ornamentidfile = 0; e.ornament_icon = 0; e.ornament_idfile = 0; e.ornament_hero_model = 0; - e.guid = 0; + e.serial_number = ""; return e; } @@ -192,10 +200,12 @@ public: e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; - e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; + e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[15] ? static_cast(strtoul(row[15], nullptr, 10)) : 0; + e.ornament_idfile = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; + e.ornament_hero_model = row[17] ? static_cast(atoi(row[17])) : 0; + e.serial_number = row[18] ? row[18] : ""; return e; } @@ -242,10 +252,12 @@ public: v.push_back(columns[10] + " = " + std::to_string(e.augment_six)); v.push_back(columns[11] + " = " + std::to_string(e.instnodrop)); v.push_back(columns[12] + " = '" + Strings::Escape(e.custom_data) + "'"); - v.push_back(columns[13] + " = " + std::to_string(e.ornament_icon)); - v.push_back(columns[14] + " = " + std::to_string(e.ornament_idfile)); - v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model)); - v.push_back(columns[16] + " = " + std::to_string(e.guid)); + v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon)); + v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile)); + v.push_back(columns[15] + " = " + std::to_string(e.ornament_icon)); + v.push_back(columns[16] + " = " + std::to_string(e.ornament_idfile)); + v.push_back(columns[17] + " = " + std::to_string(e.ornament_hero_model)); + v.push_back(columns[18] + " = '" + Strings::Escape(e.serial_number) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -280,10 +292,12 @@ public: v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornamenticon)); + v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornament_icon)); v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); - v.push_back(std::to_string(e.guid)); + v.push_back("'" + Strings::Escape(e.serial_number) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -326,10 +340,12 @@ public: v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornamenticon)); + v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornament_icon)); v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); - v.push_back(std::to_string(e.guid)); + v.push_back("'" + Strings::Escape(e.serial_number) + "'"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -376,10 +392,12 @@ public: e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; - e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; + e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[15] ? static_cast(strtoul(row[15], nullptr, 10)) : 0; + e.ornament_idfile = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; + e.ornament_hero_model = row[17] ? static_cast(atoi(row[17])) : 0; + e.serial_number = row[18] ? row[18] : ""; all_entries.push_back(e); } @@ -417,10 +435,12 @@ public: e.augment_six = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; e.instnodrop = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; e.custom_data = row[12] ? row[12] : ""; - e.ornament_icon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; - e.ornament_idfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; - e.ornament_hero_model = row[15] ? static_cast(atoi(row[15])) : 0; - e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0; + e.ornamenticon = row[13] ? static_cast(strtoul(row[13], nullptr, 10)) : 0; + e.ornamentidfile = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; + e.ornament_icon = row[15] ? static_cast(strtoul(row[15], nullptr, 10)) : 0; + e.ornament_idfile = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; + e.ornament_hero_model = row[17] ? static_cast(atoi(row[17])) : 0; + e.serial_number = row[18] ? row[18] : ""; all_entries.push_back(e); } @@ -508,10 +528,12 @@ public: v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornamenticon)); + v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornament_icon)); v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); - v.push_back(std::to_string(e.guid)); + v.push_back("'" + Strings::Escape(e.serial_number) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -547,10 +569,12 @@ public: v.push_back(std::to_string(e.augment_six)); v.push_back(std::to_string(e.instnodrop)); v.push_back("'" + Strings::Escape(e.custom_data) + "'"); + v.push_back(std::to_string(e.ornamenticon)); + v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornament_icon)); v.push_back(std::to_string(e.ornament_idfile)); v.push_back(std::to_string(e.ornament_hero_model)); - v.push_back(std::to_string(e.guid)); + v.push_back("'" + Strings::Escape(e.serial_number) + "'"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 0ef0cbe4e..5e52c2a5f 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -285,7 +285,7 @@ bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance* e.ornament_icon = inst->GetOrnamentationIcon(); e.ornament_idfile = inst->GetOrnamentationIDFile(); e.ornament_hero_model = inst->GetOrnamentHeroModel(); - e.guid = inst->GetSerialNumber(); + e.serial_number = inst->GetSerialNumber2(); const int replaced = InventoryRepository::ReplaceOne(*this, e); @@ -640,12 +640,6 @@ bool SharedDatabase::GetInventory(Client *c) return false; } - for (auto const& row: results) { - if (row.guid != 0) { - EQ::ItemInstance::AddGUIDToMap(row.guid); - } - } - const auto timestamps = GetItemRecastTimestamps(char_id); auto cv_conflict = false; const auto pmask = inv.GetLookup()->PossessionsBitmask; @@ -723,6 +717,15 @@ bool SharedDatabase::GetInventory(Client *c) inst->SetOrnamentationIDFile(ornament_idfile); inst->SetOrnamentHeroModel(item->HerosForgeModel); + if (row.serial_number.empty()) { + inst->CreateSerialNumber2(); + row.serial_number = inst->GetSerialNumber2(); + queue.push_back(row); + } + else { + inst->SetSerialNumber2(row.serial_number); + } + if ( instnodrop || ( @@ -818,8 +821,7 @@ bool SharedDatabase::GetInventory(Client *c) put_slot_id = inv.PutItem(slot_id, *inst); } - row.guid = inst->GetSerialNumber(); - queue.push_back(row); + //queue.push_back(row); safe_delete(inst); @@ -849,8 +851,6 @@ bool SharedDatabase::GetInventory(Client *c) InventoryRepository::ReplaceMany(*this, queue); } - EQ::ItemInstance::ClearGUIDMap(); - // Retrieve shared inventory return GetSharedBank(char_id, &inv, true); } diff --git a/world/client.cpp b/world/client.cpp index a2d35a485..94746bee7 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -2400,7 +2400,7 @@ bool Client::StoreCharacter( e.ornament_icon = inst->GetOrnamentationIcon(); e.ornament_idfile = inst->GetOrnamentationIDFile(); e.ornament_hero_model = inst->GetOrnamentHeroModel(); - e.guid = inst->GetSerialNumber(); + e.serial_number = inst->GetSerialNumber2(); v.emplace_back(e); } diff --git a/zone/gm_commands/show/inventory.cpp b/zone/gm_commands/show/inventory.cpp index 2a3e2a377..655f796df 100644 --- a/zone/gm_commands/show/inventory.cpp +++ b/zone/gm_commands/show/inventory.cpp @@ -185,7 +185,7 @@ void ShowInventory(Client *c, const Seperator *sep) scope_bit & peekWorld ? EQ::invslot::WORLD_BEGIN + index_main : index_main, linker.GenerateLink(), item_data->ID, - inst_main->GetSerialNumber(), + inst_main->GetSerialNumber2().c_str(), inst_main->IsStackable() && inst_main->GetCharges() > 0 ? fmt::format( " (Stack of {})", @@ -254,7 +254,7 @@ void ShowInventory(Client *c, const Seperator *sep) sub_index, linker.GenerateLink(), item_data->ID, - inst_sub->GetSerialNumber(), + inst_sub->GetSerialNumber2().c_str(), ( inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ? fmt::format(