Merge 01ba31908e040e3a7f625c9c49470f91a7c8ef97 into c9aedce67f9a4b1a3a2da4a192b65264e91462d4

This commit is contained in:
Yehonal 2025-11-08 18:42:49 +01:00 committed by GitHub
commit f332d369ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 347 additions and 58 deletions

View File

@ -0,0 +1,40 @@
-- Decouple item GUIDs: widen to BIGINT UNSIGNED for persistent DB IDs
-- Item instance primary key to BIGINT
ALTER TABLE `item_instance`
MODIFY COLUMN `guid` BIGINT UNSIGNED NOT NULL,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`guid`);
-- Character inventory references
ALTER TABLE `character_inventory`
MODIFY COLUMN `item` BIGINT UNSIGNED NOT NULL;
-- Mail items references
ALTER TABLE `mail_items`
MODIFY COLUMN `item_guid` BIGINT UNSIGNED NOT NULL;
-- Auctionhouse references
ALTER TABLE `auctionhouse`
MODIFY COLUMN `itemguid` BIGINT UNSIGNED NOT NULL,
DROP INDEX `item_guid`,
ADD UNIQUE KEY `item_guid` (`itemguid`);
-- Guild bank references
ALTER TABLE `guild_bank_item`
MODIFY COLUMN `item_guid` BIGINT UNSIGNED NOT NULL,
DROP INDEX `Idx_item_guid`,
ADD KEY `Idx_item_guid` (`item_guid`);
-- Refund/trade/gift tables referencing items by guid
ALTER TABLE `item_refund_instance`
MODIFY COLUMN `item_guid` BIGINT UNSIGNED NOT NULL;
ALTER TABLE `item_soulbound_trade_data`
MODIFY COLUMN `itemGuid` BIGINT UNSIGNED NOT NULL,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`itemGuid`);
ALTER TABLE `character_gifts`
MODIFY COLUMN `item_guid` BIGINT UNSIGNED NOT NULL,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`item_guid`);

View File

