TrinityCore
Loading...
Searching...
No Matches
AuctionHouseMgr.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 "AuctionHouseMgr.h"
19#include "AuctionHouseBot.h"
20#include "AccountMgr.h"
21#include "Bag.h"
22#include "Common.h"
23#include "CharacterCache.h"
24#include "DatabaseEnv.h"
25#include "DBCStores.h"
26#include "GameTime.h"
27#include "Item.h"
28#include "Language.h"
29#include "Log.h"
30#include "Mail.h"
31#include "ObjectAccessor.h"
32#include "ObjectMgr.h"
33#include "Player.h"
34#include "ScriptMgr.h"
35#include "World.h"
36#include "WorldSession.h"
37#include "WowTime.h"
38
43
45
47{
48 for (ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
49 delete itr->second;
50}
51
57
59{
61 return &mNeutralAuctions;
62
63 // teams have linked auction houses
64 FactionTemplateEntry const* uEntry = sFactionTemplateStore.LookupEntry(factionTemplateId);
65 if (!uEntry)
66 return &mNeutralAuctions;
67 else if (uEntry->FactionGroup & FACTION_MASK_ALLIANCE)
68 return &mAllianceAuctions;
69 else if (uEntry->FactionGroup & FACTION_MASK_HORDE)
70 return &mHordeAuctions;
71 else
72 return &mNeutralAuctions;
73}
74
76{
78 return &mNeutralAuctions;
79
80 switch(auctionHouseId)
81 {
84 default : return &mNeutralAuctions;
85 }
86}
87
89{
90 uint32 MSV = pItem->GetSellPrice();
91
92 if (MSV <= 0)
93 return float(AH_MINIMUM_DEPOSIT) * sWorld->getRate(RATE_AUCTION_DEPOSIT);
94
95 float multiplier = CalculatePct(float(entry->DepositRate), 3);
96 uint32 timeHr = (((time / 60) / 60) / 12);
97 uint32 deposit = uint32(MSV * multiplier * sWorld->getRate(RATE_AUCTION_DEPOSIT));
98 float remainderbase = float(MSV * multiplier * sWorld->getRate(RATE_AUCTION_DEPOSIT)) - deposit;
99
100 deposit *= timeHr * count;
101
102 int i = count;
103 while (i > 0 && (remainderbase * i) != uint32(remainderbase * i))
104 i--;
105
106 if (i)
107 deposit += remainderbase * i * timeHr;
108
109 TC_LOG_DEBUG("auctionHouse", "MSV: {}", MSV);
110 TC_LOG_DEBUG("auctionHouse", "Items: {}", count);
111 TC_LOG_DEBUG("auctionHouse", "Multiplier: {}", multiplier);
112 TC_LOG_DEBUG("auctionHouse", "Deposit: {}", deposit);
113 TC_LOG_DEBUG("auctionHouse", "Deposit rm: {}", remainderbase * count);
114
115 if (deposit < float(AH_MINIMUM_DEPOSIT) * sWorld->getRate(RATE_AUCTION_DEPOSIT))
116 return float(AH_MINIMUM_DEPOSIT) * sWorld->getRate(RATE_AUCTION_DEPOSIT);
117 else
118 return deposit;
119}
120
121//does not clear ram
123{
124 Item* pItem = GetAItem(auction->itemGUIDLow);
125 if (!pItem)
126 return;
127
128 uint32 bidderAccId = 0;
129 ObjectGuid bidderGuid = ObjectGuid::Create<HighGuid::Player>(auction->bidder);
130 Player* bidder = ObjectAccessor::FindConnectedPlayer(bidderGuid);
131 // data for gm.log
132 std::string bidderName;
133 bool logGmTrade = (auction->Flags & AUCTION_ENTRY_FLAG_GM_LOG_BUYER) != AUCTION_ENTRY_FLAG_NONE;
134
135 if (bidder)
136 {
137 bidderAccId = bidder->GetSession()->GetAccountId();
138 bidderName = bidder->GetName();
139 }
140 else
141 {
142 bidderAccId = sCharacterCache->GetCharacterAccountIdByGuid(bidderGuid);
143
144 if (logGmTrade && !sCharacterCache->GetCharacterNameByGuid(bidderGuid, bidderName))
145 bidderName = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN);
146 }
147
148 if (logGmTrade)
149 {
150 ObjectGuid ownerGuid = ObjectGuid::Create<HighGuid::Player>(auction->owner);
151 std::string ownerName;
152 if (!sCharacterCache->GetCharacterNameByGuid(ownerGuid, ownerName))
153 ownerName = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN);
154
155 uint32 ownerAccId = sCharacterCache->GetCharacterAccountIdByGuid(ownerGuid);
156
157 sLog->OutCommand(bidderAccId, "GM {} (Account: {}) won item in auction: {} (Entry: {} Count: {}) and pay money: {}. Original owner {} (Account: {})",
158 bidderName, bidderAccId, pItem->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale()),
159 pItem->GetEntry(), pItem->GetCount(), auction->bid, ownerName, ownerAccId);
160 }
161
162 // receiver exist
163 if ((bidder || bidderAccId) && !sAuctionBotConfig->IsBotChar(auction->bidder))
164 {
165 // set owner to bidder (to prevent delete item with sender char deleting)
166 // owner in `data` will set at mail receive and item extracting
168 stmt->setUInt32(0, auction->bidder);
169 stmt->setUInt32(1, pItem->GetGUID().GetCounter());
170 trans->Append(stmt);
171
172 if (bidder)
173 {
174 bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, bidderGuid, 0, 0, auction->itemEntry);
175 // FIXME: for offline player need also
177 }
178
179 MailDraft(auction->BuildAuctionMailSubject(AUCTION_WON), AuctionEntry::BuildAuctionWonMailBody(ObjectGuid::Create<HighGuid::Player>(auction->owner), auction->bid, auction->buyout))
180 .AddItem(pItem)
182 }
183 else
184 {
185 // bidder doesn't exist, delete the item
186 sAuctionMgr->RemoveAItem(auction->itemGUIDLow, true, &trans);
187 }
188}
189
191{
192 ObjectGuid owner_guid = ObjectGuid::Create<HighGuid::Player>(auction->owner);
193 Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid);
194 uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(owner_guid);
195 // owner exist (online or offline)
196 if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner))
197 {
199 eta += Seconds(sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY));
200 if (owner)
201 eta += owner->GetSession()->GetTimezoneOffset();
202
204 AuctionEntry::BuildAuctionInvoiceMailBody(ObjectGuid::Create<HighGuid::Player>(auction->bidder), auction->bid, auction->buyout, auction->deposit,
205 auction->GetAuctionCut(), sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY), eta.GetPackedTime()))
206 .SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED | MAIL_CHECK_MASK_AUCTION);
207 }
208}
209
210//call this method to send mail to auction owner, when auction is successful, it does not clear ram
212{
213 ObjectGuid owner_guid = ObjectGuid::Create<HighGuid::Player>(auction->owner);
214 Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid);
215 uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(owner_guid);
216 // owner exist
217 if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner))
218 {
219 uint32 profit = auction->bid + auction->deposit - auction->GetAuctionCut();
220
221 //FIXME: what do if owner offline
222 if (owner)
223 {
226 //send auction owner notification, bidder must be current!
227 owner->GetSession()->SendAuctionOwnerNotification(auction);
228 }
229
230 MailDraft(auction->BuildAuctionMailSubject(AUCTION_SUCCESSFUL), AuctionEntry::BuildAuctionSoldMailBody(ObjectGuid::Create<HighGuid::Player>(auction->bidder), auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut()))
231 .AddMoney(profit)
233 }
234}
235
236//does not clear ram
238{
239 //return an item in auction to its owner by mail
240 Item* pItem = GetAItem(auction->itemGUIDLow);
241 if (!pItem)
242 return;
243
244 ObjectGuid owner_guid = ObjectGuid::Create<HighGuid::Player>(auction->owner);
245 Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid);
246 uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(owner_guid);
247 // owner exist
248 if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner))
249 {
250 if (owner)
251 owner->GetSession()->SendAuctionOwnerNotification(auction);
252
254 .AddItem(pItem)
255 .SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED | MAIL_CHECK_MASK_AUCTION, 0);
256 }
257 else
258 {
259 // owner doesn't exist, delete the item
260 sAuctionMgr->RemoveAItem(auction->itemGUIDLow, true, &trans);
261 }
262}
263
264//this function sends mail to old bidder
266{
267 ObjectGuid oldBidder_guid = ObjectGuid::Create<HighGuid::Player>(auction->bidder);
268 Player* oldBidder = ObjectAccessor::FindConnectedPlayer(oldBidder_guid);
269
270 uint32 oldBidder_accId = 0;
271 if (!oldBidder)
272 oldBidder_accId = sCharacterCache->GetCharacterAccountIdByGuid(oldBidder_guid);
273
274 // old bidder exist
275 if ((oldBidder || oldBidder_accId) && !sAuctionBotConfig->IsBotChar(auction->bidder))
276 {
277 if (oldBidder && newBidder)
278 oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->itemEntry);
279
281 .AddMoney(auction->bid)
282 .SendMailTo(trans, MailReceiver(oldBidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED | MAIL_CHECK_MASK_AUCTION);
283 }
284}
285
286//this function sends mail, when auction is cancelled to old bidder
288{
289 ObjectGuid bidder_guid = ObjectGuid::Create<HighGuid::Player>(auction->bidder);
290 Player* bidder = ObjectAccessor::FindConnectedPlayer(bidder_guid);
291
292 uint32 bidder_accId = 0;
293 if (!bidder)
294 bidder_accId = sCharacterCache->GetCharacterAccountIdByGuid(bidder_guid);
295
296 if (bidder)
297 bidder->GetSession()->SendAuctionRemovedNotification(auction->Id, auction->itemEntry, item->GetItemRandomPropertyId());
298
299 // bidder exist
300 if ((bidder || bidder_accId) && !sAuctionBotConfig->IsBotChar(auction->bidder))
302 .AddMoney(auction->bid)
303 .SendMailTo(trans, MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED | MAIL_CHECK_MASK_AUCTION);
304}
305
307{
308 uint32 oldMSTime = getMSTime();
309
310 // need to clear in case we are reloading
311 if (!mAitems.empty())
312 {
313 for (ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
314 delete itr->second;
315
316 mAitems.clear();
317 }
318
319 // data needs to be at first place for Item::LoadFromDB
321 PreparedQueryResult result = CharacterDatabase.Query(stmt);
322
323 if (!result)
324 {
325 TC_LOG_INFO("server.loading", ">> Loaded 0 auction items. DB table `auctionhouse` or `item_instance` is empty!");
326
327 return;
328 }
329
330 uint32 count = 0;
331
332 do
333 {
334 Field* fields = result->Fetch();
335
336 ObjectGuid::LowType item_guid = fields[11].GetUInt32();
337 uint32 itemEntry = fields[12].GetUInt32();
338
339 ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
340 if (!proto)
341 {
342 TC_LOG_ERROR("misc", "AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: {} item entry: #{}) in auction, skipped.", item_guid, itemEntry);
343 continue;
344 }
345
346 Item* item = NewItemOrBag(proto);
347 if (!item->LoadFromDB(item_guid, ObjectGuid::Empty, fields, itemEntry))
348 {
349 delete item;
350 continue;
351 }
352 AddAItem(item);
353
354 ++count;
355 }
356 while (result->NextRow());
357
358 TC_LOG_INFO("server.loading", ">> Loaded {} auction items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
359
360}
361
363{
364 uint32 oldMSTime = getMSTime();
365
367 PreparedQueryResult resultAuctions = CharacterDatabase.Query(stmt);
368
369 if (!resultAuctions)
370 {
371 TC_LOG_INFO("server.loading", ">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
372
373 return;
374 }
375
376 // parse bidder list
377 std::unordered_map<uint32, std::unordered_set<ObjectGuid>> biddersByAuction;
379
380 uint32 countBidders = 0;
381 if (PreparedQueryResult resultBidders = CharacterDatabase.Query(stmt2))
382 {
383 do
384 {
385 Field* fields = resultBidders->Fetch();
386 biddersByAuction[fields[0].GetUInt32()].insert(ObjectGuid::Create<HighGuid::Player>(fields[1].GetUInt32()));
387 ++countBidders;
388 }
389 while (resultBidders->NextRow());
390 }
391
392 // parse auctions from db
393 uint32 countAuctions = 0;
394 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
395 do
396 {
397 Field* fields = resultAuctions->Fetch();
398
399 AuctionEntry* aItem = new AuctionEntry();
400 if (!aItem->LoadFromDB(fields))
401 {
402 aItem->DeleteFromDB(trans);
403 delete aItem;
404 continue;
405 }
406
407 auto it = biddersByAuction.find(aItem->Id);
408 if (it != biddersByAuction.end())
409 aItem->bidders = std::move(it->second);
410
412 ++countAuctions;
413 } while (resultAuctions->NextRow());
414
415 CharacterDatabase.CommitTransaction(trans);
416
417 TC_LOG_INFO("server.loading", ">> Loaded {} auctions with {} bidders in {} ms", countAuctions, countBidders, GetMSTimeDiffToNow(oldMSTime));
418}
419
421{
422 ASSERT(item);
423 ASSERT_WITH_SIDE_EFFECTS(mAitems.emplace(item->GetGUID().GetCounter(), item).second);
424}
425
426bool AuctionHouseMgr::RemoveAItem(ObjectGuid::LowType id, bool deleteItem /*= false*/, CharacterDatabaseTransaction* trans /*= nullptr*/)
427{
428 ItemMap::iterator i = mAitems.find(id);
429 if (i == mAitems.end())
430 return false;
431
432 if (deleteItem)
433 {
434 ASSERT(trans);
435 i->second->FSetState(ITEM_REMOVED);
436 i->second->SaveToDB(*trans);
437 }
438
439 mAitems.erase(i);
440 return true;
441}
442
444{
445 PlayerAuctions* thisAH;
446 auto itr = pendingAuctionMap.find(player->GetGUID());
447 if (itr != pendingAuctionMap.end())
448 {
449 thisAH = itr->second.first;
450
451 // Get deposit so far
452 uint32 totalDeposit = 0;
453 for (AuctionEntry const* thisAuction : *thisAH)
454 totalDeposit += thisAuction->deposit;
455
456 // Add this deposit
457 totalDeposit += aEntry->deposit;
458
459 if (!player->HasEnoughMoney(totalDeposit))
460 return false;
461 }
462 else
463 {
464 thisAH = new PlayerAuctions;
465 pendingAuctionMap[player->GetGUID()] = AuctionPair(thisAH, 0);
466 }
467 thisAH->push_back(aEntry);
468 return true;
469}
470
472{
473 auto const itr = pendingAuctionMap.find(player->GetGUID());
474 if (itr != pendingAuctionMap.end())
475 return itr->second.first->size();
476
477 return 0;
478}
479
481{
482 auto iterMap = pendingAuctionMap.find(player->GetGUID());
483 if (iterMap == pendingAuctionMap.end())
484 return;
485
486 PlayerAuctions* thisAH = iterMap->second.first;
487
488 uint32 totaldeposit = 0;
489 auto itrAH = thisAH->begin();
490 for (; itrAH != thisAH->end(); ++itrAH)
491 {
492 AuctionEntry* AH = (*itrAH);
493 if (!player->HasEnoughMoney(totaldeposit + AH->deposit))
494 break;
495
496 totaldeposit += AH->deposit;
497 }
498
499 // expire auctions we cannot afford
500 if (itrAH != thisAH->end())
501 {
502 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
503
504 do
505 {
506 AuctionEntry* AH = (*itrAH);
508 AH->DeleteFromDB(trans);
509 AH->SaveToDB(trans);
510 ++itrAH;
511 } while (itrAH != thisAH->end());
512
513 CharacterDatabase.CommitTransaction(trans);
514 }
515
516 pendingAuctionMap.erase(player->GetGUID());
517 delete thisAH;
518 player->ModifyMoney(-int32(totaldeposit));
519}
520
522{
523 for (auto itr = pendingAuctionMap.begin(); itr != pendingAuctionMap.end();)
524 {
525 ObjectGuid playerGUID = itr->first;
526 if (Player* player = ObjectAccessor::FindConnectedPlayer(playerGUID))
527 {
528 // Check if there were auctions since last update process if not
529 if (PendingAuctionCount(player) == itr->second.second)
530 {
531 ++itr;
532 PendingAuctionProcess(player);
533 }
534 else
535 {
536 ++itr;
537 pendingAuctionMap[playerGUID].second = PendingAuctionCount(player);
538 }
539 }
540 else
541 {
542 // Expire any auctions that we couldn't get a deposit for
543 TC_LOG_WARN("auctionHouse", "Player {} was offline, unable to retrieve deposit!", playerGUID.ToString());
544 PlayerAuctions* thisAH = itr->second.first;
545 ++itr;
546 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
547 for (auto AHitr = thisAH->begin(); AHitr != thisAH->end();)
548 {
549 AuctionEntry* AH = (*AHitr);
550 ++AHitr;
552 AH->DeleteFromDB(trans);
553 AH->SaveToDB(trans);
554 }
555 CharacterDatabase.CommitTransaction(trans);
556 pendingAuctionMap.erase(playerGUID);
557 delete thisAH;
558 }
559 }
560}
561
568
570{
571 uint32 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house
572
574 {
575 // FIXME: found way for proper auctionhouse selection by another way
576 // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races.
577 // but no easy way convert creature faction to player race faction for specific city
578 FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId);
579 if (!u_entry)
580 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house
581 else if (u_entry->FactionGroup & FACTION_MASK_ALLIANCE)
582 houseid = AUCTIONHOUSE_ALLIANCE; // human auction house
583 else if (u_entry->FactionGroup & FACTION_MASK_HORDE)
584 houseid = AUCTIONHOUSE_HORDE; // orc auction house
585 else
586 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house
587 }
588
589 return sAuctionHouseStore.LookupEntry(houseid);
590}
591
596
598{
599 ASSERT(auction);
600
601 AuctionsMap[auction->Id] = auction;
602 sScriptMgr->OnAuctionAdd(this, auction);
603}
604
606{
607 bool wasInMap = AuctionsMap.erase(auction->Id) ? true : false;
608
609 sScriptMgr->OnAuctionRemove(this, auction);
610
611 // we need to delete the entry, it is not referenced any more
612 delete auction;
613 return wasInMap;
614}
615
617{
618 time_t curTime = GameTime::GetGameTime();
620
621 // If storage is empty, no need to update. next == NULL in this case.
622 if (AuctionsMap.empty())
623 return;
624
625 // Clear expired throttled players
626 for (PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.begin(); itr != GetAllThrottleMap.end();)
627 {
628 if (itr->second <= curTime)
629 itr = GetAllThrottleMap.erase(itr);
630 else
631 ++itr;
632 }
633
634 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
635
636 for (AuctionEntryMap::iterator it = AuctionsMap.begin(); it != AuctionsMap.end();)
637 {
638 // from auctionhousehandler.cpp, creates auction pointer & player pointer
639 AuctionEntry* auction = it->second;
640 // Increment iterator due to AuctionEntry deletion
641 ++it;
642
644 if (auction->expire_time > curTime + 60)
645 continue;
646
648 if (!auction->bidder && auction->bid == 0)
649 {
650 sAuctionMgr->SendAuctionExpiredMail(auction, trans);
651 sScriptMgr->OnAuctionExpire(this, auction);
652 }
654 else
655 {
656 //we should send an "item sold" message if the seller is online
657 //we send the item to the winner
658 //we send the money to the seller
659 sAuctionMgr->SendAuctionSuccessfulMail(auction, trans);
660 sAuctionMgr->SendAuctionWonMail(auction, trans);
661 sScriptMgr->OnAuctionSuccessful(this, auction);
662 }
663
665 auction->DeleteFromDB(trans);
666
667 sAuctionMgr->RemoveAItem(auction->itemGUIDLow);
668 RemoveAuction(auction);
669 }
670
671 // Run DB changes
672 CharacterDatabase.CommitTransaction(trans);
673}
674
676{
677 for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
678 {
679 AuctionEntry* Aentry = itr->second;
680 if (Aentry && Aentry->bidders.find(player->GetGUID()) != Aentry->bidders.end())
681 {
682 if (itr->second->BuildAuctionInfo(data))
683 ++count;
684
685 ++totalcount;
686 }
687 }
688}
689
691{
692 for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
693 {
694 AuctionEntry* Aentry = itr->second;
695 if (Aentry && Aentry->owner == player->GetGUID().GetCounter())
696 {
697 if (Aentry->BuildAuctionInfo(data))
698 ++count;
699
700 ++totalcount;
701 }
702 }
703}
704
706 std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
707 uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
708 uint32& count, uint32& totalcount, bool getall)
709{
710 LocaleConstant localeConstant = player->GetSession()->GetSessionDbLocaleIndex();
711 int locdbc_idx = player->GetSession()->GetSessionDbcLocale();
712
713 time_t curTime = GameTime::GetGameTime();
714
715 auto itr = GetAllThrottleMap.find(player->GetGUID());
716 time_t throttleTime = itr != GetAllThrottleMap.end() ? itr->second : curTime;
717
718 if (getall && throttleTime <= curTime)
719 {
720 for (AuctionEntryMap::const_iterator it = AuctionsMap.begin(); it != AuctionsMap.end(); ++it)
721 {
722 AuctionEntry* Aentry = it->second;
723 // Skip expired auctions
724 if (Aentry->expire_time < curTime)
725 continue;
726
727 Item* item = sAuctionMgr->GetAItem(Aentry->itemGUIDLow);
728 if (!item)
729 continue;
730
731 ++count;
732 ++totalcount;
733 Aentry->BuildAuctionInfo(data, item);
734
735 if (count >= MAX_GETALL_RETURN)
736 break;
737 }
738 GetAllThrottleMap[player->GetGUID()] = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY);
739 return;
740 }
741
742 for (AuctionEntryMap::const_iterator it = AuctionsMap.begin(); it != AuctionsMap.end(); ++it)
743 {
744 AuctionEntry* Aentry = it->second;
745 // Skip expired auctions
746 if (Aentry->expire_time < curTime)
747 continue;
748
749 Item* item = sAuctionMgr->GetAItem(Aentry->itemGUIDLow);
750 if (!item)
751 continue;
752
753 ItemTemplate const* proto = item->GetTemplate();
754
755 if (itemClass != 0xffffffff && proto->GetClass() != itemClass)
756 continue;
757
758 if (itemSubClass != 0xffffffff && proto->GetSubClass() != itemSubClass)
759 continue;
760
761 if (inventoryType != 0xffffffff && proto->GetInventoryType() != inventoryType)
762 {
763 // Cloth items can have INVTYPE_CHEST or INVTYPE_ROBE
764 if (!(inventoryType == INVTYPE_CHEST && proto->GetInventoryType() == INVTYPE_ROBE))
765 continue;
766 }
767
768 if (quality != 0xffffffff && proto->GetQuality() != quality)
769 continue;
770
771 if (levelmin != 0 && (proto->GetRequiredLevel() < levelmin || (levelmax != 0 && proto->GetRequiredLevel() > levelmax)))
772 continue;
773
774 if (usable != 0 && player->CanUseItem(item) != EQUIP_ERR_OK)
775 continue;
776
777 // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey)
778 // No need to do any of this if no search term was entered
779 if (!wsearchedname.empty())
780 {
781 std::string name = proto->GetDefaultLocaleName();
782 if (name.empty())
783 continue;
784
785 // local name
786 if (localeConstant != LOCALE_enUS)
787 if (ItemLocale const* il = sObjectMgr->GetItemLocale(proto->GetId()))
788 ObjectMgr::GetLocaleString(il->Name, localeConstant, name);
789
790 // DO NOT use GetItemEnchantMod(proto->RandomProperty) as it may return a result
791 // that matches the search but it may not equal item->GetItemRandomPropertyId()
792 // used in BuildAuctionInfo() which then causes wrong items to be listed
793 int32 propRefID = item->GetItemRandomPropertyId();
794
795 if (propRefID)
796 {
797 // Append the suffix to the name (ie: of the Monkey) if one exists
798 // These are found in ItemRandomSuffix.dbc and ItemRandomProperties.dbc
799 // even though the DBC names seem misleading
800
801 std::array<char const*, 16> const* suffix = nullptr;
802
803 if (propRefID < 0)
804 {
805 ItemRandomSuffixEntry const* itemRandSuffix = sItemRandomSuffixStore.LookupEntry(-propRefID);
806 if (itemRandSuffix)
807 suffix = &itemRandSuffix->Name;
808 }
809 else
810 {
811 ItemRandomPropertiesEntry const* itemRandProp = sItemRandomPropertiesStore.LookupEntry(propRefID);
812 if (itemRandProp)
813 suffix = &itemRandProp->Name;
814 }
815
816 // dbc local name
817 if (suffix)
818 {
819 // Append the suffix (ie: of the Monkey) to the name using localization
820 // or default enUS if localization is invalid
821 name += ' ';
822 name += (*suffix)[locdbc_idx >= 0 ? locdbc_idx : LOCALE_enUS];
823 }
824 }
825
826 // Perform the search (with or without suffix)
827 if (!Utf8FitTo(name, wsearchedname))
828 continue;
829 }
830
831 // Add the item if no search term or if entered search term was found
832 if (count < 50 && totalcount >= listfrom)
833 {
834 ++count;
835 Aentry->BuildAuctionInfo(data, item);
836 }
837 ++totalcount;
838 }
839}
840
841//this function inserts to WorldPacket auction's data
842bool AuctionEntry::BuildAuctionInfo(WorldPacket& data, Item* sourceItem) const
843{
844 Item* item = (sourceItem) ? sourceItem : sAuctionMgr->GetAItem(itemGUIDLow);
845 if (!item)
846 {
847 TC_LOG_ERROR("misc", "AuctionEntry::BuildAuctionInfo: Auction {} has a non-existent item: {}", Id, itemGUIDLow);
848 return false;
849 }
850 data << uint32(Id);
851 data << uint32(item->GetEntry());
852
853 for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
854 {
855 data << uint32(item->GetEnchantmentId(EnchantmentSlot(i)));
858 }
859
860 data << int32(item->GetItemRandomPropertyId()); // Random item property id
861 data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
862 data << uint32(item->GetCount()); // item->count
863 data << uint32(item->GetSpellCharges()); // item->charge FFFFFFF
864 data << uint32(item->GetUInt32Value(ITEM_FIELD_FLAGS)); // item flags
865 data << uint64(owner); // Auction->owner
866 data << uint32(startbid); // Auction->startbid (not sure if useful)
867 data << uint32(bid ? GetAuctionOutBid() : 0);
868 // Minimal outbid
869 data << uint32(buyout); // Auction->buyout
870 data << uint32((expire_time - GameTime::GetGameTime()) * IN_MILLISECONDS); // time left
871 data << uint64(bidder); // auction->bidder current
872 data << uint32(bid); // current bid
873 return true;
874}
875
877{
879 return std::max(cut, 0);
880}
881
884{
885 uint32 outbid = CalculatePct(bid, 5);
886 return outbid ? outbid : 1;
887}
888
890{
892
893 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION);
894 stmt->setUInt32(0, Id);
895 trans->Append(stmt);
896
897 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION_BIDDERS);
898 stmt->setUInt32(0, Id);
899 trans->Append(stmt);
900}
901
903{
905 stmt->setUInt32(0, Id);
906 stmt->setUInt8(1, houseId);
907 stmt->setUInt32(2, itemGUIDLow);
908 stmt->setUInt32(3, owner);
909 stmt->setUInt32(4, buyout);
910 stmt->setUInt32(5, uint32(expire_time));
911 stmt->setUInt32(6, bidder);
912 stmt->setUInt32(7, bid);
913 stmt->setUInt32(8, startbid);
914 stmt->setUInt32(9, deposit);
915 stmt->setUInt8(10, Flags);
916 trans->Append(stmt);
917}
918
920{
921 Id = fields[0].GetUInt32();
922 houseId = fields[1].GetUInt8();
923 itemGUIDLow = fields[2].GetUInt32();
924 itemEntry = fields[3].GetUInt32();
925 itemCount = fields[4].GetUInt32();
926 owner = fields[5].GetUInt32();
927 buyout = fields[6].GetUInt32();
928 expire_time = fields[7].GetUInt32();
929 bidder = fields[8].GetUInt32();
930 bid = fields[9].GetUInt32();
931 startbid = fields[10].GetUInt32();
932 deposit = fields[11].GetUInt32();
933 Flags = AuctionEntryFlag(fields[12].GetUInt8());
934
937 {
938 TC_LOG_ERROR("misc", "Auction {} has invalid house id {}", Id, houseId);
939 return false;
940 }
941
942 // check if sold item exists for guid
943 // and itemEntry in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems)
944 if (!sAuctionMgr->GetAItem(itemGUIDLow))
945 {
946 TC_LOG_ERROR("misc", "Auction {} has not a existing item : {}", Id, itemGUIDLow);
947 return false;
948 }
949
950 return true;
951}
953{
954 Item* item = sAuctionMgr->GetAItem(itemGUIDLow);
955 return Trinity::StringFormat("{}:{}:{}:{}:{}", itemEntry, item ? item->GetItemRandomPropertyId() : 0, response, Id, itemCount);
956}
957
959{
960 return Trinity::StringFormat("{:X}:{}:{}", guid.GetRawValue(), bid, buyout);
961}
962
963std::string AuctionEntry::BuildAuctionSoldMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 consignment)
964{
965 return Trinity::StringFormat("{:X}:{}:{}:{}:{}", guid.GetRawValue(), bid, buyout, deposit, consignment);
966}
967
968std::string AuctionEntry::BuildAuctionInvoiceMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 consignment, uint32 moneyDelay, uint32 eta)
969{
970 return Trinity::StringFormat("{:X}:{}:{}:{}:{}:{}:{}", guid.GetRawValue(), bid, buyout, deposit, consignment, moneyDelay, eta);
971}
#define sAuctionBotConfig
eAuctionHouse
@ AH_MINIMUM_DEPOSIT
MailAuctionAnswers
@ AUCTION_EXPIRED
@ AUCTION_CANCELLED_TO_BIDDER
@ AUCTION_SALE_PENDING
@ AUCTION_SUCCESSFUL
@ AUCTION_WON
@ AUCTION_OUTBIDDED
AuctionEntryFlag
@ AUCTION_ENTRY_FLAG_NONE
@ AUCTION_ENTRY_FLAG_GM_LOG_BUYER
@ AUCTIONHOUSE_HORDE
@ AUCTIONHOUSE_ALLIANCE
@ AUCTIONHOUSE_NEUTRAL
#define MAX_GETALL_RETURN
#define sAuctionMgr
Item * NewItemOrBag(ItemTemplate const *proto)
Definition Bag.h:69
#define sCharacterCache
@ CHAR_UPD_ITEM_OWNER
@ CHAR_SEL_AUCTION_ITEMS
@ CHAR_SEL_AUCTION_BIDDERS
@ CHAR_DEL_AUCTION_BIDDERS
@ CHAR_DEL_AUCTION
@ CHAR_INS_AUCTION
@ CHAR_SEL_AUCTIONS
LocaleConstant
Definition Common.h:48
@ LOCALE_enUS
Definition Common.h:49
@ IN_MILLISECONDS
Definition Common.h:35
@ FACTION_MASK_ALLIANCE
Definition DBCEnums.h:325
@ FACTION_MASK_HORDE
Definition DBCEnums.h:326
@ ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS
Definition DBCEnums.h:202
@ ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS
Definition DBCEnums.h:205
@ ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD
Definition DBCEnums.h:206
DBCStorage< ItemRandomSuffixEntry > sItemRandomSuffixStore(ItemRandomSuffixfmt)
DBCStorage< ItemRandomPropertiesEntry > sItemRandomPropertiesStore(ItemRandomPropertiesfmt)
DBCStorage< FactionTemplateEntry > sFactionTemplateStore(FactionTemplateEntryfmt)
DBCStorage< AuctionHouseEntry > sAuctionHouseStore(AuctionHouseEntryfmt)
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint64_t uint64
Definition Define.h:132
uint32_t uint32
Definition Define.h:133
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition Duration.h:27
#define ASSERT_WITH_SIDE_EFFECTS
Definition Errors.h:72
#define ASSERT
Definition Errors.h:68
EnchantmentSlot
@ MAX_INSPECTED_ENCHANTMENT_SLOT
@ EQUIP_ERR_OK
Definition ItemDefines.h:26
@ INVTYPE_ROBE
@ INVTYPE_CHEST
@ ITEM_REMOVED
Definition Item.h:56
@ LANG_UNKNOWN
Definition Language.h:77
#define TC_LOG_WARN(filterType__,...)
Definition Log.h:162
#define TC_LOG_DEBUG(filterType__,...)
Definition Log.h:156
#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_AUCTION_WON
This mail is delivering won auction items.
Definition Mail.h:54
@ MAIL_CHECK_MASK_COPIED
This mail was copied. Do not allow making a copy of items in mail.
Definition Mail.h:50
@ MAIL_CHECK_MASK_AUCTION
This mail came from auction house.
Definition Mail.h:58
#define sObjectMgr
Definition ObjectMgr.h:1721
#define sScriptMgr
Definition ScriptMgr.h:1168
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
@ ITEM_FIELD_FLAGS
bool Utf8FitTo(std::string_view str, std::wstring_view search)
Definition Util.cpp:565
T CalculatePct(T base, U pct)
Definition Util.h:71
void PendingAuctionProcess(Player *player)
void SendAuctionExpiredMail(AuctionEntry *auction, CharacterDatabaseTransaction trans)
bool RemoveAItem(ObjectGuid::LowType id, bool deleteItem=false, CharacterDatabaseTransaction *trans=nullptr)
AuctionHouseObject * GetAuctionsMapByHouseId(uint8 auctionHouseId)
static AuctionHouseEntry const * GetAuctionHouseEntry(uint32 factionTemplateId)
std::pair< PlayerAuctions *, uint32 > AuctionPair
void SendAuctionSuccessfulMail(AuctionEntry *auction, CharacterDatabaseTransaction trans)
static AuctionHouseEntry const * GetAuctionHouseEntryFromHouse(uint8 houseId)
void SendAuctionWonMail(AuctionEntry *auction, CharacterDatabaseTransaction trans)
static uint32 GetAuctionDeposit(AuctionHouseEntry const *entry, uint32 time, Item *pItem, uint32 count)
void SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice, Player *newBidder, CharacterDatabaseTransaction trans)
void SendAuctionSalePendingMail(AuctionEntry *auction, CharacterDatabaseTransaction trans)
AuctionHouseObject * GetAuctionsMap(uint32 factionTemplateId)
AuctionHouseObject mNeutralAuctions
std::vector< AuctionEntry * > PlayerAuctions
std::map< ObjectGuid, AuctionPair > pendingAuctionMap
AuctionHouseObject mHordeAuctions
AuctionHouseObject mAllianceAuctions
Item * GetAItem(ObjectGuid::LowType id)
void AddAItem(Item *item)
void SendAuctionCancelledToBidderMail(AuctionEntry *auction, CharacterDatabaseTransaction trans, Item *item)
bool PendingAuctionAdd(Player *player, AuctionEntry *aEntry)
uint32 PendingAuctionCount(Player const *player) const
static AuctionHouseMgr * instance()
void BuildListBidderItems(WorldPacket &data, Player *player, uint32 &count, uint32 &totalcount)
PlayerGetAllThrottleMap GetAllThrottleMap
AuctionEntryMap AuctionsMap
void AddAuction(AuctionEntry *auction)
void BuildListAuctionItems(WorldPacket &data, Player *player, std::wstring const &searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, uint32 &count, uint32 &totalcount, bool getall=false)
void BuildListOwnerItems(WorldPacket &data, Player *player, uint32 &count, uint32 &totalcount)
bool RemoveAuction(AuctionEntry *auction)
Class used to access individual fields of database query result.
Definition Field.h:92
uint8 GetUInt8() const
Definition Field.cpp:29
uint32 GetUInt32() const
Definition Field.cpp:61
Definition Item.h:62
std::string const & GetNameForLocaleIdx(LocaleConstant locale) const override
Definition Item.cpp:297
virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field *fields, uint32 entry)
Definition Item.cpp:415
uint32 GetEnchantmentId(EnchantmentSlot slot) const
Definition Item.h:165
int32 GetSpellCharges(uint8 index=0) const
Definition Item.h:179
uint32 GetEnchantmentDuration(EnchantmentSlot slot) const
Definition Item.h:166
int32 GetItemRandomPropertyId() const
Definition Item.h:157
ItemTemplate const * GetTemplate() const
Definition Item.cpp:541
uint32 GetItemSuffixFactor() const
Definition Item.h:158
uint32 GetSellPrice(bool forVendor=false) const
Definition Item.cpp:1148
uint32 GetEnchantmentCharges(EnchantmentSlot slot) const
Definition Item.h:167
uint32 GetCount() const
Definition Item.h:136
MailDraft & AddMoney(uint32 money)
Definition Mail.h:137
void SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const &receiver, MailSender const &sender, MailCheckMask checked=MAIL_CHECK_MASK_NONE, uint32 deliver_delay=0)
Definition Mail.cpp:188
MailDraft & AddItem(Item *item)
Definition Mail.cpp:90
LowType GetCounter() const
Definition ObjectGuid.h:156
static ObjectGuid const Empty
Definition ObjectGuid.h:140
uint64 GetRawValue() const
Definition ObjectGuid.h:148
std::string ToString() const
uint32 LowType
Definition ObjectGuid.h:142
static std::string_view GetLocaleString(std::vector< std::string > const &data, LocaleConstant locale)
Definition ObjectMgr.h:1519
uint32 GetUInt32Value(uint16 index) const
Definition Object.cpp:241
uint32 GetEntry() const
Definition Object.h:81
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1=0, uint32 miscValue2=0, WorldObject *ref=nullptr)
Definition Player.cpp:24914
bool ModifyMoney(int32 amount, bool sendError=true)
Definition Player.cpp:22336
WorldSession * GetSession() const
Definition Player.h:1705
bool HasEnoughMoney(uint32 amount) const
Definition Player.h:1394
InventoryResult CanUseItem(Item *pItem, bool not_loading=true) const
Definition Player.cpp:11368
void setUInt32(uint8 index, uint32 value)
void setUInt8(uint8 index, uint8 value)
std::string const & GetName() const
Definition Object.h:384
LocaleConstant GetSessionDbLocaleIndex() const
void SendAuctionRemovedNotification(uint32 auctionId, uint32 itemEntry, int32 randomPropertyId)
Minutes GetTimezoneOffset() const
LocaleConstant GetSessionDbcLocale() const
void SendAuctionOwnerNotification(AuctionEntry *auction)
uint32 GetAccountId() const
void SendAuctionBidderNotification(uint32 location, uint32 auctionId, ObjectGuid bidder, uint32 bidSum, uint32 diff, uint32 item_template)
uint32 GetPackedTime() const
Definition WowTime.cpp:23
#define sWorld
Definition World.h:901
@ CONFIG_AUCTION_GETALL_DELAY
Definition World.h:392
@ CONFIG_MAIL_DELIVERY_DELAY
Definition World.h:269
@ RATE_AUCTION_DEPOSIT
Definition World.h:460
@ RATE_AUCTION_CUT
Definition World.h:461
@ CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION
Definition World.h:100
WowTime const * GetUtcWowTime()
Definition GameTime.cpp:67
time_t GetGameTime()
Definition GameTime.cpp:42
TC_GAME_API Player * FindConnectedPlayer(ObjectGuid const &)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
ObjectGuid::LowType bidder
static std::string BuildAuctionInvoiceMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 consignment, uint32 moneyDelay, uint32 eta)
uint32 GetAuctionOutBid() const
the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
void DeleteFromDB(CharacterDatabaseTransaction trans) const
uint8 GetHouseId() const
AuctionHouseEntry const * auctionHouseEntry
std::unordered_set< ObjectGuid > bidders
void SaveToDB(CharacterDatabaseTransaction trans) const
ObjectGuid::LowType owner
std::string BuildAuctionMailSubject(MailAuctionAnswers response) const
uint32 GetAuctionCut() const
static std::string BuildAuctionWonMailBody(ObjectGuid guid, uint32 bid, uint32 buyout)
bool LoadFromDB(Field *fields)
ObjectGuid::LowType itemGUIDLow
static std::string BuildAuctionSoldMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 consignment)
AuctionEntryFlag Flags
bool BuildAuctionInfo(WorldPacket &data, Item *sourceItem=nullptr) const
std::array< char const *, 16 > Name
std::array< char const *, 16 > Name
std::string const & GetDefaultLocaleName() const
uint32 GetQuality() const
uint32 GetId() const
int32 GetRequiredLevel() const
uint32 GetSubClass() const
::InventoryType GetInventoryType() const
uint32 GetClass() const