Compare commits

...

11 Commits

Author SHA1 Message Date
Yehonal
57136ea87a
Merge 01ba31908e040e3a7f625c9c49470f91a7c8ef97 into 611a85529d719e3078d8e1e4061c0c52c5ec4a7a 2025-11-09 16:08:35 +01:00
github-actions[bot]
611a85529d chore(DB): import pending files
Referenced commit(s): 040e7a0a4d690532a0bd240a515f0519173d9e4a
2025-11-09 13:15:20 +00:00
Andrew
040e7a0a4d
fix(DB/Creature): Despawn all instances of Superior Healing Ward (#23584) 2025-11-09 10:14:19 -03:00
github-actions[bot]
d4cd580ddc chore(DB): import pending files
Referenced commit(s): 37833c66e69733c68c4244c2a2070b5c2421f0a8
2025-11-09 08:50:22 +00:00
Andrew
37833c66e6
fix(DB/Creature): Remove xp from Reclamation mobs (#23579) 2025-11-09 05:49:21 -03:00
github-actions[bot]
ec274182a2 chore(DB): import pending files
Referenced commit(s): d9b2e775e3266f4b592ca09eefd99b6f212d1861
2025-11-09 07:58:11 +00:00
Andrew
d9b2e775e3
fix(DB/Creature): Fix Sorlof visibility distance (#23573) 2025-11-09 04:57:04 -03:00
killerwife
c85c86b285
Remove double unroot in Unit::_ExitVehicle (#23545) 2025-11-08 19:40:30 -03:00
sogladev
125e1aec9d
fix(Scripts/AzjolNerub): update Azjol-Nerub's Anub'arak (#23570) 2025-11-08 17:27:56 -03:00
Yehonal
01ba31908e
Merge branch 'master' into feat/item-instance-64-bit 2025-09-03 15:30:24 +02:00
Yehonal
18958dfb26 feat(DB): decouple item GUIDs to BIGINT UNSIGNED for future-proof database integrity 2025-09-03 11:59:25 +02:00
18 changed files with 363 additions and 72 deletions

View File

@ -0,0 +1,3 @@
-- DB update 2025_11_08_02 -> 2025_11_09_00
--
UPDATE `creature_addon` SET `visibilityDistanceType` = 3 WHERE `guid` = 103278;

View File

@ -0,0 +1,3 @@
-- DB update 2025_11_09_00 -> 2025_11_09_01
--
UPDATE `creature_template` SET `flags_extra` = `flags_extra`|64 WHERE `entry` IN (28220, 28218, 28242, 28103, 28212, 28207, 28170);

View File

@ -0,0 +1,3 @@
-- DB update 2025_11_09_01 -> 2025_11_09_02
--
DELETE FROM `creature` WHERE `id1` = 10218;

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 "UpdateTime.h"
#include "World.h" #include "World.h"
#include "WorldPacket.h" #include "WorldPacket.h"
#include "Entities/Item/ItemGuidMap.h"
#include <vector> #include <vector>
constexpr auto AH_MINIMUM_DEPOSIT = 100; constexpr auto AH_MINIMUM_DEPOSIT = 100;
@ -580,7 +581,11 @@ void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION);
stmt->SetData(0, Id); stmt->SetData(0, Id);
stmt->SetData(1, houseId); 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(3, owner.GetCounter());
stmt->SetData (4, buyout); stmt->SetData (4, buyout);
stmt->SetData(5, uint32(expire_time)); stmt->SetData(5, uint32(expire_time));
@ -595,7 +600,11 @@ bool AuctionEntry::LoadFromDB(Field* fields)
{ {
Id = fields[0].Get<uint32>(); Id = fields[0].Get<uint32>();
houseId = AuctionHouseId(fields[1].Get<uint8>()); 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>(); item_template = fields[3].Get<uint32>();
itemCount = fields[4].Get<uint32>(); itemCount = fields[4].Get<uint32>();
owner = ObjectGuid::Create<HighGuid::Player>(fields[5].Get<uint32>()); owner = ObjectGuid::Create<HighGuid::Player>(fields[5].Get<uint32>());

View File

@ -28,6 +28,7 @@
#include "StringConvert.h" #include "StringConvert.h"
#include "Tokenize.h" #include "Tokenize.h"
#include "WorldPacket.h" #include "WorldPacket.h"
#include "ItemGuidMap.h"
void AddItemsSetItem(Player* player, Item* item) 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_DURABILITY));
stmt->SetData(++index, GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)); stmt->SetData(++index, GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME));
stmt->SetData(++index, m_text); 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); trans->Append(stmt);
@ -382,7 +388,7 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans)
{ {
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GIFT_OWNER); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GIFT_OWNER);
stmt->SetData(0, GetOwnerGUID().GetCounter()); stmt->SetData(0, GetOwnerGUID().GetCounter());
stmt->SetData(1, guid); stmt->SetData(1, static_cast<uint64>(m_dbGuid));
trans->Append(stmt); trans->Append(stmt);
} }
break; break;
@ -390,19 +396,21 @@ void Item::SaveToDB(CharacterDatabaseTransaction trans)
case ITEM_REMOVED: case ITEM_REMOVED:
{ {
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->SetData(0, guid); stmt->SetData(0, static_cast<uint64>(m_dbGuid));
trans->Append(stmt); trans->Append(stmt);
if (IsWrapped()) if (IsWrapped())
{ {
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->SetData(0, guid); stmt->SetData(0, static_cast<uint64>(m_dbGuid));
trans->Append(stmt); trans->Append(stmt);
} }
if (!isInTransaction) if (!isInTransaction)
CharacterDatabase.CommitTransaction(trans); CharacterDatabase.CommitTransaction(trans);
if (m_dbGuid)
sItemGuidMap->Release(m_dbGuid);
delete this; delete this;
return; return;
} }
@ -514,12 +522,105 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi
return true; 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*/ /*static*/
void Item::DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid) void Item::DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
{ {
sScriptMgr->OnGlobalItemDelFromDB(trans, itemGuid); sScriptMgr->OnGlobalItemDelFromDB(trans, itemGuid);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->SetData(0, itemGuid); stmt->SetData(0, static_cast<uint64>(itemGuid));
trans->Append(stmt); trans->Append(stmt);
} }
@ -532,7 +633,7 @@ void Item::DeleteFromDB(CharacterDatabaseTransaction trans)
void Item::DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid) void Item::DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
{ {
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->SetData(0, itemGuid); stmt->SetData(0, static_cast<uint64>(itemGuid));
trans->Append(stmt); 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"); ASSERT_NODEBUGINFO(count != 0 && "pProto->Stackable == 0 but checked at loading already");
Item* pItem = NewItemOrBag(pProto); 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); pItem->SetCount(count);
if (!clone) if (!clone)
pItem->SetItemRandomProperties(randomPropertyId ? randomPropertyId : Item::GenerateItemRandomPropertyId(item)); pItem->SetItemRandomProperties(randomPropertyId ? randomPropertyId : Item::GenerateItemRandomPropertyId(item));
@ -1174,11 +1276,11 @@ void Item::SaveRefundDataToDB()
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
stmt->SetData(0, GetGUID().GetCounter()); stmt->SetData(0, static_cast<uint64>(GetDbGuid()));
trans->Append(stmt); trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_REFUND_INSTANCE); 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(1, GetRefundRecipient());
stmt->SetData(2, GetPaidMoney()); stmt->SetData(2, GetPaidMoney());
stmt->SetData(3, uint16(GetPaidExtendedCost())); stmt->SetData(3, uint16(GetPaidExtendedCost()));
@ -1192,7 +1294,7 @@ void Item::DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans)
if (trans) if (trans)
{ {
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
stmt->SetData(0, GetGUID().GetCounter()); stmt->SetData(0, static_cast<uint64>(GetDbGuid()));
(*trans)->Append(stmt); (*trans)->Append(stmt);
} }
} }
@ -1270,7 +1372,7 @@ void Item::ClearSoulboundTradeable(Player* currentOwner)
allowedGUIDs.clear(); allowedGUIDs.clear();
SetState(ITEM_CHANGED, currentOwner); SetState(ITEM_CHANGED, currentOwner);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE);
stmt->SetData(0, GetGUID().GetCounter()); stmt->SetData(0, static_cast<uint64>(GetDbGuid()));
CharacterDatabase.Execute(stmt); CharacterDatabase.Execute(stmt);
} }