@ -29,6 +29,7 @@
#include "UpdateTime.h"
#include "World.h"
#include "WorldPacket.h"
#include "Entities/Item/ItemGuidMap.h"
#include <vector>
constexpr auto AH_MINIMUM_DEPOSIT = 100;
@ -580,7 +581,11 @@ void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION);
stmt->SetData(0, Id);
stmt->SetData(1, houseId);
stmt->SetData(2, item_guid.GetCounter());
{
Item* it = sAuctionMgr->GetAItem(item_guid);
uint64 dbGuid = it ? it->GetDbGuid() : static_cast<uint64>(item_guid.GetCounter());
stmt->SetData(2, dbGuid);
}
stmt->SetData(3, owner.GetCounter());
stmt->SetData (4, buyout);
stmt->SetData(5, uint32(expire_time));
@ -595,7 +600,11 @@ bool AuctionEntry::LoadFromDB(Field* fields)
{
Id = fields[0].Get<uint32>();
houseId = AuctionHouseId(fields[1].Get<uint8>());
item_guid = ObjectGuid::Create<HighGuid::Item>(fields[2].Get<uint32>());
{
uint64 dbGuid = fields[2].Get<uint64>();
uint32 low = sItemGuidMap->Acquire(dbGuid);
item_guid = ObjectGuid::Create<HighGuid::Item>(low);
}
item_template = fields[3].Get<uint32>();
itemCount = fields[4].Get<uint32>();
owner = ObjectGuid::Create<HighGuid::Player>(fields[5].Get<uint32>());

View File

@ -28,6 +28,7 @@
#include "StringConvert.h"
#include "Tokenize.h"
#include "WorldPacket.h"
#include "ItemGuidMap.h"
void AddItemsSetItem(Player* player, Item* item)
{
@ -374,7 +375,12 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans)
stmt->SetData(++index, GetUInt32Value(ITEM_FIELD_DURABILITY));
stmt->SetData(++index, GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME));
stmt->SetData(++index, m_text);
stmt->SetData(++index, guid);
if (m_dbGuid == 0)
{
m_dbGuid = sItemGuidMap->AcquireDbGuid();
sItemGuidMap->Bind(m_dbGuid, guid);
}
stmt->SetData(++index, static_cast<uint64>(m_dbGuid));
trans->Append(stmt);
@ -382,7 +388,7 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GIFT_OWNER);
stmt->SetData(0, GetOwnerGUID().GetCounter());
stmt->SetData(1, guid);
stmt->SetData(1, static_cast<uint64>(m_dbGuid));
trans->Append(stmt);
}
break;
@ -390,19 +396,21 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans)
case ITEM_REMOVED:
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->SetData(0, guid);
stmt->SetData(0, static_cast<uint64>(m_dbGuid));
trans->Append(stmt);
if (IsWrapped())
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->SetData(0, guid);
stmt->SetData(0, static_cast<uint64>(m_dbGuid));
trans->Append(stmt);
}
if (!isInTransaction)
CharacterDatabase.CommitTransaction(trans);
if (m_dbGuid)
sItemGuidMap->Release(m_dbGuid);
delete this;
return;
}
@ -514,12 +522,105 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi
return true;
}
bool Item::LoadFromDB64(uint64 dbGuid, ObjectGuid owner_guid, Field* fields, uint32 entry)
{
uint32 low = sItemGuidMap->Acquire(dbGuid);
Object::_Create(low, 0, HighGuid::Item);
m_dbGuid = dbGuid;
SetEntry(entry);
SetObjectScale(1.0f);
ItemTemplate const* proto = GetTemplate();
if (!proto)
{
LOG_ERROR("entities.item", "Invalid entry {} for item {}. Refusing to load.", GetEntry(), GetGUID().ToString());
return false;
}
if (owner_guid)
SetOwnerGUID(owner_guid);
bool need_save = false;
SetGuidValue(ITEM_FIELD_CREATOR, ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()));
SetGuidValue(ITEM_FIELD_GIFTCREATOR, ObjectGuid::Create<HighGuid::Player>(fields[1].Get<uint32>()));
SetCount( fields[2].Get<uint32>());
// Duration
uint32 duration = fields[3].Get<uint32>();
SetUInt32Value(ITEM_FIELD_DURATION, duration);
if ((proto->Duration == 0) != (duration == 0))
{
SetUInt32Value(ITEM_FIELD_DURATION, proto->Duration);
need_save = true;
}
// Charges
{
std::vector<std::string_view> tokens = Acore::Tokenize(fields[4].Get<std::string_view>(), ' ', false);
if (tokens.size() == MAX_ITEM_PROTO_SPELLS)
{
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
if (Optional<int32> charges = Acore::StringTo<int32>(tokens[i]))
SetSpellCharges(i, *charges);
else
LOG_ERROR("entities.item", "Invalid charge info '{}' for item {}, charge data not loaded.", tokens.at(i), GetGUID().ToString());
}
}
}
// Flags
SetUInt32Value(ITEM_FIELD_FLAGS, fields[5].Get<uint32>());
if (IsSoulBound() && proto->Bonding == NO_BIND && sScriptMgr->CanApplySoulboundFlag(this, proto))
{
ApplyModFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_SOULBOUND, false);
need_save = true;
}
// Enchantments
if (!_LoadIntoDataField(fields[6].Get<std::string>(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET))
{
LOG_WARN("entities.item", "Invalid enchantment data '{}' for item {}. Forcing partial load.", fields[6].Get<std::string>(), GetGUID().ToString());
}
// Random properties
SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID, fields[7].Get<int16>());
if (GetItemRandomPropertyId() < 0)
UpdateItemSuffixFactor();
// Durability
uint32 durability = fields[8].Get<uint16>();
SetUInt32Value(ITEM_FIELD_DURABILITY, durability);
SetUInt32Value(ITEM_FIELD_MAXDURABILITY, proto->MaxDurability);
if (durability > proto->MaxDurability && !IsWrapped())
{
SetUInt32Value(ITEM_FIELD_DURABILITY, proto->MaxDurability);
need_save = true;
}
SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, fields[9].Get<uint32>());
SetText(fields[10].Get<std::string>());
if (need_save)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD);
stmt->SetData(0, GetUInt32Value(ITEM_FIELD_DURATION));
stmt->SetData(1, GetUInt32Value(ITEM_FIELD_FLAGS));
stmt->SetData(2, GetUInt32Value(ITEM_FIELD_DURABILITY));
stmt->SetData(3, static_cast<uint64>(m_dbGuid));
CharacterDatabase.Execute(stmt);
}
return true;
}
/*static*/
void Item::DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
{
sScriptMgr->OnGlobalItemDelFromDB(trans, itemGuid);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->SetData(0, itemGuid);
stmt->SetData(0, static_cast<uint64>(itemGuid));
trans->Append(stmt);
}
@ -532,7 +633,7 @@ void Item::DeleteFromDB(CharacterDatabaseTransaction trans)
void Item::DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->SetData(0, itemGuid);
stmt->SetData(0, static_cast<uint64>(itemGuid));
trans->Append(stmt);
}
@ -1098,8 +1199,9 @@ Item* Item::CreateItem(uint32 item, uint32 count, Player const* player, bool clo
ASSERT_NODEBUGINFO(count != 0 && "pProto->Stackable == 0 but checked at loading already");
Item* pItem = NewItemOrBag(pProto);
if (pItem->Create(sObjectMgr->GetGenerator<HighGuid::Item>().Generate(), item, player))
{
uint32 newLow = sItemGuidMap->AcquireForNew();
if (pItem->Create(newLow, item, player))
{
pItem->SetCount(count);
if (!clone)
pItem->SetItemRandomProperties(randomPropertyId ? randomPropertyId : Item::GenerateItemRandomPropertyId(item));
@ -1174,11 +1276,11 @@ void Item::SaveRefundDataToDB()
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
stmt->SetData(0, GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(GetDbGuid()));
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_REFUND_INSTANCE);
stmt->SetData(0, GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(GetDbGuid()));
stmt->SetData(1, GetRefundRecipient());
stmt->SetData(2, GetPaidMoney());
stmt->SetData(3, uint16(GetPaidExtendedCost()));
@ -1192,7 +1294,7 @@ void Item::DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans)
if (trans)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
stmt->SetData(0, GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(GetDbGuid()));
(*trans)->Append(stmt);
}
}
@ -1270,7 +1372,7 @@ void Item::ClearSoulboundTradeable(Player* currentOwner)
allowedGUIDs.clear();
SetState(ITEM_CHANGED, currentOwner);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE);
stmt->SetData(0, GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(GetDbGuid()));
CharacterDatabase.Execute(stmt);
}

