Fix exploit with swaping already looted items.

Patch by Vladimir from idea of Zhenya.
Fixes issue #702.

--HG--
branch : trunk
This commit is contained in:
Trazom62
2010-05-31 19:10:26 +02:00
parent 1a636d3af8
commit 15847b2f2f
4 changed files with 104 additions and 91 deletions
+20
View File
@@ -705,6 +705,9 @@ bool Item::IsEquipped() const
bool Item::CanBeTraded(bool mail) const bool Item::CanBeTraded(bool mail) const
{ {
if (m_lootGenerated)
return false;
if ((!mail || !IsBoundAccountWide()) && IsSoulBound()) if ((!mail || !IsBoundAccountWide()) && IsSoulBound())
return false; return false;
@@ -766,6 +769,23 @@ bool Item::IsBoundByEnchant() const
return false; return false;
} }
uint8 Item::CanBeMergedPartlyWith(ItemPrototype const* proto) const
{
// not allow merge looting currently items
if (m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
// check item type
if (GetEntry() != proto->ItemId)
return EQUIP_ERR_ITEM_CANT_STACK;
// check free space (full stacks can't be target of merge
if (GetCount() >= proto->GetMaxStackSize())
return EQUIP_ERR_ITEM_CANT_STACK;
return EQUIP_ERR_OK;
}
bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const
{ {
ItemPrototype const* proto = GetProto(); ItemPrototype const* proto = GetProto();
+1
View File
@@ -265,6 +265,7 @@ class Item : public Object
uint32 GetMaxStackCount() const { return GetProto()->GetMaxStackSize(); } uint32 GetMaxStackCount() const { return GetProto()->GetMaxStackSize(); }
uint8 GetGemCountWithID(uint32 GemID) const; uint8 GetGemCountWithID(uint32 GemID) const;
uint8 GetGemCountWithLimitCategory(uint32 limitCategory) const; uint8 GetGemCountWithLimitCategory(uint32 limitCategory) const;
uint8 CanBeMergedPartlyWith(ItemPrototype const* proto) const;
uint8 GetSlot() const {return m_slot;} uint8 GetSlot() const {return m_slot;}
Bag *GetContainer() { return m_container; } Bag *GetContainer() { return m_container; }
-7
View File
@@ -153,13 +153,6 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket & recv_data)
if (!pSrcItem) if (!pSrcItem)
return; // only at cheat return; // only at cheat
if (pSrcItem->m_lootGenerated) // prevent swap looting item
{
//best error message found for attempting to swap while looting
_player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL);
return;
}
uint16 dest; uint16 dest;
uint8 msg = _player->CanEquipItem(NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag()); uint8 msg = _player->CanEquipItem(NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag());
if (msg != EQUIP_ERR_OK) if (msg != EQUIP_ERR_OK)
+83 -84
View File
@@ -9892,13 +9892,10 @@ uint8 Player::_CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVe
// non empty slot, check item type // non empty slot, check item type
else else
{ {
// check item type // can be merged at least partly
if (pItem2->GetEntry() != pProto->ItemId) uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
return EQUIP_ERR_ITEM_CANT_STACK; if (res != EQUIP_ERR_OK)
return res;
// check free space
if (pItem2->GetCount() >= pProto->GetMaxStackSize())
return EQUIP_ERR_ITEM_CANT_STACK;
// free stack space or infinity // free stack space or infinity
need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); need_space = pProto->GetMaxStackSize() - pItem2->GetCount();
@@ -9953,40 +9950,30 @@ uint8 Player::_CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, ItemPrototyp
if ((pItem2 != NULL) != merge) if ((pItem2 != NULL) != merge)
continue; continue;
uint32 need_space = pProto->GetMaxStackSize();
if (pItem2) if (pItem2)
{ {
if (pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) // can be merged at least partly
{ uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); if (res != EQUIP_ERR_OK)
if (need_space > count) continue;
need_space = count;
ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space); // descrease at current stacksize
if (!newPosition.isContainedIn(dest)) need_space -= pItem2->GetCount();
{
dest.push_back(newPosition);
count -= need_space;
if (count == 0)
return EQUIP_ERR_OK;
}
}
} }
else
if (need_space > count)
need_space = count;
ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
if (!newPosition.isContainedIn(dest))
{ {
uint32 need_space = pProto->GetMaxStackSize(); dest.push_back(newPosition);
if (need_space > count) count -= need_space;
need_space = count;
ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space); if (count==0)
if (!newPosition.isContainedIn(dest)) return EQUIP_ERR_OK;
{
dest.push_back(newPosition);
count -= need_space;
if (count == 0)
return EQUIP_ERR_OK;
}
} }
} }
return EQUIP_ERR_OK; return EQUIP_ERR_OK;
@@ -10010,39 +9997,30 @@ uint8 Player::_CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end, I
if ((pItem2 != NULL) != merge) if ((pItem2 != NULL) != merge)
continue; continue;
uint32 need_space = pProto->GetMaxStackSize();
if (pItem2) if (pItem2)
{ {
if (pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->GetMaxStackSize()) // can be merged at least partly
{ uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
uint32 need_space = pProto->GetMaxStackSize() - pItem2->GetCount(); if (res != EQUIP_ERR_OK)
if (need_space > count) continue;
need_space = count;
ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
if (!newPosition.isContainedIn(dest))
{
dest.push_back(newPosition);
count -= need_space;
if (count == 0) // descrease at current stacksize
return EQUIP_ERR_OK; need_space -= pItem2->GetCount();
}
}
} }
else
if (need_space > count)
need_space = count;
ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
if (!newPosition.isContainedIn(dest))
{ {
uint32 need_space = pProto->GetMaxStackSize(); dest.push_back(newPosition);
if (need_space > count) count -= need_space;
need_space = count;
ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space); if (count==0)
if (!newPosition.isContainedIn(dest)) return EQUIP_ERR_OK;
{
dest.push_back(newPosition);
count -= need_space;
if (count == 0)
return EQUIP_ERR_OK;
}
} }
} }
return EQUIP_ERR_OK; return EQUIP_ERR_OK;
@@ -10060,11 +10038,22 @@ uint8 Player::_CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32
return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND; return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND;
} }
if (pItem && pItem->IsBindedNotWith(this)) if (pItem)
{ {
if (no_space_count) // item used
*no_space_count = count; if (pItem->m_lootGenerated)
return EQUIP_ERR_DONT_OWN_THAT_ITEM; {
if (no_space_count)
*no_space_count = count;
return EQUIP_ERR_ALREADY_LOOTED;
}
if (pItem->IsBindedNotWith(this))
{
if (no_space_count)
*no_space_count = count;
return EQUIP_ERR_DONT_OWN_THAT_ITEM;
}
} }
// check count of items (skip for auto move for same player from bank) // check count of items (skip for auto move for same player from bank)
@@ -10542,6 +10531,10 @@ uint8 Player::CanStoreItems(Item **pItems,int count) const
if (!pProto) if (!pProto)
return EQUIP_ERR_ITEM_NOT_FOUND; return EQUIP_ERR_ITEM_NOT_FOUND;
// item used
if (pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
// item it 'bind' // item it 'bind'
if (pItem->IsBindedNotWith(this)) if (pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
@@ -10561,8 +10554,8 @@ uint8 Player::CanStoreItems(Item **pItems,int count) const
for (int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; ++t) for (int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; ++t)
{ {
pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t); pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
if (pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize())
{ {
inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount(); inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount();
b_found = true; b_found = true;
@@ -10574,7 +10567,7 @@ uint8 Player::CanStoreItems(Item **pItems,int count) const
for (int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t) for (int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t)
{ {
pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t); pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t);
if (pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_tokens[t-CURRENCYTOKEN_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_tokens[t-CURRENCYTOKEN_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize())
{ {
inv_tokens[t-CURRENCYTOKEN_SLOT_START] += pItem->GetCount(); inv_tokens[t-CURRENCYTOKEN_SLOT_START] += pItem->GetCount();
b_found = true; b_found = true;
@@ -10586,7 +10579,7 @@ uint8 Player::CanStoreItems(Item **pItems,int count) const
for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t) for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t)
{ {
pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t); pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t);
if (pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->GetMaxStackSize())
{ {
inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount(); inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount();
b_found = true; b_found = true;
@@ -10603,7 +10596,7 @@ uint8 Player::CanStoreItems(Item **pItems,int count) const
for (uint32 j = 0; j < pBag->GetBagSize(); j++) for (uint32 j = 0; j < pBag->GetBagSize(); j++)
{ {
pItem2 = GetItemByPos(t, j); pItem2 = GetItemByPos(t, j);
if (pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->GetMaxStackSize()) if( pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->GetMaxStackSize())
{ {
inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount(); inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount();
b_found = true; b_found = true;
@@ -10745,6 +10738,10 @@ uint8 Player::CanEquipItem(uint8 slot, uint16 &dest, Item *pItem, bool swap, boo
ItemPrototype const *pProto = pItem->GetProto(); ItemPrototype const *pProto = pItem->GetProto();
if (pProto) if (pProto)
{ {
// item used
if (pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
if (pItem->IsBindedNotWith(this)) if (pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
@@ -10888,6 +10885,10 @@ uint8 Player::CanUnequipItem(uint16 pos, bool swap) const
if (!pProto) if (!pProto)
return EQUIP_ERR_ITEM_NOT_FOUND; return EQUIP_ERR_ITEM_NOT_FOUND;
// item used
if (pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
// do not allow unequipping gear except weapons, offhands, projectiles, relics in // do not allow unequipping gear except weapons, offhands, projectiles, relics in
// - combat // - combat
// - in-progress arenas // - in-progress arenas
@@ -10912,12 +10913,6 @@ uint8 Player::CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pI
if (!pItem) if (!pItem)
return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
if (pItem->m_lootGenerated)
{
GetSession()->DoLootRelease(GetLootGUID());
return EQUIP_ERR_OK;
}
uint32 count = pItem->GetCount(); uint32 count = pItem->GetCount();
sLog.outDebug("STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount()); sLog.outDebug("STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount());
@@ -10925,6 +10920,10 @@ uint8 Player::CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pI
if (!pProto) if (!pProto)
return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
// item used
if (pItem->m_lootGenerated)
return EQUIP_ERR_ALREADY_LOOTED;
if (pItem->IsBindedNotWith(this)) if (pItem->IsBindedNotWith(this))
return EQUIP_ERR_DONT_OWN_THAT_ITEM; return EQUIP_ERR_DONT_OWN_THAT_ITEM;
@@ -12061,6 +12060,13 @@ void Player::SplitItem(uint16 src, uint16 dst, uint32 count)
return; return;
} }
if (pSrcItem->m_lootGenerated) // prevent split looting item (item
{
//best error message found for attempting to split while looting
SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL);
return;
}
// not let split all items (can be only at cheating) // not let split all items (can be only at cheating)
if (pSrcItem->GetCount() == count) if (pSrcItem->GetCount() == count)
{ {
@@ -12075,13 +12081,6 @@ void Player::SplitItem(uint16 src, uint16 dst, uint32 count)
return; return;
} }
if (pSrcItem->m_lootGenerated) // prevent split looting item (item
{
//best error message found for attempting to split while looting
SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL);
return;
}
sLog.outDebug("STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count); sLog.outDebug("STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
Item *pNewItem = pSrcItem->CloneItem(count, this); Item *pNewItem = pSrcItem->CloneItem(count, this);
if (!pNewItem) if (!pNewItem)