From a3e3fd029fa01c26b12e7789c9e149b798c7e14a Mon Sep 17 00:00:00 2001 From: robinsch Date: Wed, 21 Jul 2021 12:05:12 +0200 Subject: [PATCH] fix(Core/Item): prevent possible items dupe (#6943) --- .../rev_1626271703991541000.sql | 3 +++ .../Implementation/CharacterDatabase.cpp | 6 +++--- src/server/game/Entities/Player/Player.cpp | 2 +- src/server/game/Loot/LootItemStorage.cpp | 20 +++++++++++-------- src/server/game/Loot/LootItemStorage.h | 11 +++++----- src/server/game/Loot/LootMgr.cpp | 4 +++- src/server/game/Loot/LootMgr.h | 1 + 7 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 data/sql/updates/pending_db_characters/rev_1626271703991541000.sql diff --git a/data/sql/updates/pending_db_characters/rev_1626271703991541000.sql b/data/sql/updates/pending_db_characters/rev_1626271703991541000.sql new file mode 100644 index 000000000..a7f7de989 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1626271703991541000.sql @@ -0,0 +1,3 @@ +INSERT INTO `version_db_characters` (`sql_rev`) VALUES ('1626271703991541000'); +ALTER TABLE `item_loot_storage` + ADD COLUMN `item_index` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `count`; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 9009b99c1..760776a88 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -510,9 +510,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC); // Items that hold loot or money - PrepareStatement(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT containerGUID, itemid, count, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest, conditionLootId FROM item_loot_storage", CONNECTION_SYNCH); - PrepareStatement(CHAR_DEL_ITEMCONTAINER_SINGLE_ITEM, "DELETE FROM item_loot_storage WHERE containerGUID = ? AND itemid = ? AND count = ? LIMIT 1", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM, "INSERT INTO item_loot_storage (containerGUID, itemid, count, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest, conditionLootId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT containerGUID, itemid, count, item_index, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest, conditionLootId FROM item_loot_storage", CONNECTION_SYNCH); + PrepareStatement(CHAR_DEL_ITEMCONTAINER_SINGLE_ITEM, "DELETE FROM item_loot_storage WHERE containerGUID = ? AND itemid = ? AND count = ? AND item_index = ? LIMIT 1", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM, "INSERT INTO item_loot_storage (containerGUID, itemid, item_index, count, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest, conditionLootId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ITEMCONTAINER_CONTAINER, "DELETE FROM item_loot_storage WHERE containerGUID = ?", CONNECTION_ASYNC); // Calendar diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 4537fe67a..c2bd33ea1 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -12616,7 +12616,7 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot) // LootItem is being removed (looted) from the container, delete it from the DB. if (loot->containerGUID) - sLootItemStorage->RemoveStoredLootItem(loot->containerGUID, item->itemid, item->count, loot); + sLootItemStorage->RemoveStoredLootItem(loot->containerGUID, item->itemid, item->count, loot, item->itemIndex); #ifdef ELUNA sEluna->OnLootItem(this, newitem, item->count, this->GetLootGUID()); diff --git a/src/server/game/Loot/LootItemStorage.cpp b/src/server/game/Loot/LootItemStorage.cpp index 3edda5a09..5d736e48f 100644 --- a/src/server/game/Loot/LootItemStorage.cpp +++ b/src/server/game/Loot/LootItemStorage.cpp @@ -41,8 +41,8 @@ void LootItemStorage::LoadStorageFromDB() Field* fields = result->Fetch(); StoredLootItemList& itemList = lootItemStore[ObjectGuid::Create(fields[0].GetUInt32())]; - itemList.push_back(StoredLootItem(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetInt32(), fields[4].GetUInt32(), fields[5].GetBool(), - fields[6].GetBool(), fields[7].GetBool(), fields[8].GetBool(), fields[9].GetBool(), fields[10].GetBool(), fields[11].GetUInt32())); + itemList.push_back(StoredLootItem(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[3].GetUInt32(), fields[4].GetInt32(), fields[5].GetUInt32(), fields[6].GetBool(), + fields[7].GetBool(), fields[8].GetBool(), fields[9].GetBool(), fields[10].GetBool(), fields[11].GetBool(), fields[12].GetUInt32())); ++count; } while (result->NextRow()); @@ -51,7 +51,7 @@ void LootItemStorage::LoadStorageFromDB() LOG_INFO("server.loading", " "); } -void LootItemStorage::RemoveEntryFromDB(ObjectGuid containerGUID, uint32 itemid, uint32 count) +void LootItemStorage::RemoveEntryFromDB(ObjectGuid containerGUID, uint32 itemid, uint32 count, uint32 itemIndex) { CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); @@ -59,6 +59,7 @@ void LootItemStorage::RemoveEntryFromDB(ObjectGuid containerGUID, uint32 itemid, stmt->setUInt32(0, containerGUID.GetCounter()); stmt->setUInt32(1, itemid); stmt->setUInt32(2, count); + stmt->setUInt32(3, itemIndex); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); @@ -80,12 +81,13 @@ void LootItemStorage::AddNewStoredLoot(Loot* loot, Player* /*player*/) // Gold at first if (loot->gold) { - itemList.push_back(StoredLootItem(0, loot->gold, 0, 0, false, false, false, false, false, false, 0)); + itemList.push_back(StoredLootItem(0, 0, loot->gold, 0, 0, false, false, false, false, false, false, 0)); uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM); stmt->setUInt32(index++, loot->containerGUID.GetCounter()); stmt->setUInt32(index++, 0); + stmt->setUInt32(index++, 0); stmt->setUInt32(index++, loot->gold); stmt->setInt32(index++, 0); stmt->setUInt32(index++, 0); @@ -118,13 +120,14 @@ void LootItemStorage::AddNewStoredLoot(Loot* loot, Player* /*player*/) conditionLootId = li->conditions.front()->SourceGroup; } - itemList.push_back(StoredLootItem(li->itemid, li->count, li->randomPropertyId, li->randomSuffix, li->follow_loot_rules, li->freeforall, li->is_blocked, li->is_counted, + itemList.push_back(StoredLootItem(li->itemid, li->itemIndex, li->count, li->randomPropertyId, li->randomSuffix, li->follow_loot_rules, li->freeforall, li->is_blocked, li->is_counted, li->is_underthreshold, li->needs_quest, conditionLootId)); uint8 index = 0; stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM); stmt->setUInt32(index++, loot->containerGUID.GetCounter()); stmt->setUInt32(index++, li->itemid); + stmt->setUInt32(index++, li->itemIndex); stmt->setUInt32(index++, li->count); stmt->setInt32 (index++, li->randomPropertyId); stmt->setUInt32(index++, li->randomSuffix); @@ -168,6 +171,7 @@ bool LootItemStorage::LoadStoredLoot(Item* item, Player* player) { LootItem li; li.itemid = it2->itemid; + li.itemIndex = it2->itemIndex; li.count = it2->count; li.follow_loot_rules = it2->follow_loot_rules; li.freeforall = it2->freeforall; @@ -212,7 +216,7 @@ bool LootItemStorage::LoadStoredLoot(Item* item, Player* player) return true; } -void LootItemStorage::RemoveStoredLootItem(ObjectGuid containerGUID, uint32 itemid, uint32 count, Loot* loot) +void LootItemStorage::RemoveStoredLootItem(ObjectGuid containerGUID, uint32 itemid, uint32 count, Loot* loot, uint32 itemIndex) { LootItemContainer::iterator itr = lootItemStore.find(containerGUID); if (itr == lootItemStore.end()) @@ -222,7 +226,7 @@ void LootItemStorage::RemoveStoredLootItem(ObjectGuid containerGUID, uint32 item for (StoredLootItemList::iterator it2 = itemList.begin(); it2 != itemList.end(); ++it2) if (it2->itemid == itemid && it2->count == count) { - RemoveEntryFromDB(containerGUID, itemid, count); + RemoveEntryFromDB(containerGUID, itemid, count, itemIndex); itemList.erase(it2); break; } @@ -243,7 +247,7 @@ void LootItemStorage::RemoveStoredLootMoney(ObjectGuid containerGUID, Loot* loot for (StoredLootItemList::iterator it2 = itemList.begin(); it2 != itemList.end(); ++it2) if (it2->itemid == 0) { - RemoveEntryFromDB(containerGUID, 0, it2->count); + RemoveEntryFromDB(containerGUID, 0, it2->count, 0); itemList.erase(it2); break; } diff --git a/src/server/game/Loot/LootItemStorage.h b/src/server/game/Loot/LootItemStorage.h index 0e982822f..ec6862ad6 100644 --- a/src/server/game/Loot/LootItemStorage.h +++ b/src/server/game/Loot/LootItemStorage.h @@ -13,13 +13,14 @@ Xinef struct StoredLootItem { - StoredLootItem(uint32 i, uint32 c, int32 ri, uint32 rs, bool follow_loot_rules, bool freeforall, - bool is_blocked, bool is_counted, bool is_underthreshold, bool needs_quest, uint32 conditionLootId) : - itemid(i), count(c), randomPropertyId(ri), randomSuffix(rs), follow_loot_rules(follow_loot_rules), freeforall(freeforall), is_blocked(is_blocked), + StoredLootItem(uint32 i, uint32 idx, uint32 c, int32 ri, uint32 rs, bool follow_loot_rules, bool freeforall, + bool is_blocked, bool is_counted, bool is_underthreshold, bool needs_quest, uint32 conditionLootId) : itemid(i), itemIndex(idx), + count(c), randomPropertyId(ri), randomSuffix(rs), follow_loot_rules(follow_loot_rules), freeforall(freeforall), is_blocked(is_blocked), is_counted(is_counted), is_underthreshold(is_underthreshold), needs_quest(needs_quest), conditionLootId(conditionLootId) { } // If itemid == 0 - money amount is stored in count value uint32 itemid; + uint32 itemIndex; uint32 count; int32 randomPropertyId; uint32 randomSuffix; @@ -45,12 +46,12 @@ public: static LootItemStorage* instance(); void LoadStorageFromDB(); - void RemoveEntryFromDB(ObjectGuid containerGUID, uint32 itemid, uint32 count); + void RemoveEntryFromDB(ObjectGuid containerGUID, uint32 itemid, uint32 count, uint32 itemIndex); void AddNewStoredLoot(Loot* loot, Player* player); bool LoadStoredLoot(Item* item, Player* player); - void RemoveStoredLootItem(ObjectGuid containerGUID, uint32 itemid, uint32 count, Loot* loot); + void RemoveStoredLootItem(ObjectGuid containerGUID, uint32 itemid, uint32 count, Loot* loot, uint32 itemIndex); void RemoveStoredLootMoney(ObjectGuid containerGUID, Loot* loot); void RemoveStoredLoot(ObjectGuid containerGUID); diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 733023fc0..752cb35c5 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -368,7 +368,8 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const // Constructor, copies most fields from LootStoreItem and generates random count LootItem::LootItem(LootStoreItem const& li) { - itemid = li.itemid; + itemid = li.itemid; + itemIndex = 0; conditions = li.conditions; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); @@ -466,6 +467,7 @@ void Loot::AddItem(LootStoreItem const& item) { LootItem generatedLoot(item); generatedLoot.count = std::min(count, proto->GetMaxStackSize()); + generatedLoot.itemIndex = lootItems.size(); lootItems.push_back(generatedLoot); count -= proto->GetMaxStackSize(); diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 3ed2873c9..b1d6a3f14 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -143,6 +143,7 @@ typedef GuidSet AllowedLooterSet; struct LootItem { uint32 itemid; + uint32 itemIndex; uint32 randomSuffix; int32 randomPropertyId; ConditionList conditions; // additional loot condition