View File

@ -239,11 +239,18 @@ public:
[[nodiscard]] bool IsBoundByEnchant() const;
[[nodiscard]] bool IsBoundByTempEnchant() const;
virtual void SaveToDB(CharacterDatabaseTransaction trans);
// Legacy: Load by low-guid (pre-decoupling)
virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry);
// Decoupled: Load by persistent db-guid (64-bit), will map to a transient low-guid
virtual bool LoadFromDB64(uint64 dbGuid, ObjectGuid owner_guid, Field* fields, uint32 entry);
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
virtual void DeleteFromDB(CharacterDatabaseTransaction trans);
static void DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
void DeleteFromInventoryDB(CharacterDatabaseTransaction trans);
// Decoupled: DB GUID accessors
uint64 GetDbGuid() const { return m_dbGuid; }
void SetDbGuid(uint64 v) { m_dbGuid = v; }
void SaveRefundDataToDB();
void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans);
@ -376,5 +383,6 @@ private:
uint32 m_paidMoney;
uint32 m_paidExtendedCost;
AllowedLooterSet allowedGUIDs;
uint64 m_dbGuid = 0; // Persistent DB identifier (64-bit)
};
#endif

View File

@ -0,0 +1,59 @@
/*
* Lightweight runtime mapper for Items: DB GUID (uint64) <-> transient Low GUID (uint32)
*/
#include "ItemGuidMap.h"
ItemGuidMap* ItemGuidMap::instance()
{
// Function-local static to respect the private constructor
static ItemGuidMap s_Instance;
return &s_Instance;
}
uint32_t ItemGuidMap::NextLow()
{
// Simple monotonically increasing counter with free-list reuse
if (!_free.empty())
{
uint32_t id = _free.front();
_free.pop();
return id;
}
// wrap-around protection is not handled here; upper layers should ensure recyclable usage
return _next++;
}
uint32_t ItemGuidMap::Acquire(uint64_t dbGuid)
{
std::lock_guard<std::mutex> g(_mtx);
auto it = _dbToLow.find(dbGuid);
if (it != _dbToLow.end())
return it->second;
uint32_t low = NextLow();
_dbToLow.emplace(dbGuid, low);
return low;
}
uint32_t ItemGuidMap::AcquireForNew()
{
std::lock_guard<std::mutex> g(_mtx);
return NextLow();
}
void ItemGuidMap::Bind(uint64_t dbGuid, uint32_t lowGuid)
{
std::lock_guard<std::mutex> g(_mtx);
_dbToLow[dbGuid] = lowGuid;
}
void ItemGuidMap::Release(uint64_t dbGuid)
{
std::lock_guard<std::mutex> g(_mtx);
auto it = _dbToLow.find(dbGuid);
if (it != _dbToLow.end())
{
_free.push(it->second);
_dbToLow.erase(it);
}
}

