Pass 1 - working hash generator and saving to inventory table

This commit is contained in:
Mitch Freeman 2025-03-28 21:51:10 -03:00
parent 8175ae6187
commit c5dbd1a0c5
9 changed files with 171 additions and 53 deletions

View File

@ -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
},

View File

@ -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());
}

View File

@ -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 <iomanip>
#include <sstream>
#include <chrono>
#include <random>
#include <string>
#include <functional>
#include <thread>
#include <array>
//#include "../common/light_source.h"
@ -35,6 +39,7 @@
int32 next_item_serial_number = 1;
std::unordered_set<uint64> 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) {
@ -153,6 +158,10 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy)
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;
}

View File

@ -23,6 +23,10 @@
#ifndef COMMON_ITEM_INSTANCE_H
#define COMMON_ITEM_INSTANCE_H
#include <array>
#include <random>
#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<std::string, ::Timer>& GetTimers() const { return m_timers; }
void SetTimer(std::string name, uint32 time);
@ -346,7 +357,8 @@ namespace EQ
std::map<uint8, ItemInstance*>::const_iterator _cbegin() { return m_contents.cbegin(); }
std::map<uint8, ItemInstance*>::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<std::string, std::string> m_custom_data {};
mutable std::map<std::string, ::Timer> 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<uint64_t>(rd()) << 32) | rd();
}
public:
static std::string generate()
{
std::lock_guard<std::mutex> lock(mtx);
// Get current timestamp in nanoseconds
auto now = std::chrono::high_resolution_clock::now();
auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(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::thread::id>{}(std::this_thread::get_id());
// Generate random component
std::mt19937_64 rng(seed);
std::uniform_int_distribution<size_t> dist(0, ALPHANUM_SIZE - 1);
// Create 16-byte result
std::array<char, 16> 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*/

View File

@ -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;

View File

@ -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<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.instnodrop = row[11] ? static_cast<uint8_t>(strtoul(row[11], nullptr, 10)) : 0;
e.custom_data = row[12] ? row[12] : "";
e.ornament_icon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornament_idfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_icon = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.ornament_idfile = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.ornament_hero_model = row[17] ? static_cast<int32_t>(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<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.instnodrop = row[11] ? static_cast<uint8_t>(strtoul(row[11], nullptr, 10)) : 0;
e.custom_data = row[12] ? row[12] : "";
e.ornament_icon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornament_idfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_icon = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.ornament_idfile = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.ornament_hero_model = row[17] ? static_cast<int32_t>(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<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
e.instnodrop = row[11] ? static_cast<uint8_t>(strtoul(row[11], nullptr, 10)) : 0;
e.custom_data = row[12] ? row[12] : "";
e.ornament_icon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornament_idfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_icon = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.ornament_idfile = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.ornament_hero_model = row[17] ? static_cast<int32_t>(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) + ")");
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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(