View File

@ -239,11 +239,18 @@ public:
[[nodiscard]] bool IsBoundByEnchant() const; [[nodiscard]] bool IsBoundByEnchant() const;
[[nodiscard]] bool IsBoundByTempEnchant() const; [[nodiscard]] bool IsBoundByTempEnchant() const;
virtual void SaveToDB(CharacterDatabaseTransaction trans); 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); 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); static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
virtual void DeleteFromDB(CharacterDatabaseTransaction trans); virtual void DeleteFromDB(CharacterDatabaseTransaction trans);
static void DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid); static void DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
void DeleteFromInventoryDB(CharacterDatabaseTransaction trans); void DeleteFromInventoryDB(CharacterDatabaseTransaction trans);
// Decoupled: DB GUID accessors
uint64 GetDbGuid() const { return m_dbGuid; }
void SetDbGuid(uint64 v) { m_dbGuid = v; }
void SaveRefundDataToDB(); void SaveRefundDataToDB();
void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans); void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans);
@ -376,5 +383,6 @@ private:
uint32 m_paidMoney; uint32 m_paidMoney;
uint32 m_paidExtendedCost; uint32 m_paidExtendedCost;
AllowedLooterSet allowedGUIDs; AllowedLooterSet allowedGUIDs;
uint64 m_dbGuid = 0; // Persistent DB identifier (64-bit)
}; };
#endif #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 "Util.h"
#include "World.h" #include "World.h"
#include "WorldPacket.h" #include "WorldPacket.h"
#include "Entities/Item/ItemGuidMap.h"
/// @todo: this import is not necessary for compilation and marked as unused by the IDE /// @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 // 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(); ss << ' ' << (*itr).GetCounter();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE); 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()); stmt->SetData(1, ss.str());
CharacterDatabase.Execute(stmt); CharacterDatabase.Execute(stmt);
} }
@ -3036,7 +3037,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
if (pItem->IsWrapped()) if (pItem->IsWrapped())
{ {
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->SetData(0, pItem->GetGUID().GetCounter()); stmt->SetData(0, static_cast<uint64>(pItem->GetDbGuid()));
CharacterDatabase.Execute(stmt); 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* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields)
{ {
Item* item = nullptr; Item* item = nullptr;
ObjectGuid::LowType itemGuid = fields[13].Get<uint32>(); uint64 itemGuidDb = fields[13].Get<uint64>();
uint32 itemEntry = fields[14].Get<uint32>(); uint32 itemEntry = fields[14].Get<uint32>();
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry)) if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry))
{ {
bool remove = false; bool remove = false;
item = NewItemOrBag(proto); item = NewItemOrBag(proto);
if (item->LoadFromDB(itemGuid, GetGUID(), fields, itemEntry)) bool ok = item->LoadFromDB64(itemGuidDb, GetGUID(), fields, itemEntry);
if (ok)
{ {
CharacterDatabasePreparedStatement* stmt = nullptr; CharacterDatabasePreparedStatement* stmt = nullptr;
// Do not allow to have item limited to another map/zone in alive state // Do not allow to have item limited to another map/zone in alive state
if (IsAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zoneId)) 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); GetGUID().ToString(), GetName(), GetMapId(), item->GetGUID().ToString(), item->GetEntry(), zoneId);
remove = true; 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.", 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()); GetGUID().ToString(), GetName(), item->GetGUID().ToString(), item->GetEntry(), item->GetPlayedTime());
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); 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); trans->Append(stmt);
item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE); item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE);
@ -6013,7 +6015,7 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
{ {
// xinef: sync query // xinef: sync query
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_REFUNDS); 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()); stmt->SetData(1, GetGUID().GetCounter());
if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{ {
@ -6033,7 +6035,7 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
else if (item->IsBOPTradable()) else if (item->IsBOPTradable())
{ {
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_BOP_TRADE); 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)) if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{ {
@ -6083,13 +6085,15 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
else else
{ {
LOG_ERROR("entities.player", "Player::_LoadInventory: player ({}, name: '{}') has broken item (GUID: {}, entry: {}) in inventory. Deleting item.", 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 = true;
} }
// Remove item from inventory if necessary // Remove item from inventory if necessary
if (remove) 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->FSetState(ITEM_REMOVED);
item->SaveToDB(trans); // it also deletes item object! item->SaveToDB(trans); // it also deletes item object!
item = nullptr; 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.", LOG_ERROR("entities.player", "Player::_LoadInventory: player ({}, name: '{}') has unknown item (entry: {}) in inventory. Deleting item.",
GetGUID().ToString(), GetName(), itemEntry); GetGUID().ToString(), GetName(), itemEntry);
Item::DeleteFromInventoryDB(trans, itemGuid); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
Item::DeleteFromDB(trans, itemGuid); 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; return item;
} }
@ -6108,19 +6116,19 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
// load mailed item which should receive current player // load mailed item which should receive current player
Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint32 mailId, Mail* mail, Field* fields) 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>(); uint32 itemEntry = fields[12].Get<uint32>();
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry); ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
if (!proto) if (!proto)
{ {
LOG_ERROR("entities.player", "Player {} ({}) has unknown item in mailed items (GUID: {}, Entry: {}) in mail ({}), deleted.", 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(); CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM);
stmt->SetData(0, itemGuid); stmt->SetData(0, static_cast<uint64>(itemGuidDb));
trans->Append(stmt); trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans); CharacterDatabase.CommitTransaction(trans);
@ -6130,12 +6138,12 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint
Item* item = NewItemOrBag(proto); Item* item = NewItemOrBag(proto);
ObjectGuid ownerGuid = fields[13].Get<uint32>() ? ObjectGuid::Create<HighGuid::Player>(fields[13].Get<uint32>()) : ObjectGuid::Empty; 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); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
stmt->SetData(0, itemGuid); stmt->SetData(0, static_cast<uint64>(itemGuidDb));
CharacterDatabase.Execute(stmt); CharacterDatabase.Execute(stmt);
item->FSetState(ITEM_REMOVED); item->FSetState(ITEM_REMOVED);
@ -6147,7 +6155,7 @@ Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint
if (mail) if (mail)
{ {
mail->AddItem(itemGuid, itemEntry); mail->AddItem(item->GetGUID().GetCounter(), itemEntry);
} }
if (player) if (player)
@ -7271,11 +7279,11 @@ void Player::_SaveInventory(CharacterDatabaseTransaction trans)
} }
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM); 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); trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->SetData(0, item->GetGUID().GetCounter()); stmt->SetData(0, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt); trans->Append(stmt);
m_items[i]->FSetState(ITEM_NEW); m_items[i]->FSetState(ITEM_NEW);
@ -7369,16 +7377,23 @@ void Player::_SaveInventory(CharacterDatabaseTransaction trans)
{ {
case ITEM_NEW: case ITEM_NEW:
case ITEM_CHANGED: 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 = CharacterDatabase.GetPreparedStatement(CHAR_REP_INVENTORY_ITEM);
stmt->SetData(0, lowGuid); stmt->SetData(0, lowGuid);
stmt->SetData(1, bag_guid); stmt->SetData(1, bag_guid);
stmt->SetData (2, item->GetSlot()); stmt->SetData (2, item->GetSlot());
stmt->SetData(3, item->GetGUID().GetCounter()); stmt->SetData(3, static_cast<uint64>(item->GetDbGuid()));
trans->Append(stmt); trans->Append(stmt);
break; break;
case ITEM_REMOVED: case ITEM_REMOVED:
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM); 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); trans->Append(stmt);
case ITEM_UNCHANGED: case ITEM_UNCHANGED:
break; break;