View File

@ -0,0 +1,56 @@
/*
* Lightweight runtime mapper for Items: DB GUID (uint64) <-> transient Low GUID (uint32)
*/
#pragma once
#include <cstdint>
#include <unordered_map>
#include <queue>
#include <mutex>
#include <atomic>
class ItemGuidMap
{
public:
static ItemGuidMap* instance();
// Acquire or create a low-guid for a persistent db-guid
uint32_t Acquire(uint64_t dbGuid);
// Reserve a new low-guid for a brand new, unsaved item (dbGuid not known yet)
uint32_t AcquireForNew();
// Bind a db-guid to an already reserved low-guid (used after first save assigns dbGuid)
void Bind(uint64_t dbGuid, uint32_t lowGuid);
// Release mapping and recycle low-guid
void Release(uint64_t dbGuid);
// Initialize the starting value for persistent DB GUID generator
void InitDbGuidSeed(uint64_t start)
{
std::lock_guard<std::mutex> g(_mtx);
if (_nextDbGuid.load() == 0 || start > _nextDbGuid.load())
_nextDbGuid.store(start);
}
// Get next persistent DB GUID
uint64_t AcquireDbGuid()
{
return _nextDbGuid.fetch_add(1, std::memory_order_relaxed);
}
private:
ItemGuidMap() = default;
uint32_t NextLow();
std::unordered_map<uint64_t, uint32_t> _dbToLow;
std::queue<uint32_t> _free;
uint32_t _next{1};
std::mutex _mtx;
std::atomic<uint64_t> _nextDbGuid{0};
};
#define sItemGuidMap ItemGuidMap::instance()

View File

