mirror of
https://github.com/azerothcore/azerothcore-wotlk.git
synced 2025-11-10 21:04:26 +08:00
Compare commits
11 Commits
27eab39adb
...
57136ea87a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57136ea87a | ||
|
|
611a85529d | ||
|
|
040e7a0a4d | ||
|
|
d4cd580ddc | ||
|
|
37833c66e6 | ||
|
|
ec274182a2 | ||
|
|
d9b2e775e3 | ||
|
|
c85c86b285 | ||
|
|
125e1aec9d | ||
|
|
01ba31908e | ||
|
|
18958dfb26 |
3
data/sql/updates/db_world/2025_11_09_00.sql
Normal file
3
data/sql/updates/db_world/2025_11_09_00.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-- DB update 2025_11_08_02 -> 2025_11_09_00
|
||||||
|
--
|
||||||
|
UPDATE `creature_addon` SET `visibilityDistanceType` = 3 WHERE `guid` = 103278;
|
||||||
3
data/sql/updates/db_world/2025_11_09_01.sql
Normal file
3
data/sql/updates/db_world/2025_11_09_01.sql
Normal 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);
|
||||||
3
data/sql/updates/db_world/2025_11_09_02.sql
Normal file
3
data/sql/updates/db_world/2025_11_09_02.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-- DB update 2025_11_09_01 -> 2025_11_09_02
|
||||||
|
--
|
||||||
|
DELETE FROM `creature` WHERE `id1` = 10218;
|
||||||
@ -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`);
|
||||||
@ -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>());
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
59
src/server/game/Entities/Item/ItemGuidMap.cpp
Normal file
59
src/server/game/Entities/Item/ItemGuidMap.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/server/game/Entities/Item/ItemGuidMap.h
Normal file
56
src/server/game/Entities/Item/ItemGuidMap.h
Normal 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()
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user