TrinityCore
BlackMarketMgr.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 "BlackMarketMgr.h"
19#include "AccountMgr.h"
20#include "BlackMarketPackets.h"
21#include "CharacterCache.h"
22#include "Containers.h"
23#include "DatabaseEnv.h"
24#include "GameTime.h"
25#include "Item.h"
26#include "Language.h"
27#include "Log.h"
28#include "Mail.h"
29#include "ObjectAccessor.h"
30#include "ObjectMgr.h"
31#include "Player.h"
32#include "Realm.h"
33#include "StringConvert.h"
34#include "World.h"
35#include "WorldSession.h"
36#include <sstream>
37
39{
40}
41
43{
44 for (auto itr = _auctions.begin(); itr != _auctions.end(); ++itr)
45 delete itr->second;
46
47 for (auto itr = _templates.begin(); itr != _templates.end(); ++itr)
48 delete itr->second;
49}
50
52{
53 static BlackMarketMgr instance;
54 return &instance;
55}
56
58{
59 uint32 oldMSTime = getMSTime();
60
61 // Clear in case we are reloading
62 if (!_templates.empty())
63 {
64 for (BlackMarketTemplateMap::iterator itr = _templates.begin(); itr != _templates.end(); ++itr)
65 delete itr->second;
66
67 _templates.clear();
68 }
69
70 QueryResult result = WorldDatabase.Query("SELECT marketId, sellerNpc, itemEntry, quantity, minBid, duration, chance, bonusListIDs FROM blackmarket_template");
71 if (!result)
72 {
73 TC_LOG_INFO("server.loading", ">> Loaded 0 black market templates. DB table `blackmarket_template` is empty.");
74 return;
75 }
76
77 do
78 {
79 Field* fields = result->Fetch();
81
82 if (!templ->LoadFromDB(fields)) // Add checks
83 {
84 delete templ;
85 continue;
86 }
87
88 AddTemplate(templ);
89 } while (result->NextRow());
90
91 TC_LOG_INFO("server.loading", ">> Loaded {} black market templates in {} ms.", uint32(_templates.size()), GetMSTimeDiffToNow(oldMSTime));
92}
93
95{
96 uint32 oldMSTime = getMSTime();
97
98 // Clear in case we are reloading
99 if (!_auctions.empty())
100 {
101 for (BlackMarketEntryMap::iterator itr = _auctions.begin(); itr != _auctions.end(); ++itr)
102 delete itr->second;
103
104 _auctions.clear();
105 }
106
108 PreparedQueryResult result = CharacterDatabase.Query(stmt);
109 if (!result)
110 {
111 TC_LOG_INFO("server.loading", ">> Loaded 0 black market auctions. DB table `blackmarket_auctions` is empty.");
112 return;
113 }
114
115 _lastUpdate = GameTime::GetGameTime(); //Set update time before loading
116
117 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
118 do
119 {
120 Field* fields = result->Fetch();
121 BlackMarketEntry* auction = new BlackMarketEntry();
122
123 if (!auction->LoadFromDB(fields))
124 {
125 auction->DeleteFromDB(trans);
126 delete auction;
127 continue;
128 }
129
130 if (auction->IsCompleted())
131 {
132 auction->DeleteFromDB(trans);
133 delete auction;
134 continue;
135 }
136
137 AddAuction(auction);
138 } while (result->NextRow());
139
140 CharacterDatabase.CommitTransaction(trans);
141
142 TC_LOG_INFO("server.loading", ">> Loaded {} black market auctions in {} ms.", uint32(_auctions.size()), GetMSTimeDiffToNow(oldMSTime));
143}
144
145void BlackMarketMgr::Update(bool updateTime)
146{
147 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
148 time_t now = GameTime::GetGameTime();
149 for (BlackMarketEntryMap::iterator itr = _auctions.begin(); itr != _auctions.end(); ++itr)
150 {
151 BlackMarketEntry* entry = itr->second;
152
153 if (entry->IsCompleted() && entry->GetBidder())
154 SendAuctionWonMail(entry, trans);
155
156 if (updateTime)
157 entry->Update(now);
158 }
159
160 if (updateTime)
161 _lastUpdate = now;
162
163 CharacterDatabase.CommitTransaction(trans);
164}
165
167{
168 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
169 // Delete completed auctions
170 for (BlackMarketEntryMap::iterator itr = _auctions.begin(); itr != _auctions.end();)
171 {
172 BlackMarketEntry* entry = itr->second;
173 if (!entry->IsCompleted())
174 {
175 ++itr;
176 continue;
177 }
178
179 entry->DeleteFromDB(trans);
180 itr = _auctions.erase(itr);
181 delete entry;
182 }
183
184 CharacterDatabase.CommitTransaction(trans);
185 trans = CharacterDatabase.BeginTransaction();
186
187 std::list<BlackMarketTemplate const*> templates;
188 for (auto const& pair : _templates)
189 {
190 if (GetAuctionByID(pair.second->MarketID))
191 continue;
192 if (!roll_chance_f(pair.second->Chance))
193 continue;
194
195 templates.push_back(pair.second);
196 }
197
199
200 for (BlackMarketTemplate const* templat : templates)
201 {
202 BlackMarketEntry* entry = new BlackMarketEntry();
203 entry->Initialize(templat->MarketID, templat->Duration);
204 entry->SaveToDB(trans);
205 AddAuction(entry);
206 }
207
208 CharacterDatabase.CommitTransaction(trans);
209
210 Update(true);
211}
212
214{
215 return sWorld->getBoolConfig(CONFIG_BLACKMARKET_ENABLED);
216}
217
219{
220 packet.LastUpdateID = _lastUpdate;
221 packet.Items.reserve(_auctions.size());
222 for (auto itr = _auctions.begin(); itr != _auctions.end(); ++itr)
223 {
224 BlackMarketTemplate const* templ = itr->second->GetTemplate();
225
227 item.MarketID = itr->second->GetMarketId();
228 item.SellerNPC = templ->SellerNPC;
229 item.Item = templ->Item;
230 item.Quantity = templ->Quantity;
231
232 // No bids yet
233 if (!itr->second->GetNumBids())
234 {
235 item.MinBid = templ->MinBid;
236 item.MinIncrement = 1;
237 }
238 else
239 {
240 item.MinIncrement = itr->second->GetMinIncrement(); // 5% increment minimum
241 item.MinBid = itr->second->GetCurrentBid() + item.MinIncrement;
242 }
243
244 item.CurrentBid = itr->second->GetCurrentBid();
245 item.SecondsRemaining = itr->second->GetSecondsRemaining();
246 item.HighBid = (itr->second->GetBidder() == player->GetGUID().GetCounter());
247 item.NumBids = itr->second->GetNumBids();
248
249 packet.Items.push_back(item);
250 }
251}
252
254{
255 _auctions[auction->GetMarketId()] = auction;
256}
257
259{
260 _templates[templ->MarketID] = templ;
261}
262
264{
265 // Mail already sent
266 if (entry->GetMailSent())
267 return;
268
269 uint32 bidderAccId = 0;
270 ObjectGuid bidderGuid = ObjectGuid::Create<HighGuid::Player>(entry->GetBidder());
271 Player* bidder = ObjectAccessor::FindConnectedPlayer(bidderGuid);
272 // data for gm.log
273 std::string bidderName;
274 bool logGmTrade = false;
275
276 if (bidder)
277 {
278 bidderAccId = bidder->GetSession()->GetAccountId();
279 bidderName = bidder->GetName();
281 }
282 else
283 {
284 bidderAccId = sCharacterCache->GetCharacterAccountIdByGuid(bidderGuid);
285 if (!bidderAccId) // Account exists
286 return;
287
289
290 if (logGmTrade && !sCharacterCache->GetCharacterNameByGuid(bidderGuid, bidderName))
291 bidderName = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN);
292 }
293
294 // Create item
295 BlackMarketTemplate const* templ = entry->GetTemplate();
297 if (!item)
298 return;
299
300 if (templ->Item.ItemBonus)
301 for (int32 bonusList : templ->Item.ItemBonus->BonusListIDs)
302 item->AddBonuses(bonusList);
303
304 item->SetOwnerGUID(bidderGuid);
305
306 item->SaveToDB(trans);
307
308 // Log trade
309 if (logGmTrade)
310 sLog->OutCommand(bidderAccId, "GM {} (Account: {}) won item in blackmarket auction: {} (Entry: {} Count: {}) and payed gold : {}.",
311 bidderName, bidderAccId, item->GetTemplate()->GetDefaultLocaleName(), item->GetEntry(), item->GetCount(), entry->GetCurrentBid() / GOLD);
312
313 if (bidder)
314 bidder->GetSession()->SendBlackMarketWonNotification(entry, item);
315
317 .AddItem(item)
318 .SendMailTo(trans, MailReceiver(bidder, entry->GetBidder()), entry, MAIL_CHECK_MASK_COPIED);
319
320 entry->MailSent();
321}
322
324{
325 ObjectGuid oldBidder_guid = ObjectGuid::Create<HighGuid::Player>(entry->GetBidder());
326 Player* oldBidder = ObjectAccessor::FindConnectedPlayer(oldBidder_guid);
327
328 uint32 oldBidder_accId = 0;
329 if (!oldBidder)
330 oldBidder_accId = sCharacterCache->GetCharacterAccountIdByGuid(oldBidder_guid);
331
332 // old bidder exist
333 if (!oldBidder && !oldBidder_accId)
334 return;
335
336 if (oldBidder)
338
340 .AddMoney(entry->GetCurrentBid())
341 .SendMailTo(trans, MailReceiver(oldBidder, entry->GetBidder()), entry, MAIL_CHECK_MASK_COPIED);
342}
343
345{
346 BlackMarketEntryMap::const_iterator itr = _auctions.find(marketId);
347 if (itr != _auctions.end())
348 return itr->second;
349
350 return nullptr;
351}
352
354{
355 BlackMarketTemplateMap::const_iterator itr = _templates.find(marketId);
356 if (itr != _templates.end())
357 return itr->second;
358
359 return nullptr;
360}
361
363{
364 MarketID = fields[0].GetInt32();
365 SellerNPC = fields[1].GetInt32();
366 Item.ItemID = fields[2].GetUInt32();
367 Quantity = fields[3].GetInt32();
368 MinBid = fields[4].GetUInt64();
369 Duration = static_cast<time_t>(fields[5].GetUInt32());
370 Chance = fields[6].GetFloat();
371
372 std::vector<int32> bonusListIDs;
373 for (std::string_view token : Trinity::Tokenize(fields[7].GetStringView(), ' ', false))
374 if (Optional<int32> bonusListID = Trinity::StringTo<int32>(token))
375 bonusListIDs.push_back(*bonusListID);
376
377 if (!bonusListIDs.empty())
378 {
379 Item.ItemBonus.emplace();
380 Item.ItemBonus->BonusListIDs = bonusListIDs;
381 }
382
383 if (!sObjectMgr->GetCreatureTemplate(SellerNPC))
384 {
385 TC_LOG_ERROR("misc", "Black market template {} does not have a valid seller. (Entry: {})", MarketID, SellerNPC);
386 return false;
387 }
388
389 if (!sObjectMgr->GetItemTemplate(Item.ItemID))
390 {
391 TC_LOG_ERROR("misc", "Black market template {} does not have a valid item. (Entry: {})", MarketID, Item.ItemID);
392 return false;
393 }
394
395 return true;
396}
397
398void BlackMarketEntry::Update(time_t newTimeOfUpdate)
399{
400 _secondsRemaining = _secondsRemaining - (newTimeOfUpdate - sBlackMarketMgr->GetLastUpdate());
401}
402
404{
405 return sBlackMarketMgr->GetTemplateByID(_marketId);
406}
407
409{
410 return _secondsRemaining - (GameTime::GetGameTime() - sBlackMarketMgr->GetLastUpdate());
411}
412
414{
416}
417
419{
420 return GetSecondsRemaining() <= 0;
421}
422
424{
425 _marketId = fields[0].GetInt32();
426
427 // Invalid MarketID
428 BlackMarketTemplate const* templ = sBlackMarketMgr->GetTemplateByID(_marketId);
429 if (!templ)
430 {
431 TC_LOG_ERROR("misc", "Black market auction {} does not have a valid id.", _marketId);
432 return false;
433 }
434
435 _currentBid = fields[1].GetUInt64();
436 _secondsRemaining = static_cast<time_t>(fields[2].GetInt64()) - sBlackMarketMgr->GetLastUpdate();
437 _numBids = fields[3].GetInt32();
438 _bidder = fields[4].GetUInt64();
439
440 // Either no bidder or existing player
441 if (_bidder && !sCharacterCache->GetCharacterAccountIdByGuid(ObjectGuid::Create<HighGuid::Player>(_bidder))) // Probably a better way to check if player exists
442 {
443 TC_LOG_ERROR("misc", "Black market auction {} does not have a valid bidder (GUID: {} ).", _marketId, _bidder);
444 return false;
445 }
446
447 return true;
448}
449
451{
453
454 stmt->setInt32(0, _marketId);
455 stmt->setUInt64(1, _currentBid);
456 stmt->setInt64(2, GetExpirationTime());
457 stmt->setInt32(3, _numBids);
458 stmt->setUInt64(4, _bidder);
459
460 trans->Append(stmt);
461}
462
464{
466 stmt->setInt32(0, _marketId);
467 trans->Append(stmt);
468}
469
471{
472 if (bid <= _currentBid)
473 return false;
474
475 if (bid < _currentBid + GetMinIncrement())
476 return false;
477
478 if (bid >= BMAH_MAX_BID)
479 return false;
480
481 return true;
482}
483
485{
486 if (bid < _currentBid)
487 return;
488
489 _currentBid = bid;
490 ++_numBids;
491
492 if (GetSecondsRemaining() < 30 * MINUTE)
494
495 _bidder = player->GetGUID().GetCounter();
496
497 player->ModifyMoney(-static_cast<int64>(bid));
498
500
501 stmt->setUInt64(0, _currentBid);
502 stmt->setInt64(1, GetExpirationTime());
503 stmt->setInt32(2, _numBids);
504 stmt->setUInt64(3, _bidder);
505 stmt->setInt32(4, _marketId);
506
507 trans->Append(stmt);
508
509 sBlackMarketMgr->Update(true);
510}
511
513{
514 std::ostringstream strm;
515 strm << GetTemplate()->Item.ItemID << ":0:" << response << ':' << GetMarketId() << ':' << GetTemplate()->Quantity;
516 return strm.str();
517}
518
520{
521 std::ostringstream strm;
522 strm << GetTemplate()->SellerNPC << ':' << _currentBid;
523
524 return strm.str();
525}
#define sBlackMarketMgr
static const uint64 BMAH_MAX_BID
BMAHMailAuctionAnswers
@ BMAH_AUCTION_WON
@ BMAH_AUCTION_OUTBID
#define sCharacterCache
@ CHAR_INS_BLACKMARKET_AUCTIONS
@ CHAR_UPD_BLACKMARKET_AUCTIONS
@ CHAR_DEL_BLACKMARKET_AUCTIONS
@ CHAR_SEL_BLACKMARKET_AUCTIONS
@ MINUTE
Definition: Common.h:29
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
int64_t int64
Definition: Define.h:137
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
uint32_t uint32
Definition: Define.h:142
@ LANG_UNKNOWN
Definition: Language.h:77
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define sLog
Definition: Log.h:130
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
@ MAIL_CHECK_MASK_COPIED
This mail was returned. Do not allow returning mail back again.
Definition: Mail.h:55
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
bool roll_chance_f(float chance)
Definition: Random.h:53
@ GOLD
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
static bool HasPermission(uint32 accountId, uint32 permission, uint32 realmId)
Definition: AccountMgr.cpp:577
void DeleteFromDB(CharacterDatabaseTransaction trans) const
int32 GetMarketId() const
void SaveToDB(CharacterDatabaseTransaction trans) const
void Update(time_t newTimeOfUpdate)
bool IsCompleted() const
std::string BuildAuctionMailSubject(BMAHMailAuctionAnswers response) const
ObjectGuid::LowType _bidder
uint64 GetCurrentBid() const
BlackMarketTemplate const * GetTemplate() const
bool GetMailSent() const
uint32 GetSecondsRemaining() const
time_t GetExpirationTime() const
std::string BuildAuctionMailBody()
bool ValidateBid(uint64 bid) const
bool LoadFromDB(Field *fields)
ObjectGuid::LowType GetBidder() const
void PlaceBid(uint64 bid, Player *player, CharacterDatabaseTransaction trans)
void Initialize(int32 marketId, uint32 duration)
uint64 GetMinIncrement() const
void AddTemplate(BlackMarketTemplate *templ)
void SendAuctionOutbidMail(BlackMarketEntry *entry, CharacterDatabaseTransaction trans)
void Update(bool updateTime=false)
BlackMarketTemplate const * GetTemplateByID(int32 marketId) const
void BuildItemsResponse(WorldPackets::BlackMarket::BlackMarketRequestItemsResult &packet, Player *player)
bool IsEnabled() const
BlackMarketEntryMap _auctions
BlackMarketTemplateMap _templates
BlackMarketEntry * GetAuctionByID(int32 marketId) const
static BlackMarketMgr * Instance()
void SendAuctionWonMail(BlackMarketEntry *entry, CharacterDatabaseTransaction trans)
void AddAuction(BlackMarketEntry *auction)
Class used to access individual fields of database query result.
Definition: Field.h:90
int64 GetInt64() const
Definition: Field.cpp:86
uint64 GetUInt64() const
Definition: Field.cpp:78
float GetFloat() const
Definition: Field.cpp:94
uint32 GetUInt32() const
Definition: Field.cpp:62
int32 GetInt32() const
Definition: Field.cpp:70
Definition: Item.h:170
virtual void SaveToDB(CharacterDatabaseTransaction trans)
Definition: Item.cpp:561
void AddBonuses(uint32 bonusListID)
Definition: Item.cpp:2534
ItemTemplate const * GetTemplate() const
Definition: Item.cpp:1141
uint32 GetCount() const
Definition: Item.h:273
void SetOwnerGUID(ObjectGuid guid)
Definition: Item.h:189
static Item * CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Player const *player=nullptr, bool addDefaultBonuses=true)
Definition: Item.cpp:1625
void SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const &receiver, MailSender const &sender, MailCheckMask checked=MAIL_CHECK_MASK_NONE, uint32 deliver_delay=0)
Definition: Mail.cpp:192
MailDraft & AddItem(Item *item)
Definition: Mail.cpp:99
MailDraft & AddMoney(uint64 money)
Definition: Mail.h:145
LowType GetCounter() const
Definition: ObjectGuid.h:293
uint32 GetEntry() const
Definition: Object.h:161
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
bool ModifyMoney(int64 amount, bool sendError=true)
Definition: Player.cpp:24098
WorldSession * GetSession() const
Definition: Player.h:2101
void setInt32(const uint8 index, const int32 value)
void setInt64(const uint8 index, const int64 value)
void setUInt64(const uint8 index, const uint64 value)
std::string const & GetName() const
Definition: Object.h:555
void SendBlackMarketOutbidNotification(BlackMarketTemplate const *templ)
void SendBlackMarketWonNotification(BlackMarketEntry const *entry, Item const *item)
bool HasPermission(uint32 permissionId)
uint32 GetAccountId() const
#define sWorld
Definition: World.h:931
Realm realm
Definition: World.cpp:3966
@ CONFIG_BLACKMARKET_MAXAUCTIONS
Definition: World.h:438
@ CONFIG_BLACKMARKET_ENABLED
Definition: World.h:183
time_t GetGameTime()
Definition: GameTime.cpp:44
TC_GAME_API Player * FindConnectedPlayer(ObjectGuid const &)
void RandomResize(C &container, std::size_t requestedSize)
Definition: Containers.h:67
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition: Util.cpp:56
@ RBAC_PERM_LOG_GM_TRADE
Definition: RBAC.h:64
WorldPackets::Item::ItemInstance Item
bool LoadFromDB(Field *fields)
char const * GetDefaultLocaleName() const
Battlenet::RealmHandle Id
Definition: Realm.h:82
Optional< ItemBonuses > ItemBonus