@ -64,6 +64,7 @@
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
#include "Entities/Item/ItemGuidMap.h"
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
// however, for some reasons removing it would cause a damn linking issue
@ -2567,7 +2568,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& dest, uint32 item, bool update
ss << ' ' << (*itr).GetCounter();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE);
stmt->SetData(0, pItem->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(pItem->GetDbGuid()));
stmt->SetData(1, ss.str());
CharacterDatabase.Execute(stmt);
}
@ -3036,7 +3037,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
if (pItem->IsWrapped())
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->SetData(0, pItem->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(pItem->GetDbGuid()));
CharacterDatabase.Execute(stmt);
}
@ -5973,20 +5974,21 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields)
{
Item* item = nullptr;
ObjectGuid::LowType itemGuid = fields[13].Get<uint32>();
uint64 itemGuidDb = fields[13].Get<uint64>();
uint32 itemEntry = fields[14].Get<uint32>();
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry))
{
bool remove = false;
item = NewItemOrBag(proto);
if (item->LoadFromDB(itemGuid, GetGUID(), fields, itemEntry))
bool ok = item->LoadFromDB64(itemGuidDb, GetGUID(), fields, itemEntry);
if (ok)
{
CharacterDatabasePreparedStatement* stmt = nullptr;
// Do not allow to have item limited to another map/zone in alive state
if (IsAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zoneId))
{
LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player ({}, name: '{}', map: {}) has item ({}, entry: {}) limited to another map ({}). Deleting item.",
LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player ({}, name: '{}', map: {}) has item ({}, entry: {}) limited to another map ({}). Deleting item.",
GetGUID().ToString(), GetName(), GetMapId(), item->GetGUID().ToString(), item->GetEntry(), zoneId);
remove = true;
}
@ -6004,7 +6006,7 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player ({}, name: '{}') has item ({}, entry: {}) with expired refund time ({}). Deleting refund data and removing refundable flag.",
GetGUID().ToString(), GetName(), item->GetGUID().ToString(), item->GetEntry(), item->GetPlayedTime());
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt);
item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE);
@ -6013,7 +6015,7 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
{
// xinef: sync query
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_REFUNDS);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
stmt->SetData(1, GetGUID().GetCounter());
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{
@ -6033,7 +6035,7 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
else if (item->IsBOPTradable())
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_BOP_TRADE);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{
@ -6083,13 +6085,15 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
else
{
LOG_ERROR("entities.player", "Player::_LoadInventory: player ({}, name: '{}') has broken item (GUID: {}, entry: {}) in inventory. Deleting item.",
GetGUID().ToString(), GetName(), itemGuid, itemEntry);
GetGUID().ToString(), GetName(), itemGuidDb, itemEntry);
remove = true;
}
// Remove item from inventory if necessary
if (remove)
{
Item::DeleteFromInventoryDB(trans, itemGuid);
CharacterDatabasePreparedStatement* delStmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
delStmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
trans->Append(delStmt);
item->FSetState(ITEM_REMOVED);
item->SaveToDB(trans); // it also deletes item object!
item = nullptr;
@ -6099,8 +6103,12 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
{
LOG_ERROR("entities.player", "Player::_LoadInventory: player ({}, name: '{}') has unknown item (entry: {}) in inventory. Deleting item.",
GetGUID().ToString(), GetName(), itemEntry);
Item::DeleteFromInventoryDB(trans, itemGuid);
Item::DeleteFromDB(trans, itemGuid);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->SetData(0, static_cast<uint64>(itemGuidDb));
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->SetData(0, static_cast<uint64>(itemGuidDb));
trans->Append(stmt);
}
return item;
}
@ -6108,19 +6116,19 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
// load mailed item which should receive current player
Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint32 mailId, Mail* mail, Field* fields)
{
ObjectGuid::LowType itemGuid = fields[11].Get<uint32>();
uint64 itemGuidDb = fields[11].Get<uint64>();
uint32 itemEntry = fields[12].Get<uint32>();
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
if (!proto)
{
LOG_ERROR("entities.player", "Player {} ({}) has unknown item in mailed items (GUID: {}, Entry: {}) in mail ({}), deleted.",
player ? player->GetName() : "<unknown>", playerGuid.ToString(), itemGuid, itemEntry, mailId);
player ? player->GetName() : "<unknown>", playerGuid.ToString(), itemGuidDb, itemEntry, mailId);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM);
stmt->SetData(0, itemGuid);
stmt->SetData(0, static_cast<uint64>(itemGuidDb));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
@ -6130,12 +6138,12 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint
Item* item = NewItemOrBag(proto);
ObjectGuid ownerGuid = fields[13].Get<uint32>() ? ObjectGuid::Create<HighGuid::Player>(fields[13].Get<uint32>()) : ObjectGuid::Empty;
if (!item->LoadFromDB(itemGuid, ownerGuid, fields, itemEntry))
if (!item->LoadFromDB64(itemGuidDb, ownerGuid, fields, itemEntry))
{
LOG_ERROR("entities.player", "Player::_LoadMailedItems: Item (GUID: {}) in mail ({}) doesn't exist, deleted from mail.", itemGuid, mailId);
LOG_ERROR("entities.player", "Player::_LoadMailedItems: Item (GUID: {}) in mail ({}) doesn't exist, deleted from mail.", itemGuidDb, mailId);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
stmt->SetData(0, itemGuid);
stmt->SetData(0, static_cast<uint64>(itemGuidDb));
CharacterDatabase.Execute(stmt);
item->FSetState(ITEM_REMOVED);
@ -6147,7 +6155,7 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint
if (mail)
{
mail->AddItem(itemGuid, itemEntry);
mail->AddItem(item->GetGUID().GetCounter(), itemEntry);
}
if (player)
@ -7271,11 +7279,11 @@ void Player::_SaveInventory(CharacterDatabaseTransaction trans)
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt);
m_items[i]->FSetState(ITEM_NEW);
@ -7369,16 +7377,23 @@ void Player::_SaveInventory(CharacterDatabaseTransaction trans)
{
case ITEM_NEW:
case ITEM_CHANGED:
// Ensure a DB GUID is assigned before writing character_inventory
if (item->GetDbGuid() == 0)
{
uint64 newDbGuid = sItemGuidMap->AcquireDbGuid();
sItemGuidMap->Bind(newDbGuid, item->GetGUID().GetCounter());
item->SetDbGuid(newDbGuid);
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_INVENTORY_ITEM);
stmt->SetData(0, lowGuid);
stmt->SetData(1, bag_guid);
stmt->SetData (2, item->GetSlot());
stmt->SetData(3, item->GetGUID().GetCounter());
stmt->SetData(3, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt);
break;
case ITEM_REMOVED:
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt);
case ITEM_UNCHANGED:
break;