View File

@ -19586,12 +19586,6 @@ void Unit::_ExitVehicle(Position const* exitPosition)
sScriptMgr->AnticheatSetUnderACKmount(player); sScriptMgr->AnticheatSetUnderACKmount(player);
} }
else if (HasUnitMovementFlag(MOVEMENTFLAG_ROOT))
{
WorldPacket data(SMSG_SPLINE_MOVE_UNROOT, 8);
data << GetPackGUID();
SendMessageToSet(&data, false);
}
// xinef: hack for flameleviathan seat vehicle // xinef: hack for flameleviathan seat vehicle
VehicleEntry const* vehicleInfo = vehicle->GetVehicleInfo(); VehicleEntry const* vehicleInfo = vehicle->GetVehicleInfo();

View File

@ -27,6 +27,7 @@
#include "DBCStructure.h" #include "DBCStructure.h"
#include "DatabaseEnv.h" #include "DatabaseEnv.h"
#include "DisableMgr.h" #include "DisableMgr.h"
#include "Entities/Item/ItemGuidMap.h"
#include "GameEventMgr.h" #include "GameEventMgr.h"
#include "GameObjectAIFactory.h" #include "GameObjectAIFactory.h"
#include "GameTime.h" #include "GameTime.h"
@ -6327,7 +6328,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
do do
{ {
Field* fields = items->Fetch(); 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>(); item.item_template = fields[1].Get<uint32>();
uint32 mailId = fields[2].Get<uint32>(); uint32 mailId = fields[2].Get<uint32>();
itemsCache[mailId].push_back(item); itemsCache[mailId].push_back(item);
@ -7195,14 +7196,11 @@ void ObjectMgr::SetHighestGuids()
GetGuidSequenceGenerator<HighGuid::Player>().Set((*result)[0].Get<uint32>() + 1); GetGuidSequenceGenerator<HighGuid::Player>().Set((*result)[0].Get<uint32>() + 1);
result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance"); result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
if (result) // Initialize persistent item DB GUID starting point (64-bit)
GetGuidSequenceGenerator<HighGuid::Item>().Set((*result)[0].Get<uint32>() + 1); // 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"); result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
if (result) if (result)

View File

@ -387,25 +387,26 @@ void Guild::BankTab::LoadFromDB(Field* fields)
bool Guild::BankTab::LoadItemFromDB(Field* fields) bool Guild::BankTab::LoadItemFromDB(Field* fields)
{ {
uint8 slotId = fields[13].Get<uint8>(); 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>(); uint32 itemEntry = fields[15].Get<uint32>();
if (slotId >= GUILD_BANK_MAX_SLOTS) 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; return false;
} }
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry); ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
if (!proto) 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; return false;
} }
Item* pItem = NewItemOrBag(proto); 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); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM);
stmt->SetData(0, m_guildId); stmt->SetData(0, m_guildId);

