diff options
-rwxr-xr-x | src/server/game/Entities/Item/Item.cpp | 20 | ||||
-rwxr-xr-x | src/server/game/Entities/Item/Item.h | 2 | ||||
-rwxr-xr-x | src/server/game/Entities/Player/Player.cpp | 303 | ||||
-rwxr-xr-x | src/server/game/Entities/Player/Player.h | 3 |
4 files changed, 162 insertions, 166 deletions
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index aad234141d6..8ace11b723d 100755 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -451,20 +451,32 @@ bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, Field* fields, uint32 entr return true; } -void Item::DeleteFromDB(SQLTransaction& trans) +/*static*/ +void Item::DeleteFromDB(SQLTransaction& trans, uint32 itemGuid) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); - stmt->setUInt32(0, GetGUIDLow()); + stmt->setUInt32(0, itemGuid); trans->Append(stmt); } -void Item::DeleteFromInventoryDB(SQLTransaction& trans) +void Item::DeleteFromDB(SQLTransaction& trans) +{ + DeleteFromDB(trans, GetGUIDLow()); +} + +/*static*/ +void Item::DeleteFromInventoryDB(SQLTransaction& trans, uint32 itemGuid) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVENTORY_ITEM); - stmt->setUInt32(0, GetGUIDLow()); + stmt->setUInt32(0, itemGuid); trans->Append(stmt); } +void Item::DeleteFromInventoryDB(SQLTransaction& trans) +{ + DeleteFromInventoryDB(trans, GetGUIDLow()); +} + ItemPrototype const *Item::GetProto() const { return ObjectMgr::GetItemPrototype(GetEntry()); diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 3696916eba5..cf6759804dd 100755 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -246,7 +246,9 @@ class Item : public Object bool IsBoundByEnchant() const; virtual void SaveToDB(SQLTransaction& trans); virtual bool LoadFromDB(uint32 guid, uint64 owner_guid, Field* fields, uint32 entry); + static void DeleteFromDB(SQLTransaction& trans, uint32 itemGuid); virtual void DeleteFromDB(SQLTransaction& trans); + static void DeleteFromInventoryDB(SQLTransaction& trans, uint32 itemGuid); void DeleteFromInventoryDB(SQLTransaction& trans); void SaveRefundDataToDB(); void DeleteRefundDataFromDB(); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b7c67a252ce..cc2442615ce 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16995,87 +16995,145 @@ void Player::LoadCorpse() } } -void Player::_LoadInventory(PreparedQueryResult result, uint32 timediff) +void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) { //QueryResult *result = CharacterDatabase.PQuery("SELECT data,text,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GetGUIDLow()); - std::map<uint64, Bag*> bagMap; // fast guid lookup for bags //NOTE: the "order by `bag`" is important because it makes sure //the bagMap is filled before items in the bags are loaded //NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?) //expected to be equipped before offhand items (TODO: fixme) - uint32 zone = GetZoneId(); - if (result) { + uint32 zoneId = GetZoneId(); + + std::map<uint64, Bag*> bagMap; // fast guid lookup for bags std::list<Item*> problematicItems; SQLTransaction trans = CharacterDatabase.BeginTransaction(); - // prevent items from being added to the queue when stored + // Prevent items from being added to the queue while loading m_itemUpdateQueueBlocked = true; do { Field* fields = result->Fetch(); + if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields)) + { + uint32 bagGuid = fields[11].GetUInt32(); + uint8 slot = fields[12].GetUInt8(); - uint32 bag_guid = fields[11].GetUInt32(); - uint8 slot = fields[12].GetUInt8(); - uint32 item_guid = fields[13].GetUInt32(); - uint32 item_id = fields[14].GetUInt32(); + uint8 err = EQUIP_ERR_OK; + // Item is not in bag + if (!bagGuid) + { + item->SetContainer(NULL); + item->SetSlot(slot); - ItemPrototype const * proto = ObjectMgr::GetItemPrototype(item_id); + if (IsInventoryPos(INVENTORY_SLOT_BAG_0, slot)) + { + ItemPosCountVec dest; + err = CanStoreItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false); + if (err == EQUIP_ERR_OK) + item = StoreItem(dest, item, true); + } + else if (IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot)) + { + uint16 dest; + err = CanEquipItem(slot, dest, item, false, false); + if (err == EQUIP_ERR_OK) + QuickEquipItem(dest, item); + } + else if (IsBankPos(INVENTORY_SLOT_BAG_0, slot)) + { + ItemPosCountVec dest; + err = CanBankItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false, false); + if (err == EQUIP_ERR_OK) + item = BankItem(dest, item, true); + } - if (!proto) - { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVENTORY_ITEM); - stmt->setUInt32(0, item_guid); - trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); - stmt->setUInt32(0, item_guid); - trans->Append(stmt); - sLog->outError("Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.", GetName(),item_id); - continue; + // Remember bags that may contain items in them + if (err == EQUIP_ERR_OK) + if (item->IsBag() && IsBagPos(item->GetPos())) + bagMap[item->GetGUIDLow()] = (Bag*)item; + } + else + { + item->SetSlot(NULL_SLOT); + // Item is in the bag, find the bag + std::map<uint64, Bag*>::iterator itr = bagMap.find(bagGuid); + if (itr != bagMap.end()) + { + ItemPosCountVec dest; + err = CanStoreItem(itr->second->GetSlot(), slot, dest, item); + if (err == EQUIP_ERR_OK) + itr->second->StoreItem(slot, item, true); + } + } + + // Item's state may have changed after storing + if (err == EQUIP_ERR_OK) + item->SetState(ITEM_UNCHANGED, this); + else + { + sLog->outError("Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) which can't be loaded into inventory (Bag GUID: %u, slot: %u) by reason %u. Item will be sent by mail.", + GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry(), bagGuid, slot, err); + item->DeleteFromInventoryDB(trans); + problematicItems.push_back(item); + } } + } while (result->NextRow()); - Item *item = NewItemOrBag(proto); + m_itemUpdateQueueBlocked = false; + + // Send problematic items by mail + while (!problematicItems.empty()) + { + std::string subject = GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM); - if (!item->LoadFromDB(item_guid, GetGUID(), fields, item_id)) + MailDraft draft(subject, "There were problems with equipping item(s)."); + for (uint8 i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i) { - sLog->outError("Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(),item_id); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVENTORY_ITEM); - stmt->setUInt32(0, item_guid); - trans->Append(stmt); - item->FSetState(ITEM_REMOVED); - item->SaveToDB(trans); // it also deletes item object ! - continue; + draft.AddItem(problematicItems.front()); + problematicItems.pop_front(); } + draft.SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED); + } + CharacterDatabase.CommitTransaction(trans); + } + //if (isAlive()) + _ApplyAllItemMods(); +} - // not allow have in alive state item limited to another map/zone - if (isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zone)) +Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields) +{ + Item* item = NULL; + uint32 itemGuid = fields[13].GetUInt32(); + uint32 itemEntry = fields[14].GetUInt32(); + if (ItemPrototype const * proto = ObjectMgr::GetItemPrototype(itemEntry)) + { + bool remove = false; + item = NewItemOrBag(proto); + if (item->LoadFromDB(itemGuid, GetGUID(), fields, itemEntry)) + { + // Do not allow to have item limited to another map/zone in alive state + if (isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zoneId)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVENTORY_ITEM); - stmt->setUInt32(0, item_guid); - trans->Append(stmt); - item->FSetState(ITEM_REMOVED); - item->SaveToDB(trans); // it also deletes item object ! - continue; + sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s', map: %u) has item (GUID: %u, entry: %u) limited to another map (%u). Deleting item.", + GetGUIDLow(), GetName(), GetMapId(), item->GetGUIDLow(), item->GetEntry(), zoneId); + remove = true; } - // "Conjured items disappear if you are logged out for more than 15 minutes" - if (timediff > 15*MINUTE && proto->Flags & ITEM_PROTO_FLAG_CONJURED) + else if (timeDiff > 15 * MINUTE && proto->Flags & ITEM_PROTO_FLAG_CONJURED) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVENTORY_ITEM); - stmt->setUInt32(0, item_guid); - trans->Append(stmt); - item->FSetState(ITEM_REMOVED); - item->SaveToDB(trans); // it also deletes item object ! - continue; + sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s', diff: %u) has conjured item (GUID: %u, entry: %u) with expired lifetime (15 minutes). Deleting item.", + GetGUIDLow(), GetName(), timeDiff, item->GetGUIDLow(), item->GetEntry()); + remove = true; } - - if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) + else if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) { - if (item->GetPlayedTime() > (2*HOUR)) + if (item->GetPlayedTime() > (2 * HOUR)) { - sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Item::LoadFromDB, Item GUID: %u: refund time expired, deleting refund data and removing refundable flag.", item->GetGUIDLow()); + sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with expired refund time (%u). Deleting refund data and removing refundable flag.", + GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry(), item->GetPlayedTime()); trans->PAppend("DELETE FROM item_refund_instance WHERE item_guid = '%u'", item->GetGUIDLow()); item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE); } @@ -17084,19 +17142,18 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timediff) PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_ITEM_REFUNDS); stmt->setUInt32(0, item->GetGUIDLow()); stmt->setUInt32(1, GetGUIDLow()); - PreparedQueryResult result2 = CharacterDatabase.Query(stmt); - if (!result2) + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { - sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Item::LoadFromDB, Item GUID: %u has field flags & ITEM_FLAGS_REFUNDABLE but has no data in item_refund_instance, removing flag.", item->GetGUIDLow()); - item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE); + item->SetRefundRecipient((*result)[0].GetUInt32()); + item->SetPaidMoney((*result)[1].GetUInt32()); + item->SetPaidExtendedCost((*result)[2].GetUInt16()); + AddRefundReference(item->GetGUIDLow()); } else { - Field* fields2 = result2->Fetch(); - item->SetRefundRecipient(fields2[0].GetUInt32()); - item->SetPaidMoney(fields2[1].GetUInt32()); - item->SetPaidExtendedCost(fields2[2].GetUInt16()); - AddRefundReference(item->GetGUIDLow()); + sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with refundable flags, but without data in item_refund_instance. Removing flag.", + GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry()); + item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE); } } } @@ -17104,16 +17161,9 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timediff) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_ITEM_BOP_TRADE); stmt->setUInt32(0, item->GetGUIDLow()); - PreparedQueryResult result2 = CharacterDatabase.Query(stmt); - if (!result2) + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { - sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Item::LoadFromDB, Item GUID: %u has flag ITEM_FLAG_BOP_TRADEABLE but has no data in item_soulbound_trade_data, removing flag.", item->GetGUIDLow()); - item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE); - } - else - { - Field* fields2 = result2->Fetch(); - std::string strGUID = fields2[0].GetString(); + std::string strGUID = (*result)[0].GetString(); Tokens GUIDlist(strGUID, ' '); AllowedLooterSet looters; for (Tokens::iterator itr = GUIDlist.begin(); itr != GUIDlist.end(); ++itr) @@ -17121,106 +17171,37 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timediff) item->SetSoulboundTradeable(&looters, this, true); m_itemSoulboundTradeable.push_back(item); } - } - - bool success = true; - - if (!bag_guid) - { - // the item is not in a bag - item->SetContainer(NULL); - item->SetSlot(slot); - - if (IsInventoryPos(INVENTORY_SLOT_BAG_0, slot)) - { - ItemPosCountVec dest; - if (CanStoreItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false) == EQUIP_ERR_OK) - item = StoreItem(dest, item, true); - else - success = false; - } - else if (IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot)) - { - uint16 dest; - if (CanEquipItem(slot, dest, item, false, false) == EQUIP_ERR_OK) - QuickEquipItem(dest, item); - else - success = false; - } - else if (IsBankPos(INVENTORY_SLOT_BAG_0, slot)) - { - ItemPosCountVec dest; - if (CanBankItem(INVENTORY_SLOT_BAG_0, slot, dest, item, false, false) == EQUIP_ERR_OK) - item = BankItem(dest, item, true); - else - success = false; - } - - if (success) - { - // store bags that may contain items in them - if (item->IsBag() && IsBagPos(item->GetPos())) - bagMap[item_guid] = (Bag*)item; - } - } - else - { - item->SetSlot(NULL_SLOT); - // the item is in a bag, find the bag - std::map<uint64, Bag*>::iterator itr = bagMap.find(bag_guid); - if (itr != bagMap.end()) + else { - ItemPosCountVec dest; - uint8 result = CanStoreItem(itr->second->GetSlot(), slot, dest, item); - if (result == EQUIP_ERR_OK) - itr->second->StoreItem(slot, item, true); - else - { - sLog->outError("Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by reason %u.", GetName(),item_guid, item_id, bag_guid, slot, result); - success = false; - } + sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with ITEM_FLAG_BOP_TRADEABLE flag, but without data in item_soulbound_trade_data. Removing flag.", + GetGUIDLow(), GetName(), item->GetGUIDLow(), item->GetEntry()); + item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE); } - else - success = false; } - - // item's state may have changed after stored - if (success) - item->SetState(ITEM_UNCHANGED, this); - else - { - sLog->outError("Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by some reason, will send by mail.", GetName(),item_guid, item_id, bag_guid, slot); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVENTORY_ITEM); - stmt->setUInt32(0, item_guid); - trans->Append(stmt); - problematicItems.push_back(item); - } - } while (result->NextRow()); - - m_itemUpdateQueueBlocked = false; - - // send by mail problematic items - while (!problematicItems.empty()) + } + else { - std::string subject = GetSession()->GetTrinityString(LANG_NOT_EQUIPPED_ITEM); - - // fill mail - MailDraft draft(subject, "There's were problems with equipping item(s)."); - - for (uint8 i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i) - { - Item* item = problematicItems.front(); - problematicItems.pop_front(); - - draft.AddItem(item); - } - - draft.SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED); + sLog->outError("Player::_LoadInventory: player (GUID: %u, name: '%s') has broken item (GUID: %u, entry: %u) in inventory. Deleting item.", + GetGUIDLow(), GetName(), itemGuid, itemEntry); + remove = true; + } + // Remove item from inventory if necessary + if (remove) + { + Item::DeleteFromInventoryDB(trans, itemGuid); + item->FSetState(ITEM_REMOVED); + item->SaveToDB(trans); // it also deletes item object! + item = NULL; } - CharacterDatabase.CommitTransaction(trans); } - //if (isAlive()) - _ApplyAllItemMods(); + else + { + sLog->outError("Player::_LoadInventory: player (GUID: %u, name: '%s') has unknown item (entry: %u) in inventory. Deleting item.", + GetGUIDLow(), GetName(), itemEntry); + Item::DeleteFromInventoryDB(trans, itemGuid); + Item::DeleteFromDB(trans, itemGuid); + } + return item; } // load mailed item which should receive current player diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3052e98f9ce..0f314a0c41e 100755 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2446,7 +2446,7 @@ class Player : public Unit, public GridObject<Player> void _LoadAuras(PreparedQueryResult result, uint32 timediff); void _LoadGlyphAuras(); void _LoadBoundInstances(PreparedQueryResult result); - void _LoadInventory(PreparedQueryResult result, uint32 timediff); + void _LoadInventory(PreparedQueryResult result, uint32 timeDiff); void _LoadMailInit(PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery); void _LoadMail(); void _LoadMailedItems(Mail *mail); @@ -2662,6 +2662,7 @@ class Player : public Unit, public GridObject<Player> uint8 _CanStoreItem_InBag(uint8 bag, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot) const; uint8 _CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot) const; Item* _StoreItem(uint16 pos, Item *pItem, uint32 count, bool clone, bool update); + Item* _LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields); std::set<uint32> m_refundableItems; void SendRefundInfo(Item* item); |