View File

@ -27,6 +27,7 @@
#include "DBCStructure.h"
#include "DatabaseEnv.h"
#include "DisableMgr.h"
#include "Entities/Item/ItemGuidMap.h"
#include "GameEventMgr.h"
#include "GameObjectAIFactory.h"
#include "GameTime.h"
@ -6327,7 +6328,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
do
{
Field* fields = items->Fetch();
item.item_guid = fields[0].Get<uint32>();
item.item_guid = fields[0].Get<uint64>();
item.item_template = fields[1].Get<uint32>();
uint32 mailId = fields[2].Get<uint32>();
itemsCache[mailId].push_back(item);
@ -7195,14 +7196,11 @@ void ObjectMgr::SetHighestGuids()
GetGuidSequenceGenerator<HighGuid::Player>().Set((*result)[0].Get<uint32>() + 1);
result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
if (result)
GetGuidSequenceGenerator<HighGuid::Item>().Set((*result)[0].Get<uint32>() + 1);
// Initialize persistent item DB GUID starting point (64-bit)
// NOTE: In decoupled mode, item low GUIDs are process-local and not persisted.
if (QueryResult itemMax = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance"))
sItemGuidMap->InitDbGuidSeed((*itemMax)[0].Get<uint64>() + 1u);
// Cleanup other tables from not existed guids ( >= _hiItemGuid)
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE item >= '{}'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
CharacterDatabase.Execute("DELETE FROM mail_items WHERE item_guid >= '{}'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
CharacterDatabase.Execute("DELETE FROM auctionhouse WHERE itemguid >= '{}'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
CharacterDatabase.Execute("DELETE FROM guild_bank_item WHERE item_guid >= '{}'", GetGuidSequenceGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
if (result)

View File

@ -387,25 +387,26 @@ void Guild::BankTab::LoadFromDB(Field* fields)
bool Guild::BankTab::LoadItemFromDB(Field* fields)
{
uint8 slotId = fields[13].Get<uint8>();
ObjectGuid::LowType itemGuid = fields[14].Get<uint32>();
uint64 itemGuidDb = fields[14].Get<uint64>();
uint32 itemEntry = fields[15].Get<uint32>();
if (slotId >= GUILD_BANK_MAX_SLOTS)
{
LOG_ERROR("guild", "Invalid slot for item (GUID: {}, id: {}) in guild bank, skipped.", itemGuid, itemEntry);
LOG_ERROR("guild", "Invalid slot for item (GUID: {}, id: {}) in guild bank, skipped.", itemGuidDb, itemEntry);
return false;
}
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
if (!proto)
{
LOG_ERROR("guild", "Unknown item (GUID: {}, id: {}) in guild bank, skipped.", itemGuid, itemEntry);
LOG_ERROR("guild", "Unknown item (GUID: {}, id: {}) in guild bank, skipped.", itemGuidDb, itemEntry);
return false;
}
Item* pItem = NewItemOrBag(proto);
if (!pItem->LoadFromDB(itemGuid, ObjectGuid::Empty, fields, itemEntry))
bool ok = pItem->LoadFromDB64(itemGuidDb, ObjectGuid::Empty, fields, itemEntry);
if (!ok)
{
LOG_ERROR("guild", "Item (GUID {}, id: {}) not found in item_instance, deleting from guild bank!", itemGuid, itemEntry);
LOG_ERROR("guild", "Item (GUID {}, id: {}) not found in item_instance, deleting from guild bank!", itemGuidDb, itemEntry);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM);
stmt->SetData(0, m_guildId);

View File

@ -31,6 +31,7 @@
#include "ScriptMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Entities/Item/ItemGuidMap.h"
#define MAX_INBOX_CLIENT_CAPACITY 50
@ -73,14 +74,14 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
recvData >> subject;
recvData >> body;
// prevent client crash
if (subject.find("| |") != std::string::npos || body.find("| |") != std::string::npos)
{
return;
}
recvData >> body;
recvData >> unk1; // stationery?
recvData >> unk2; // 0x00000000
@ -712,7 +713,7 @@ void WorldSession::HandleGetMailList(WorldPacket& recvData)
for (uint8 i = 0; i < item_count; ++i)
{
Item* item = player->GetMItem(mail->items[i].item_guid);
Item* item = player->GetMItem(static_cast<ObjectGuid::LowType>(mail->items[i].item_guid));
// item index (0-6?)
data << uint8(i);
// item guid low?
@ -775,7 +776,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket& recvData)
}
Item* bodyItem = new Item; // This is not bag and then can be used new Item.
if (!bodyItem->Create(sObjectMgr->GetGenerator<HighGuid::Item>().Generate(), MAIL_BODY_ITEM_TEMPLATE, player))
if (!bodyItem->Create(sItemGuidMap->AcquireForNew(), MAIL_BODY_ITEM_TEMPLATE, player))
{
delete bodyItem;
return;

View File

@ -273,7 +273,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
if (item->IsWrapped())// wrapped?
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt)
.WithPreparedCallback(std::bind(&WorldSession::HandleOpenWrappedItemCallback, this, bagIndex, slot, item->GetGUID().GetCounter(), std::placeholders::_1)));
}
@ -318,7 +318,7 @@ void WorldSession::HandleOpenWrappedItemCallback(uint8 bagIndex, uint8 slot, Obj
GetPlayer()->SaveInventoryAndGoldToDB(trans);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->SetData(0, item->GetGUID().GetCounter());
stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);

View File

@ -246,7 +246,7 @@ void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver cons
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_MAIL_ITEM);
stmt->SetData(0, mailId);
stmt->SetData(1, mailItemIter->second->GetGUID().GetCounter());
stmt->SetData(1, static_cast<uint64>(mailItemIter->second->GetDbGuid()));
stmt->SetData(2, receiver.GetPlayerGUIDLow());
trans->Append(stmt);
}

View File

@ -158,7 +158,7 @@ private:
struct MailItemInfo
{
ObjectGuid::LowType item_guid;
uint64 item_guid;
uint32 item_template;
};
typedef std::vector<MailItemInfo> MailItemInfoVec;