View File

@ -31,6 +31,7 @@
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "WorldPacket.h" #include "WorldPacket.h"
#include "WorldSession.h" #include "WorldSession.h"
#include "Entities/Item/ItemGuidMap.h"
#define MAX_INBOX_CLIENT_CAPACITY 50 #define MAX_INBOX_CLIENT_CAPACITY 50
@ -73,14 +74,14 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
recvData >> subject; recvData >> subject;
recvData >> body;
// prevent client crash // prevent client crash
if (subject.find("| |") != std::string::npos || body.find("| |") != std::string::npos) if (subject.find("| |") != std::string::npos || body.find("| |") != std::string::npos)
{ {
return; return;
} }
recvData >> body;
recvData >> unk1; // stationery? recvData >> unk1; // stationery?
recvData >> unk2; // 0x00000000 recvData >> unk2; // 0x00000000
@ -712,7 +713,7 @@ void WorldSession::HandleGetMailList(WorldPacket& recvData)
for (uint8 i = 0; i < item_count; ++i) 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?) // item index (0-6?)
data << uint8(i); data << uint8(i);
// item guid low? // 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. 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; delete bodyItem;
return; return;

View File

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

View File

@ -246,7 +246,7 @@ void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver cons
{ {
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_MAIL_ITEM); stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_MAIL_ITEM);
stmt->SetData(0, mailId); 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()); stmt->SetData(2, receiver.GetPlayerGUIDLow());
trans->Append(stmt); trans->Append(stmt);
} }

