TrinityCore
LootItemStorage.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "DatabaseEnv.h"
19#include "Item.h"
20#include "ItemTemplate.h"
21#include "Log.h"
22#include "Loot.h"
23#include "LootItemStorage.h"
24#include "LootMgr.h"
25#include "ObjectMgr.h"
26#include "Player.h"
27#include "StringConvert.h"
28#include <sstream>
29#include <unordered_map>
30
31namespace
32{
33 std::unordered_map<uint64, StoredLootContainer> _lootItemStore;
34}
35
36StoredLootItem::StoredLootItem(LootItem const& lootItem) : ItemId(lootItem.itemid), Count(lootItem.count), ItemIndex(lootItem.LootListId), FollowRules(lootItem.follow_loot_rules),
37FFA(lootItem.freeforall), Blocked(lootItem.is_blocked), Counted(lootItem.is_counted), UnderThreshold(lootItem.is_underthreshold),
38NeedsQuest(lootItem.needs_quest), RandomBonusListId(lootItem.randomBonusListId), Context(lootItem.context), BonusListIDs(lootItem.BonusListIDs)
39{
40}
41
43{
45 return &instance;
46}
47
48std::shared_mutex* LootItemStorage::GetLock()
49{
50 static std::shared_mutex _lock;
51 return &_lock;
52}
53
55{
56 uint32 oldMSTime = getMSTime();
57 _lootItemStore.clear();
58 uint32 count = 0;
59
62 PreparedQueryResult result = CharacterDatabase.Query(stmt);
63 if (result)
64 {
65 do
66 {
67 Field* fields = result->Fetch();
68
69 uint64 key = fields[0].GetUInt64();
70 StoredLootContainer& storedContainer = _lootItemStore.try_emplace(key, key).first->second;
71
72 LootItem lootItem;
73 lootItem.itemid = fields[1].GetUInt32();
74 lootItem.count = fields[2].GetUInt32();
75 lootItem.LootListId = fields[3].GetUInt32();
76 lootItem.follow_loot_rules = fields[4].GetBool();
77 lootItem.freeforall = fields[5].GetBool();
78 lootItem.is_blocked = fields[6].GetBool();
79 lootItem.is_counted = fields[7].GetBool();
80 lootItem.is_underthreshold = fields[8].GetBool();
81 lootItem.needs_quest = fields[9].GetBool();
82 lootItem.randomBonusListId = fields[10].GetUInt32();
83 lootItem.context = ItemContext(fields[11].GetUInt8());
84 for (std::string_view bonusList : Trinity::Tokenize(fields[12].GetStringView(), ' ', false))
85 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(bonusList))
86 lootItem.BonusListIDs.push_back(*bonusListID);
87
88 storedContainer.AddLootItem(lootItem, trans);
89
90 ++count;
91 } while (result->NextRow());
92
93 TC_LOG_INFO("server.loading", ">> Loaded {} stored item loots in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
94 }
95 else
96 TC_LOG_INFO("server.loading", ">> Loaded 0 stored item loots");
97
98 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_MONEY);
99 result = CharacterDatabase.Query(stmt);
100 if (result)
101 {
102 count = 0;
103 do
104 {
105 Field* fields = result->Fetch();
106
107 uint64 key = fields[0].GetUInt64();
108 StoredLootContainer& storedContainer = _lootItemStore.try_emplace(key, key).first->second;
109 storedContainer.AddMoney(fields[1].GetUInt32(), trans);
110
111 ++count;
112 } while (result->NextRow());
113
114 TC_LOG_INFO("server.loading", ">> Loaded {} stored item money in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
115 }
116 else
117 TC_LOG_INFO("server.loading", ">> Loaded 0 stored item money");
118}
119
121{
122 StoredLootContainer const* container = nullptr;
123
124 // read
125 {
126 std::shared_lock<std::shared_mutex> lock(*GetLock());
127
128 auto itr = _lootItemStore.find(item->GetGUID().GetCounter());
129 if (itr == _lootItemStore.end())
130 return false;
131
132 container = &itr->second;
133 }
134
135 // container is never null at this point
136 Loot* loot = new Loot(player->GetMap(), item->GetGUID(), LOOT_ITEM, nullptr);
137 loot->gold = container->GetMoney();
138
139 if (LootTemplate const* lt = LootTemplates_Item.GetLootFor(item->GetEntry()))
140 {
141 for (auto const& storedItemPair : container->GetLootItems())
142 {
143 LootItem li;
144 li.itemid = storedItemPair.first;
145 li.count = storedItemPair.second.Count;
146 li.LootListId = storedItemPair.second.ItemIndex;
147 li.follow_loot_rules = storedItemPair.second.FollowRules;
148 li.freeforall = storedItemPair.second.FFA;
149 li.is_blocked = storedItemPair.second.Blocked;
150 li.is_counted = storedItemPair.second.Counted;
151 li.is_underthreshold = storedItemPair.second.UnderThreshold;
152 li.needs_quest = storedItemPair.second.NeedsQuest;
153 li.randomBonusListId = storedItemPair.second.RandomBonusListId;
154 li.context = storedItemPair.second.Context;
155 li.BonusListIDs = storedItemPair.second.BonusListIDs;
156
157 // Copy the extra loot conditions from the item in the loot template
158 lt->CopyConditions(&li);
159
160 // If container item is in a bag, add that player as an allowed looter
161 if (item->GetBagSlot())
162 li.AddAllowedLooter(player);
163
164 // Finally add the LootItem to the container
165 loot->items.push_back(li);
166
167 // Increment unlooted count
168 ++loot->unlootedCount;
169 }
170 }
171
172 if (!loot->items.empty())
173 {
174 std::sort(loot->items.begin(), loot->items.end(), [](LootItem const& left, LootItem const& right) { return left.LootListId < right.LootListId; });
175
176 uint32 lootListId = 0;
177 // add dummy loot items to ensure items are indexable by their LootListId
178 while (loot->items.size() <= loot->items.back().LootListId)
179 {
180 if (loot->items[lootListId].LootListId != lootListId)
181 {
182 auto li = loot->items.emplace(loot->items.begin() + lootListId);
183 li->LootListId = lootListId;
184 li->is_looted = true;
185 }
186
187 ++lootListId;
188 }
189 }
190
191 // Mark the item if it has loot so it won't be generated again on open
192 item->m_loot.reset(loot);
193 item->m_lootGenerated = true;
194 return true;
195}
196
198{
199 // write
200 std::unique_lock<std::shared_mutex> lock(*GetLock());
201
202 auto itr = _lootItemStore.find(containerId);
203 if (itr == _lootItemStore.end())
204 return;
205
206 itr->second.RemoveMoney();
207}
208
210{
211 // write
212 {
213 std::unique_lock<std::shared_mutex> lock(*GetLock());
214 _lootItemStore.erase(containerId);
215 }
216
217 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
219 stmt->setUInt64(0, containerId);
220 trans->Append(stmt);
221
222 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY);
223 stmt->setUInt64(0, containerId);
224 trans->Append(stmt);
225
226 CharacterDatabase.CommitTransaction(trans);
227}
228
230{
231 // write
232 std::unique_lock<std::shared_mutex> lock(*GetLock());
233
234 auto itr = _lootItemStore.find(containerId);
235 if (itr == _lootItemStore.end())
236 return;
237
238 itr->second.RemoveItem(itemId, count, itemIndex);
239}
240
241void LootItemStorage::AddNewStoredLoot(uint64 containerId, Loot* loot, Player* player)
242{
243 // Saves the money and item loot associated with an openable item to the DB
244 if (loot->isLooted()) // no money and no loot
245 return;
246
247 // read
248 {
249 std::shared_lock<std::shared_mutex> lock(*GetLock());
250
251 auto itr = _lootItemStore.find(containerId);
252 if (itr != _lootItemStore.end())
253 {
254 TC_LOG_ERROR("misc", "Trying to store item loot by player: {} for container id: {} that is already in storage!", player->GetGUID().ToString(), containerId);
255 return;
256 }
257 }
258
259 StoredLootContainer container(containerId);
260
261 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
262 if (loot->gold)
263 container.AddMoney(loot->gold, trans);
264
266 stmt->setUInt64(0, containerId);
267 trans->Append(stmt);
268
269 for (LootItem const& li : loot->items)
270 {
271 // Conditions are not checked when loot is generated, it is checked when loot is sent to a player.
272 // For items that are lootable, loot is saved to the DB immediately, that means that loot can be
273 // saved to the DB that the player never should have gotten. This check prevents that, so that only
274 // items that the player should get in loot are in the DB.
275 // IE: Horde items are not saved to the DB for Ally players.
276 if (!li.AllowedForPlayer(player, loot))
277 continue;
278
279 // Don't save currency tokens
280 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(li.itemid);
281 if (!itemTemplate || itemTemplate->IsCurrencyToken())
282 continue;
283
284 container.AddLootItem(li, trans);
285 }
286
287 CharacterDatabase.CommitTransaction(trans);
288
289 // write
290 {
291 std::unique_lock<std::shared_mutex> lock(*GetLock());
292 _lootItemStore.emplace(containerId, std::move(container));
293 }
294}
295
297{
298 _lootItems.emplace(std::piecewise_construct, std::forward_as_tuple(lootItem.itemid), std::forward_as_tuple(lootItem));
299 if (!trans)
300 return;
301
303
304 // container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix
305 stmt->setUInt64(0, _containerId);
306 stmt->setUInt32(1, lootItem.itemid);
307 stmt->setUInt32(2, lootItem.count);
308 stmt->setUInt32(3, lootItem.LootListId);
309 stmt->setBool(4, lootItem.follow_loot_rules);
310 stmt->setBool(5, lootItem.freeforall);
311 stmt->setBool(6, lootItem.is_blocked);
312 stmt->setBool(7, lootItem.is_counted);
313 stmt->setBool(8, lootItem.is_underthreshold);
314 stmt->setBool(9, lootItem.needs_quest);
315 stmt->setInt32(10, lootItem.randomBonusListId);
316 stmt->setUInt8(11, AsUnderlyingType(lootItem.context));
317 std::ostringstream bonusListIDs;
318 for (int32 bonusListID : lootItem.BonusListIDs)
319 bonusListIDs << bonusListID << ' ';
320 stmt->setString(12, bonusListIDs.str());
321 trans->Append(stmt);
322}
323
325{
326 _money = money;
327 if (!trans)
328 return;
329
331 stmt->setUInt64(0, _containerId);
332 trans->Append(stmt);
333
334 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_MONEY);
335 stmt->setUInt64(0, _containerId);
336 stmt->setUInt32(1, _money);
337 trans->Append(stmt);
338}
339
341{
342 _money = 0;
343
345 stmt->setUInt64(0, _containerId);
346 CharacterDatabase.Execute(stmt);
347}
348
350{
351 auto bounds = _lootItems.equal_range(itemId);
352 for (auto itr = bounds.first; itr != bounds.second; ++itr)
353 {
354 if (itr->second.ItemIndex == itemIndex)
355 {
356 _lootItems.erase(itr);
357 break;
358 }
359 }
360
361 // Deletes a single item associated with an openable item from the DB
363 stmt->setUInt64(0, _containerId);
364 stmt->setUInt32(1, itemId);
365 stmt->setUInt32(2, count);
366 stmt->setUInt32(3, itemIndex);
367 CharacterDatabase.Execute(stmt);
368}
@ CHAR_DEL_ITEMCONTAINER_MONEY
@ CHAR_DEL_ITEMCONTAINER_ITEM
@ CHAR_DEL_ITEMCONTAINER_ITEMS
@ CHAR_SEL_ITEMCONTAINER_ITEMS
@ CHAR_INS_ITEMCONTAINER_ITEMS
@ CHAR_INS_ITEMCONTAINER_MONEY
@ CHAR_SEL_ITEMCONTAINER_MONEY
ItemContext
Definition: DBCEnums.h:1063
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint32_t uint32
Definition: Define.h:142
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
LootStore LootTemplates_Item("item_loot_template", "item entry", true)
@ LOOT_ITEM
Definition: Loot.h:105
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:491
Class used to access individual fields of database query result.
Definition: Field.h:90
uint64 GetUInt64() const
Definition: Field.cpp:78
bool GetBool() const
Definition: Field.h:98
uint32 GetUInt32() const
Definition: Field.cpp:62
Definition: Item.h:170
std::unique_ptr< Loot > m_loot
Definition: Item.h:317
bool m_lootGenerated
Definition: Item.h:318
uint8 GetBagSlot() const
Definition: Item.cpp:1239
void RemoveStoredLootItemForContainer(uint64 containerId, uint32 itemId, uint32 count, uint32 itemIndex)
static LootItemStorage * instance()
void RemoveStoredMoneyForContainer(uint64 containerId)
void AddNewStoredLoot(uint64 containerId, Loot *loot, Player *player)
static std::shared_mutex * GetLock()
bool LoadStoredLoot(Item *item, Player *player)
void RemoveStoredLootForContainer(uint64 containerId)
LootTemplate const * GetLootFor(uint32 loot_id) const
Definition: LootMgr.cpp:219
LowType GetCounter() const
Definition: ObjectGuid.h:293
std::string ToString() const
Definition: ObjectGuid.cpp:554
uint32 GetEntry() const
Definition: Object.h:161
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
void setInt32(const uint8 index, const int32 value)
void setUInt8(const uint8 index, const uint8 value)
void setBool(const uint8 index, const bool value)
void setUInt32(const uint8 index, const uint32 value)
void setString(const uint8 index, const std::string &value)
void setUInt64(const uint8 index, const uint64 value)
void RemoveItem(uint32 itemId, uint32 count, uint32 itemIndex)
void AddLootItem(LootItem const &lootItem, CharacterDatabaseTransaction trans)
StoredLootItemContainer _lootItems
uint64 const _containerId
void AddMoney(uint32 money, CharacterDatabaseTransaction trans)
StoredLootItemContainer const & GetLootItems() const
uint32 GetMoney() const
Map * GetMap() const
Definition: Object.h:624
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Util.cpp:56
size_t Count(ContainerMapList< TypeList< H, T > > const &elements, SPECIFIC_TYPE *fake)
bool IsCurrencyToken() const
Definition: ItemTemplate.h:845
Definition: Loot.h:176
std::vector< int32 > BonusListIDs
Definition: Loot.h:180
uint32 itemid
Definition: Loot.h:177
bool is_blocked
Definition: Loot.h:187
void AddAllowedLooter(Player const *player)
Definition: Loot.cpp:131
bool needs_quest
Definition: Loot.h:191
bool follow_loot_rules
Definition: Loot.h:192
bool is_underthreshold
Definition: Loot.h:189
bool AllowedForPlayer(Player const *player, Loot const *loot) const
Definition: Loot.cpp:72
uint32 LootListId
Definition: Loot.h:178
uint8 count
Definition: Loot.h:185
ItemRandomBonusListId randomBonusListId
Definition: Loot.h:179
ItemContext context
Definition: Loot.h:181
bool freeforall
Definition: Loot.h:188
bool is_counted
Definition: Loot.h:190
Definition: Loot.h:281
bool isLooted() const
Definition: Loot.h:307
uint8 unlootedCount
Definition: Loot.h:286
uint32 gold
Definition: Loot.h:285
std::vector< LootItem > items
Definition: Loot.h:284
StoredLootItem(LootItem const &lootItem)