TrinityCore
Loading...
Searching...
No Matches
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 "Util.h"
29#include <sstream>
30#include <unordered_map>
31
32namespace
33{
34 std::unordered_map<uint64, StoredLootContainer> _lootItemStore;
35}
36
37StoredLootItem::StoredLootItem(LootItem const& lootItem) : ItemId(lootItem.itemid), Count(lootItem.count), ItemIndex(lootItem.LootListId), FollowRules(lootItem.follow_loot_rules),
38FFA(lootItem.freeforall), Blocked(lootItem.is_blocked), Counted(lootItem.is_counted), UnderThreshold(lootItem.is_underthreshold),
39NeedsQuest(lootItem.needs_quest), RandomBonusListId(lootItem.randomBonusListId), Context(lootItem.context), BonusListIDs(lootItem.BonusListIDs)
40{
41}
42
48
49std::shared_mutex* LootItemStorage::GetLock()
50{
51 static std::shared_mutex _lock;
52 return &_lock;
53}
54
56{
57 uint32 oldMSTime = getMSTime();
58 _lootItemStore.clear();
59 uint32 count = 0;
60
63 PreparedQueryResult result = CharacterDatabase.Query(stmt);
64 if (result)
65 {
66 do
67 {
68 Field* fields = result->Fetch();
69
70 uint64 key = fields[0].GetUInt64();
71 StoredLootContainer& storedContainer = _lootItemStore.try_emplace(key, key).first->second;
72
73 LootItem lootItem;
74 lootItem.type = static_cast<LootItemType>(fields[1].GetInt8());
75 lootItem.itemid = fields[2].GetUInt32();
76 lootItem.count = fields[3].GetUInt32();
77 lootItem.LootListId = fields[4].GetUInt32();
78 lootItem.follow_loot_rules = fields[5].GetBool();
79 lootItem.freeforall = fields[6].GetBool();
80 lootItem.is_blocked = fields[7].GetBool();
81 lootItem.is_counted = fields[8].GetBool();
82 lootItem.is_underthreshold = fields[9].GetBool();
83 lootItem.needs_quest = fields[10].GetBool();
84 lootItem.randomBonusListId = fields[11].GetUInt32();
85 lootItem.context = ItemContext(fields[12].GetUInt8());
86 for (std::string_view bonusList : Trinity::Tokenize(fields[13].GetStringView(), ' ', false))
87 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(bonusList))
88 lootItem.BonusListIDs.push_back(*bonusListID);
89
90 storedContainer.AddLootItem(lootItem, trans);
91
92 ++count;
93 } while (result->NextRow());
94
95 TC_LOG_INFO("server.loading", ">> Loaded {} stored item loots in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
96 }
97 else
98 TC_LOG_INFO("server.loading", ">> Loaded 0 stored item loots");
99
100 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_MONEY);
101 result = CharacterDatabase.Query(stmt);
102 if (result)
103 {
104 count = 0;
105 do
106 {
107 Field* fields = result->Fetch();
108
109 uint64 key = fields[0].GetUInt64();
110 StoredLootContainer& storedContainer = _lootItemStore.try_emplace(key, key).first->second;
111 storedContainer.AddMoney(fields[1].GetUInt32(), trans);
112
113 ++count;
114 } while (result->NextRow());
115
116 TC_LOG_INFO("server.loading", ">> Loaded {} stored item money in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
117 }
118 else
119 TC_LOG_INFO("server.loading", ">> Loaded 0 stored item money");
120}
121
123{
124 StoredLootContainer const* container = nullptr;
125
126 // read
127 {
128 std::shared_lock lock(*GetLock());
129
130 auto itr = _lootItemStore.find(item->GetGUID().GetCounter());
131 if (itr == _lootItemStore.end())
132 return false;
133
134 container = &itr->second;
135 }
136
137 // container is never null at this point
138 Loot* loot = new Loot(player->GetMap(), item->GetGUID(), LOOT_ITEM, nullptr);
139 loot->gold = container->GetMoney();
140
141 if (LootTemplate const* lt = LootTemplates_Item.GetLootFor(item->GetEntry()))
142 {
143 for (auto const& storedItemPair : container->GetLootItems())
144 {
145 LootItem li;
146 li.itemid = storedItemPair.first;
147 li.count = storedItemPair.second.Count;
148 li.LootListId = storedItemPair.second.ItemIndex;
149 li.follow_loot_rules = storedItemPair.second.FollowRules;
150 li.freeforall = storedItemPair.second.FFA;
151 li.is_blocked = storedItemPair.second.Blocked;
152 li.is_counted = storedItemPair.second.Counted;
153 li.is_underthreshold = storedItemPair.second.UnderThreshold;
154 li.needs_quest = storedItemPair.second.NeedsQuest;
155 li.randomBonusListId = storedItemPair.second.RandomBonusListId;
156 li.context = storedItemPair.second.Context;
157 li.BonusListIDs = storedItemPair.second.BonusListIDs;
158
159 // Copy the extra loot conditions from the item in the loot template
160 lt->CopyConditions(&li);
161
162 // If container item is in a bag, add that player as an allowed looter
163 if (item->GetBagSlot())
164 li.AddAllowedLooter(player);
165
166 // Finally add the LootItem to the container
167 loot->items.push_back(li);
168
169 // Increment unlooted count
170 ++loot->unlootedCount;
171 }
172 }
173
174 if (!loot->items.empty())
175 {
176 std::sort(loot->items.begin(), loot->items.end(), [](LootItem const& left, LootItem const& right) { return left.LootListId < right.LootListId; });
177
178 uint32 lootListId = 0;
179 // add dummy loot items to ensure items are indexable by their LootListId
180 while (loot->items.size() <= loot->items.back().LootListId)
181 {
182 if (loot->items[lootListId].LootListId != lootListId)
183 {
184 auto li = loot->items.emplace(loot->items.begin() + lootListId);
185 li->LootListId = lootListId;
186 li->is_looted = true;
187 }
188
189 ++lootListId;
190 }
191 }
192
193 // Mark the item if it has loot so it won't be generated again on open
194 item->m_loot.reset(loot);
195 item->m_lootGenerated = true;
196 return true;
197}
198
200{
201 // write
202 std::scoped_lock lock(*GetLock());
203
204 auto itr = _lootItemStore.find(containerId);
205 if (itr == _lootItemStore.end())
206 return;
207
208 itr->second.RemoveMoney();
209}
210
212{
213 // write
214 {
215 std::scoped_lock lock(*GetLock());
216 _lootItemStore.erase(containerId);
217 }
218
219 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
221 stmt->setUInt64(0, containerId);
222 trans->Append(stmt);
223
224 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY);
225 stmt->setUInt64(0, containerId);
226 trans->Append(stmt);
227
228 CharacterDatabase.CommitTransaction(trans);
229}
230
232{
233 // write
234 std::scoped_lock lock(*GetLock());
235
236 auto itr = _lootItemStore.find(containerId);
237 if (itr == _lootItemStore.end())
238 return;
239
240 itr->second.RemoveItem(type, itemId, count, itemIndex);
241}
242
243void LootItemStorage::AddNewStoredLoot(uint64 containerId, Loot* loot, Player* player)
244{
245 // Saves the money and item loot associated with an openable item to the DB
246 if (loot->isLooted()) // no money and no loot
247 return;
248
249 // read
250 {
251 std::shared_lock lock(*GetLock());
252
253 auto itr = _lootItemStore.find(containerId);
254 if (itr != _lootItemStore.end())
255 {
256 TC_LOG_ERROR("misc", "Trying to store item loot by player: {} for container id: {} that is already in storage!", player->GetGUID().ToString(), containerId);
257 return;
258 }
259 }
260
261 StoredLootContainer container(containerId);
262
263 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
264 if (loot->gold)
265 container.AddMoney(loot->gold, trans);
266
268 stmt->setUInt64(0, containerId);
269 trans->Append(stmt);
270
271 for (LootItem const& li : loot->items)
272 {
273 // Conditions are not checked when loot is generated, it is checked when loot is sent to a player.
274 // For items that are lootable, loot is saved to the DB immediately, that means that loot can be
275 // saved to the DB that the player never should have gotten. This check prevents that, so that only
276 // items that the player should get in loot are in the DB.
277 // IE: Horde items are not saved to the DB for Ally players.
278 if (!li.AllowedForPlayer(player, loot))
279 continue;
280
281 // Don't save currency tokens
282 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(li.itemid);
283 if (!itemTemplate || itemTemplate->IsCurrencyToken())
284 continue;
285
286 container.AddLootItem(li, trans);
287 }
288
289 CharacterDatabase.CommitTransaction(trans);
290
291 // write
292 {
293 std::scoped_lock lock(*GetLock());
294 _lootItemStore.emplace(containerId, std::move(container));
295 }
296}
297
299{
300 _lootItems.emplace(std::piecewise_construct, std::forward_as_tuple(lootItem.itemid), std::forward_as_tuple(lootItem));
301 if (!trans)
302 return;
303
305
306 // container_id, item_type, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix
307 stmt->setUInt64(0, _containerId);
308 stmt->setInt8(1, AsUnderlyingType(lootItem.type));
309 stmt->setUInt32(2, lootItem.itemid);
310 stmt->setUInt32(3, lootItem.count);
311 stmt->setUInt32(4, lootItem.LootListId);
312 stmt->setBool(5, lootItem.follow_loot_rules);
313 stmt->setBool(6, lootItem.freeforall);
314 stmt->setBool(7, lootItem.is_blocked);
315 stmt->setBool(8, lootItem.is_counted);
316 stmt->setBool(9, lootItem.is_underthreshold);
317 stmt->setBool(10, lootItem.needs_quest);
318 stmt->setInt32(11, lootItem.randomBonusListId);
319 stmt->setUInt8(13, AsUnderlyingType(lootItem.context));
320 std::ostringstream bonusListIDs;
321 for (int32 bonusListID : lootItem.BonusListIDs)
322 bonusListIDs << bonusListID << ' ';
323 stmt->setString(13, bonusListIDs.str());
324 trans->Append(stmt);
325}
326
328{
329 _money = money;
330 if (!trans)
331 return;
332
334 stmt->setUInt64(0, _containerId);
335 trans->Append(stmt);
336
337 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_MONEY);
338 stmt->setUInt64(0, _containerId);
339 stmt->setUInt32(1, _money);
340 trans->Append(stmt);
341}
342
344{
345 _money = 0;
346
348 stmt->setUInt64(0, _containerId);
349 CharacterDatabase.Execute(stmt);
350}
351
353{
354 auto bounds = _lootItems.equal_range(itemId);
355 for (auto itr = bounds.first; itr != bounds.second; ++itr)
356 {
357 if (itr->second.ItemIndex == itemIndex)
358 {
359 _lootItems.erase(itr);
360 break;
361 }
362 }
363
364 // Deletes a single item associated with an openable item from the DB
366 stmt->setUInt64(0, _containerId);
367 stmt->setInt8(1, AsUnderlyingType(type));
368 stmt->setUInt32(2, itemId);
369 stmt->setUInt32(3, count);
370 stmt->setUInt32(4, itemIndex);
371 CharacterDatabase.Execute(stmt);
372}
@ 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:1315
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
int32_t int32
Definition Define.h:150
uint64_t uint64
Definition Define.h:153
uint32_t uint32
Definition Define.h:154
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
LootItemType
LootStore LootTemplates_Item("item_loot_template", "item entry", true)
@ LOOT_ITEM
Definition Loot.h:106
#define sObjectMgr
Definition ObjectMgr.h:1885
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:565
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
Class used to access individual fields of database query result.
Definition Field.h:94
uint64 GetUInt64() const noexcept
Definition Field.cpp:71
bool GetBool() const noexcept
Definition Field.h:102
uint32 GetUInt32() const noexcept
Definition Field.cpp:57
int8 GetInt8() const noexcept
Definition Field.cpp:36
Definition Item.h:179
std::unique_ptr< Loot > m_loot
Definition Item.h:328
bool m_lootGenerated
Definition Item.h:329
uint8 GetBagSlot() const
Definition Item.cpp:1331
static LootItemStorage * instance()
void RemoveStoredMoneyForContainer(uint64 containerId)
void AddNewStoredLoot(uint64 containerId, Loot *loot, Player *player)
static std::shared_mutex * GetLock()
void RemoveStoredLootItemForContainer(uint64 containerId, LootItemType type, uint32 itemId, uint32 count, uint32 itemIndex)
bool LoadStoredLoot(Item *item, Player *player)
void RemoveStoredLootForContainer(uint64 containerId)
LootTemplate const * GetLootFor(uint32 loot_id) const
Definition LootMgr.cpp:207
LowType GetCounter() const
Definition ObjectGuid.h:336
std::string ToString() const
uint32 GetEntry() const
Definition Object.h:89
void setInt8(uint8 index, int8 value)
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
void setBool(uint8 index, bool value)
void setUInt64(uint8 index, uint64 value)
void setInt32(uint8 index, int32 value)
void setUInt8(uint8 index, uint8 value)
void AddLootItem(LootItem const &lootItem, CharacterDatabaseTransaction trans)
StoredLootItemContainer _lootItems
uint64 const _containerId
void AddMoney(uint32 money, CharacterDatabaseTransaction trans)
void RemoveItem(LootItemType type, uint32 itemId, uint32 count, uint32 itemIndex)
StoredLootItemContainer const & GetLootItems() const
uint32 GetMoney() const
Map * GetMap() const
Definition Object.h:411
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition Util.cpp:57
bool IsCurrencyToken() const
std::vector< int32 > BonusListIDs
Definition Loot.h:181
uint32 itemid
Definition Loot.h:178
bool is_blocked
Definition Loot.h:189
void AddAllowedLooter(Player const *player)
Definition Loot.cpp:200
bool needs_quest
Definition Loot.h:193
bool follow_loot_rules
Definition Loot.h:194
bool is_underthreshold
Definition Loot.h:191
uint32 count
Definition Loot.h:186
uint32 LootListId
Definition Loot.h:179
ItemRandomBonusListId randomBonusListId
Definition Loot.h:180
ItemContext context
Definition Loot.h:182
LootItemType type
Definition Loot.h:187
bool freeforall
Definition Loot.h:190
bool is_counted
Definition Loot.h:192
Definition Loot.h:286
bool isLooted() const
Definition Loot.h:312
uint8 unlootedCount
Definition Loot.h:291
uint32 gold
Definition Loot.h:290
std::vector< LootItem > items
Definition Loot.h:289
StoredLootItem(LootItem const &lootItem)