View File

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

View File

@ -133,13 +133,13 @@ struct boss_anub_arak : public BossAI
DoCastSelf(SPELL_IMPALE_PERIODIC, true); DoCastSelf(SPELL_IMPALE_PERIODIC, true);
++_submergePhase; ++_submergePhase;
events.Reset();
ScheduleSubmerged(); ScheduleSubmerged();
} }
} }
void ScheduleEmerged() void ScheduleEmerged()
{ {
events.Reset();
events.SetPhase(PHASE_EMERGED); events.SetPhase(PHASE_EMERGED);
events.ScheduleEvent(EVENT_CARRION_BEETLES, 6500ms, 0, PHASE_EMERGED); events.ScheduleEvent(EVENT_CARRION_BEETLES, 6500ms, 0, PHASE_EMERGED);
events.ScheduleEvent(EVENT_LEECHING_SWARM, 20s, 0, PHASE_EMERGED); events.ScheduleEvent(EVENT_LEECHING_SWARM, 20s, 0, PHASE_EMERGED);
@ -148,7 +148,6 @@ struct boss_anub_arak : public BossAI
void ScheduleSubmerged() void ScheduleSubmerged()
{ {
events.Reset();
events.SetPhase(PHASE_SUBMERGED); events.SetPhase(PHASE_SUBMERGED);
events.ScheduleEvent(EVENT_EMERGE, 60s, 0, PHASE_SUBMERGED); events.ScheduleEvent(EVENT_EMERGE, 60s, 0, PHASE_SUBMERGED);
@ -209,14 +208,13 @@ struct boss_anub_arak : public BossAI
} }
} }
void JustEngagedWith(Unit* who) override void JustEngagedWith(Unit* /*who*/) override
{ {
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO); Talk(SAY_AGGRO);
instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT);
events.SetPhase(PHASE_EMERGED); events.SetPhase(PHASE_EMERGED);
events.ScheduleEvent(EVENT_CLOSE_DOORS, 5s); events.ScheduleEvent(EVENT_CLOSE_DOORS, 5s, 0, PHASE_EMERGED);
ScheduleEmerged(); ScheduleEmerged();
// set up world triggers // set up world triggers
@ -288,7 +286,8 @@ struct boss_anub_arak : public BossAI
if (_remainingLargeSummonsBeforeEmerge == 0) if (_remainingLargeSummonsBeforeEmerge == 0)
{ {
events.Reset(); events.Reset();
events.ScheduleEvent(EVENT_EMERGE, 5s); events.SetPhase(PHASE_SUBMERGED);
events.ScheduleEvent(EVENT_EMERGE, 5s, 0, PHASE_SUBMERGED);
} }
break; break;
} }
@ -333,10 +332,10 @@ struct boss_anub_arak : public BossAI
DoCastSelf(SPELL_SELF_ROOT, true); DoCastSelf(SPELL_SELF_ROOT, true);
me->DisableRotate(true); me->DisableRotate(true);
me->SendMovementFlagUpdate(); me->SendMovementFlagUpdate();
events.ScheduleEvent(EVENT_ENABLE_ROTATE, 3300ms); events.ScheduleEvent(EVENT_ENABLE_ROTATE, 3300ms, 0, PHASE_EMERGED);
DoCast(target, SPELL_POUND); DoCast(target, SPELL_POUND);
} }
events.ScheduleEvent(EVENT_POUND, 18s); events.ScheduleEvent(EVENT_POUND, 18s, 0, PHASE_EMERGED);
break; break;
case EVENT_ENABLE_ROTATE: case EVENT_ENABLE_ROTATE:
me->RemoveAurasDueToSpell(SPELL_SELF_ROOT); me->RemoveAurasDueToSpell(SPELL_SELF_ROOT);