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 "LootItemStorage.h"
19#include "DatabaseEnv.h"
20#include "Item.h"
21#include "ItemTemplate.h"
22#include "Log.h"
23#include "Loot.h"
24#include "LootMgr.h"
25#include "ObjectMgr.h"
26#include "Player.h"
27
28#include <unordered_map>
29
30namespace
31{
32 std::unordered_map<uint32, StoredLootContainer> _lootItemStore;
33}
34
35StoredLootItem::StoredLootItem(LootItem const& lootItem) : ItemId(lootItem.itemid), Count(lootItem.count), ItemIndex(lootItem.itemIndex), FollowRules(lootItem.follow_loot_rules),
36FFA(lootItem.freeforall), Blocked(lootItem.is_blocked), Counted(lootItem.is_counted), UnderThreshold(lootItem.is_underthreshold),
37NeedsQuest(lootItem.needs_quest), RandomPropertyId(lootItem.randomPropertyId), RandomSuffix(lootItem.randomSuffix)
38{
39}
40
46
47std::shared_mutex* LootItemStorage::GetLock()
48{
49 static std::shared_mutex _lock;
50 return &_lock;
51}
52
54{
55 uint32 oldMSTime = getMSTime();
56 _lootItemStore.clear();
57 uint32 count = 0;
58
61 PreparedQueryResult result = CharacterDatabase.Query(stmt);
62 if (result)
63 {
64 do
65 {
66 Field* fields = result->Fetch();
67
68 uint32 key = fields[0].GetUInt32();
69 auto itr = _lootItemStore.find(key);
70 if (itr == _lootItemStore.end())
71 {
72 bool added;
73 std::tie(itr, added) = _lootItemStore.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(key));
74
75 ASSERT(added);
76 }
77
78 StoredLootContainer& storedContainer = itr->second;
79
80 LootItem lootItem;
81 lootItem.itemid = fields[1].GetUInt32();
82 lootItem.count = fields[2].GetUInt32();
83 lootItem.itemIndex = fields[3].GetUInt32();
84 lootItem.follow_loot_rules = fields[4].GetBool();
85 lootItem.freeforall = fields[5].GetBool();
86 lootItem.is_blocked = fields[6].GetBool();
87 lootItem.is_counted = fields[7].GetBool();
88 lootItem.is_underthreshold = fields[8].GetBool();
89 lootItem.needs_quest = fields[9].GetBool();
90 lootItem.randomPropertyId = fields[10].GetInt32();
91 lootItem.randomSuffix = fields[11].GetUInt32();
92
93 storedContainer.AddLootItem(lootItem, trans);
94
95 ++count;
96 } while (result->NextRow());
97
98 TC_LOG_INFO("server.loading", ">> Loaded {} stored item loots in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
99 }
100 else
101 TC_LOG_INFO("server.loading", ">> Loaded 0 stored item loots");
102
103 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_MONEY);
104 result = CharacterDatabase.Query(stmt);
105 if (result)
106 {
107 count = 0;
108 do
109 {
110 Field* fields = result->Fetch();
111
112 uint32 key = fields[0].GetUInt32();
113 auto itr = _lootItemStore.find(key);
114 if (itr == _lootItemStore.end())
115 {
116 bool added;
117 std::tie(itr, added) = _lootItemStore.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(key));
118
119 ASSERT(added);
120 }
121
122 StoredLootContainer& storedContainer = itr->second;
123 storedContainer.AddMoney(fields[1].GetUInt32(), trans);
124
125 ++count;
126 } while (result->NextRow());
127
128 TC_LOG_INFO("server.loading", ">> Loaded {} stored item money in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
129 }
130 else
131 TC_LOG_INFO("server.loading", ">> Loaded 0 stored item money");
132}
133
135{
136 Loot* loot = &item->loot;
137 StoredLootContainer const* container = nullptr;
138
139 // read
140 {
141 std::shared_lock<std::shared_mutex> lock(*GetLock());
142
143 auto itr = _lootItemStore.find(loot->containerID);
144 if (itr == _lootItemStore.end())
145 return false;
146
147 container = &itr->second;
148 }
149
150 // container is never null at this point
151 loot->gold = container->GetMoney();
152
153 if (LootTemplate const* lt = LootTemplates_Item.GetLootFor(item->GetEntry()))
154 {
155 for (auto const& storedItemPair : container->GetLootItems())
156 {
157 LootItem li;
158 li.itemid = storedItemPair.first;
159 li.count = storedItemPair.second.Count;
160 li.itemIndex = storedItemPair.second.ItemIndex;
161 li.follow_loot_rules = storedItemPair.second.FollowRules;
162 li.freeforall = storedItemPair.second.FFA;
163 li.is_blocked = storedItemPair.second.Blocked;
164 li.is_counted = storedItemPair.second.Counted;
165 li.is_underthreshold = storedItemPair.second.UnderThreshold;
166 li.needs_quest = storedItemPair.second.NeedsQuest;
167 li.randomPropertyId = storedItemPair.second.RandomPropertyId;
168 li.randomSuffix = storedItemPair.second.RandomSuffix;
169
170 // Copy the extra loot conditions from the item in the loot template
171 lt->CopyConditions(&li);
172
173 // If container item is in a bag, add that player as an allowed looter
174 if (item->GetBagSlot())
175 li.AddAllowedLooter(player);
176
177 // Finally add the LootItem to the container
178 loot->items.push_back(li);
179
180 // Increment unlooted count
181 ++loot->unlootedCount;
182 }
183 }
184
185 // Mark the item if it has loot so it won't be generated again on open
186 item->m_lootGenerated = true;
187 return true;
188}
189
191{
192 // write
193 std::unique_lock<std::shared_mutex> lock(*GetLock());
194
195 auto itr = _lootItemStore.find(containerId);
196 if (itr == _lootItemStore.end())
197 return;
198
199 itr->second.RemoveMoney();
200}
201
203{
204 // write
205 {
206 std::unique_lock<std::shared_mutex> lock(*GetLock());
207 _lootItemStore.erase(containerId);
208 }
209
210 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
212 stmt->setUInt32(0, containerId);
213 trans->Append(stmt);
214
215 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY);
216 stmt->setUInt32(0, containerId);
217 trans->Append(stmt);
218
219 CharacterDatabase.CommitTransaction(trans);
220}
221
223{
224 // write
225 std::unique_lock<std::shared_mutex> lock(*GetLock());
226
227 auto itr = _lootItemStore.find(containerId);
228 if (itr == _lootItemStore.end())
229 return;
230
231 itr->second.RemoveItem(itemId, count, itemIndex);
232}
233
235{
236 // Saves the money and item loot associated with an openable item to the DB
237 if (loot->isLooted()) // no money and no loot
238 return;
239
240 // read
241 {
242 std::shared_lock<std::shared_mutex> lock(*GetLock());
243
244 auto itr = _lootItemStore.find(loot->containerID);
245 if (itr != _lootItemStore.end())
246 {
247 TC_LOG_ERROR("misc", "Trying to store item loot by player: {} for container id: {} that is already in storage!", player->GetGUID().ToString(), loot->containerID);
248 return;
249 }
250 }
251
252 StoredLootContainer container(loot->containerID);
253
254 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
255 if (loot->gold)
256 container.AddMoney(loot->gold, trans);
257
259 stmt->setUInt32(0, loot->containerID);
260 trans->Append(stmt);
261
262 for (LootItem const& li : loot->items)
263 {
264 // Conditions are not checked when loot is generated, it is checked when loot is sent to a player.
265 // For items that are lootable, loot is saved to the DB immediately, that means that loot can be
266 // saved to the DB that the player never should have gotten. This check prevents that, so that only
267 // items that the player should get in loot are in the DB.
268 // IE: Horde items are not saved to the DB for Ally players.
269 if (!li.AllowedForPlayer(player, loot->lootOwnerGUID))
270 continue;
271
272 // Don't save currency tokens
273 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(li.itemid);
274 if (!itemTemplate || itemTemplate->IsCurrencyToken())
275 continue;
276
277 container.AddLootItem(li, trans);
278 }
279
280 CharacterDatabase.CommitTransaction(trans);
281
282 // write
283 {
284 std::unique_lock<std::shared_mutex> lock(*GetLock());
285 _lootItemStore.emplace(loot->containerID, std::move(container));
286 }
287}
288
290{
291 _lootItems.emplace(std::piecewise_construct, std::forward_as_tuple(lootItem.itemid), std::forward_as_tuple(lootItem));
292 if (!trans)
293 return;
294
296
297 // container_id, item_id, item_count, item_index, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix
298 stmt->setUInt32(0, _containerId);
299 stmt->setUInt32(1, lootItem.itemid);
300 stmt->setUInt32(2, lootItem.count);
301 stmt->setUInt32(3, lootItem.itemIndex);
302 stmt->setBool(4, lootItem.follow_loot_rules);
303 stmt->setBool(5, lootItem.freeforall);
304 stmt->setBool(6, lootItem.is_blocked);
305 stmt->setBool(7, lootItem.is_counted);
306 stmt->setBool(8, lootItem.is_underthreshold);
307 stmt->setBool(9, lootItem.needs_quest);
308 stmt->setInt32(10, lootItem.randomPropertyId);
309 stmt->setUInt32(11, lootItem.randomSuffix);
310
311 trans->Append(stmt);
312}
313
315{
316 _money = money;
317 if (!trans)
318 return;
319
321 stmt->setUInt32(0, _containerId);
322 trans->Append(stmt);
323
324 stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEMCONTAINER_MONEY);
325 stmt->setUInt32(0, _containerId);
326 stmt->setUInt32(1, _money);
327 trans->Append(stmt);
328}
329
331{
332 _money = 0;
333
335 stmt->setUInt32(0, _containerId);
336 CharacterDatabase.Execute(stmt);
337}
338
340{
341 auto bounds = _lootItems.equal_range(itemId);
342 for (auto itr = bounds.first; itr != bounds.second; ++itr)
343 {
344 if (itr->second.Count == count)
345 {
346 _lootItems.erase(itr);
347 break;
348 }
349 }
350
351 // Deletes a single item associated with an openable item from the DB
353 stmt->setUInt32(0, _containerId);
354 stmt->setUInt32(1, itemId);
355 stmt->setUInt32(2, count);
356 stmt->setUInt32(3, itemIndex);
357 CharacterDatabase.Execute(stmt);
358}
@ 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
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint32_t uint32
Definition Define.h:133
#define ASSERT
Definition Errors.h:68
#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)
#define sObjectMgr
Definition ObjectMgr.h:1721
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
Class used to access individual fields of database query result.
Definition Field.h:92
bool GetBool() const
Definition Field.h:100
uint32 GetUInt32() const
Definition Field.cpp:61
int32 GetInt32() const
Definition Field.cpp:69
Definition Item.h:62
bool m_lootGenerated
Definition Item.h:165
Loot loot
Definition Item.h:164
uint8 GetBagSlot() const
Definition Item.cpp:711
void AddNewStoredLoot(Loot *loot, Player *player)
static LootItemStorage * instance()
void RemoveStoredLootItemForContainer(uint32 containerId, uint32 itemId, uint32 count, uint32 itemIndex)
static std::shared_mutex * GetLock()
void RemoveStoredLootForContainer(uint32 containerId)
void RemoveStoredMoneyForContainer(uint32 containerId)
bool LoadStoredLoot(Item *item, Player *player)
LootTemplate const * GetLootFor(uint32 loot_id) const
Definition LootMgr.cpp:227
std::string ToString() const
uint32 GetEntry() const
Definition Object.h:81
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
void setUInt32(uint8 index, uint32 value)
void setBool(uint8 index, bool value)
void setInt32(uint8 index, int32 value)
void RemoveItem(uint32 itemId, uint32 count, uint32 itemIndex)
void AddLootItem(LootItem const &lootItem, CharacterDatabaseTransaction trans)
StoredLootItemContainer _lootItems
uint32 const _containerId
void AddMoney(uint32 money, CharacterDatabaseTransaction trans)
StoredLootItemContainer const & GetLootItems() const
uint32 GetMoney() const
bool IsCurrencyToken() const
uint32 itemid
Definition Loot.h:127
bool is_blocked
Definition Loot.h:136
void AddAllowedLooter(Player const *player)
Definition Loot.cpp:130
bool needs_quest
Definition Loot.h:140
bool follow_loot_rules
Definition Loot.h:141
bool is_underthreshold
Definition Loot.h:138
int32 randomPropertyId
Definition Loot.h:130
uint32 itemIndex
Definition Loot.h:128
uint8 count
Definition Loot.h:134
bool freeforall
Definition Loot.h:137
uint32 randomSuffix
Definition Loot.h:129
bool is_counted
Definition Loot.h:139
Definition Loot.h:207
bool isLooted() const
Definition Loot.h:239
uint8 unlootedCount
Definition Loot.h:217
ObjectGuid lootOwnerGUID
Definition Loot.h:219
uint32 containerID
Definition Loot.h:225
uint32 gold
Definition Loot.h:216
std::vector< LootItem > items
Definition Loot.h:214
StoredLootItem(LootItem const &lootItem)