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->GetTemplate()->SellPrice;
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->GetTemplate()->Name1, pItem->GetEntry(), pItem->GetCount(), auction->bid, ownerName, ownerAccId);
159 }
160
161 // receiver exist
162 if ((bidder || bidderAccId) && !sAuctionBotConfig->IsBotChar(auction->bidder))
163 {
164 // set owner to bidder (to prevent delete item with sender char deleting)
165 // owner in `data` will set at mail receive and item extracting
167 stmt->setUInt32(0, auction->bidder);
168 stmt->setUInt32(1, pItem->GetGUID().GetCounter());
169 trans->Append(stmt);
170
171 if (bidder)
172 {
173 bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, bidderGuid, 0, 0, auction->itemEntry);
174 // FIXME: for offline player need also
176 }
177
178 MailDraft(auction->BuildAuctionMailSubject(AUCTION_WON), AuctionEntry::BuildAuctionWonMailBody(ObjectGuid::Create<HighGuid::Player>(auction->owner), auction->bid, auction->buyout))
179 .AddItem(pItem)
180 .SendMailTo(trans, MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
181 }
182 else
183 {
184 // bidder doesn't exist, delete the item
185 sAuctionMgr->RemoveAItem(auction->itemGUIDLow, true, &trans);
186 }
187}
188
190{
191 ObjectGuid owner_guid = ObjectGuid::Create<HighGuid::Player>(auction->owner);
192 Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid);
193 uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(owner_guid);
194 // owner exist (online or offline)
195 if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner))
196 {
198 eta += Seconds(sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY));
199 if (owner)
200 eta += owner->GetSession()->GetTimezoneOffset();
201
203 AuctionEntry::BuildAuctionInvoiceMailBody(ObjectGuid::Create<HighGuid::Player>(auction->bidder), auction->bid, auction->buyout, auction->deposit,
204 auction->GetAuctionCut(), sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY), eta.GetPackedTime()))
205 .SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED);
206 }
207}
208
209//call this method to send mail to auction owner, when auction is successful, it does not clear ram
211{
212 ObjectGuid owner_guid = ObjectGuid::Create<HighGuid::Player>(auction->owner);
213 Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid);
214 uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(owner_guid);
215 // owner exist
216 if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner))
217 {
218 uint32 profit = auction->bid + auction->deposit - auction->GetAuctionCut();
219
220 //FIXME: what do if owner offline
221 if (owner)
222 {
225 //send auction owner notification, bidder must be current!
226 owner->GetSession()->SendAuctionOwnerNotification(auction);
227 }
228
229 MailDraft(auction->BuildAuctionMailSubject(AUCTION_SUCCESSFUL), AuctionEntry::BuildAuctionSoldMailBody(ObjectGuid::Create<HighGuid::Player>(auction->bidder), auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut()))
230 .AddMoney(profit)
231 .SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED, sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY));
232 }
233}
234
235//does not clear ram
237{
238 //return an item in auction to its owner by mail
239 Item* pItem = GetAItem(auction->itemGUIDLow);
240 if (!pItem)
241 return;
242
243 ObjectGuid owner_guid = ObjectGuid::Create<HighGuid::Player>(auction->owner);
244 Player* owner = ObjectAccessor::FindConnectedPlayer(owner_guid);
245 uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(owner_guid);
246 // owner exist
247 if ((owner || owner_accId) && !sAuctionBotConfig->IsBotChar(auction->owner))
248 {
249 if (owner)
250 owner->GetSession()->SendAuctionOwnerNotification(auction);
251
253 .AddItem(pItem)
254 .SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED, 0);
255 }
256 else
257 {
258 // owner doesn't exist, delete the item
259 sAuctionMgr->RemoveAItem(auction->itemGUIDLow, true, &trans);
260 }
261}
262
263//this function sends mail to old bidder
265{
266 ObjectGuid oldBidder_guid = ObjectGuid::Create<HighGuid::Player>(auction->bidder);
267 Player* oldBidder = ObjectAccessor::FindConnectedPlayer(oldBidder_guid);
268
269 uint32 oldBidder_accId = 0;
270 if (!oldBidder)
271 oldBidder_accId = sCharacterCache->GetCharacterAccountIdByGuid(oldBidder_guid);
272
273 // old bidder exist
274 if ((oldBidder || oldBidder_accId) && !sAuctionBotConfig->IsBotChar(auction->bidder))
275 {
276 if (oldBidder && newBidder)
277 oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->itemEntry);
278
280 .AddMoney(auction->bid)
281 .SendMailTo(trans, MailReceiver(oldBidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
282 }
283}
284
285//this function sends mail, when auction is cancelled to old bidder
287{
288 ObjectGuid bidder_guid = ObjectGuid::Create<HighGuid::Player>(auction->bidder);
289 Player* bidder = ObjectAccessor::FindConnectedPlayer(bidder_guid);
290
291 uint32 bidder_accId = 0;
292 if (!bidder)
293 bidder_accId = sCharacterCache->GetCharacterAccountIdByGuid(bidder_guid);
294
295 if (bidder)
296 bidder->GetSession()->SendAuctionRemovedNotification(auction->Id, auction->itemEntry, item->GetItemRandomPropertyId());
297
298 // bidder exist
299 if ((bidder || bidder_accId) && !sAuctionBotConfig->IsBotChar(auction->bidder))
301 .AddMoney(auction->bid)
302 .SendMailTo(trans, MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
303}
304
306{
307 uint32 oldMSTime = getMSTime();
308
309 // need to clear in case we are reloading
310 if (!mAitems.empty())
311 {
312 for (ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
313 delete itr->second;
314
315 mAitems.clear();
316 }
317
318 // data needs to be at first place for Item::LoadFromDB
320 PreparedQueryResult result = CharacterDatabase.Query(stmt);
321
322 if (!result)
323 {
324 TC_LOG_INFO("server.loading", ">> Loaded 0 auction items. DB table `auctionhouse` or `item_instance` is empty!");
325
326 return;
327 }
328
329 uint32 count = 0;
330
331 do
332 {
333 Field* fields = result->Fetch();
334
335 ObjectGuid::LowType item_guid = fields[11].GetUInt32();
336 uint32 itemEntry = fields[12].GetUInt32();
337
338 ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
339 if (!proto)
340 {
341 TC_LOG_ERROR("misc", "AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: {} item entry: #{}) in auction, skipped.", item_guid, itemEntry);
342 continue;
343 }
344
345 Item* item = NewItemOrBag(proto);
346 if (!item->LoadFromDB(item_guid, ObjectGuid::Empty, fields, itemEntry))
347 {
348 delete item;
349 continue;
350 }
351 AddAItem(item);
352
353 ++count;
354 }
355 while (result->NextRow());
356
357 TC_LOG_INFO("server.loading", ">> Loaded {} auction items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
358
359}
360
362{
363 uint32 oldMSTime = getMSTime();
364
366 PreparedQueryResult resultAuctions = CharacterDatabase.Query(stmt);
367
368 if (!resultAuctions)
369 {
370 TC_LOG_INFO("server.loading", ">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
371
372 return;
373 }
374
375 // parse bidder list
376 std::unordered_map<uint32, std::unordered_set<ObjectGuid>> biddersByAuction;
378
379 uint32 countBidders = 0;
380 if (PreparedQueryResult resultBidders = CharacterDatabase.Query(stmt2))
381 {
382 do
383 {
384 Field* fields = resultBidders->Fetch();
385 biddersByAuction[fields[0].GetUInt32()].insert(ObjectGuid::Create<HighGuid::Player>(fields[1].GetUInt32()));
386 ++countBidders;
387 }
388 while (resultBidders->NextRow());
389 }
390
391 // parse auctions from db
392 uint32 countAuctions = 0;
393 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
394 do
395 {
396 Field* fields = resultAuctions->Fetch();
397
398 AuctionEntry* aItem = new AuctionEntry();
399 if (!aItem->LoadFromDB(fields))
400 {
401 aItem->DeleteFromDB(trans);
402 delete aItem;
403 continue;
404 }
405
406 auto it = biddersByAuction.find(aItem->Id);
407 if (it != biddersByAuction.end())
408 aItem->bidders = std::move(it->second);
409
411 ++countAuctions;
412 } while (resultAuctions->NextRow());
413
414 CharacterDatabase.CommitTransaction(trans);
415
416 TC_LOG_INFO("server.loading", ">> Loaded {} auctions with {} bidders in {} ms", countAuctions, countBidders, GetMSTimeDiffToNow(oldMSTime));
417}
418
420{
421 ASSERT(item);
422 ASSERT_WITH_SIDE_EFFECTS(mAitems.emplace(item->GetGUID().GetCounter(), item).second);
423}
424
425bool AuctionHouseMgr::RemoveAItem(ObjectGuid::LowType id, bool deleteItem /*= false*/, CharacterDatabaseTransaction* trans /*= nullptr*/)
426{
427 ItemMap::iterator i = mAitems.find(id);
428 if (i == mAitems.end())
429 return false;
430
431 if (deleteItem)
432 {
433 ASSERT(trans);
434 i->second->FSetState(ITEM_REMOVED);
435 i->second->SaveToDB(*trans);
436 }
437
438 mAitems.erase(i);
439 return true;
440}
441
443{
444 PlayerAuctions* thisAH;
445 auto itr = pendingAuctionMap.find(player->GetGUID());
446 if (itr != pendingAuctionMap.end())
447 {
448 thisAH = itr->second.first;
449
450 // Get deposit so far
451 uint32 totalDeposit = 0;
452 for (AuctionEntry const* thisAuction : *thisAH)
453 totalDeposit += thisAuction->deposit;
454
455 // Add this deposit
456 totalDeposit += aEntry->deposit;
457
458 if (!player->HasEnoughMoney(totalDeposit))
459 return false;
460 }
461 else
462 {
463 thisAH = new PlayerAuctions;
464 pendingAuctionMap[player->GetGUID()] = AuctionPair(thisAH, 0);
465 }
466 thisAH->push_back(aEntry);
467 return true;
468}
469
471{
472 auto const itr = pendingAuctionMap.find(player->GetGUID());
473 if (itr != pendingAuctionMap.end())
474 return itr->second.first->size();
475
476 return 0;
477}
478
480{
481 auto iterMap = pendingAuctionMap.find(player->GetGUID());
482 if (iterMap == pendingAuctionMap.end())
483 return;
484
485 PlayerAuctions* thisAH = iterMap->second.first;
486
487 uint32 totaldeposit = 0;
488 auto itrAH = thisAH->begin();
489 for (; itrAH != thisAH->end(); ++itrAH)
490 {
491 AuctionEntry* AH = (*itrAH);
492 if (!player->HasEnoughMoney(totaldeposit + AH->deposit))
493 break;
494
495 totaldeposit += AH->deposit;
496 }
497
498 // expire auctions we cannot afford
499 if (itrAH != thisAH->end())
500 {
501 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
502
503 do
504 {
505 AuctionEntry* AH = (*itrAH);
507 AH->DeleteFromDB(trans);
508 AH->SaveToDB(trans);
509 ++itrAH;
510 } while (itrAH != thisAH->end());
511
512 CharacterDatabase.CommitTransaction(trans);
513 }
514
515 pendingAuctionMap.erase(player->GetGUID());
516 delete thisAH;
517 player->ModifyMoney(-int32(totaldeposit));
518}
519
521{
522 for (auto itr = pendingAuctionMap.begin(); itr != pendingAuctionMap.end();)
523 {
524 ObjectGuid playerGUID = itr->first;
525 if (Player* player = ObjectAccessor::FindConnectedPlayer(playerGUID))
526 {
527 // Check if there were auctions since last update process if not
528 if (PendingAuctionCount(player) == itr->second.second)
529 {
530 ++itr;
531 PendingAuctionProcess(player);
532 }
533 else
534 {
535 ++itr;
536 pendingAuctionMap[playerGUID].second = PendingAuctionCount(player);
537 }
538 }
539 else
540 {
541 // Expire any auctions that we couldn't get a deposit for
542 TC_LOG_WARN("auctionHouse", "Player {} was offline, unable to retrieve deposit!", playerGUID.ToString());
543 PlayerAuctions* thisAH = itr->second.first;
544 ++itr;
545 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
546 for (auto AHitr = thisAH->begin(); AHitr != thisAH->end();)
547 {
548 AuctionEntry* AH = (*AHitr);
549 ++AHitr;
551 AH->DeleteFromDB(trans);
552 AH->SaveToDB(trans);
553 }
554 CharacterDatabase.CommitTransaction(trans);
555 pendingAuctionMap.erase(playerGUID);
556 delete thisAH;
557 }
558 }
559}
560
567
569{
570 uint32 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house
571
573 {
574 // FIXME: found way for proper auctionhouse selection by another way
575 // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races.
576 // but no easy way convert creature faction to player race faction for specific city
577 FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId);
578 if (!u_entry)
579 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house
580 else if (u_entry->FactionGroup & FACTION_MASK_ALLIANCE)
581 houseid = AUCTIONHOUSE_ALLIANCE; // human auction house
582 else if (u_entry->FactionGroup & FACTION_MASK_HORDE)
583 houseid = AUCTIONHOUSE_HORDE; // orc auction house
584 else
585 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house
586 }
587
588 return sAuctionHouseStore.LookupEntry(houseid);
589}
590
595
597{
598 ASSERT(auction);
599
600 AuctionsMap[auction->Id] = auction;
601 sScriptMgr->OnAuctionAdd(this, auction);
602}
603
605{
606 bool wasInMap = AuctionsMap.erase(auction->Id) ? true : false;
607
608 sScriptMgr->OnAuctionRemove(this, auction);
609
610 // we need to delete the entry, it is not referenced any more
611 delete auction;
612 return wasInMap;
613}
614
616{
617 time_t curTime = GameTime::GetGameTime();
619
620 // If storage is empty, no need to update. next == NULL in this case.
621 if (AuctionsMap.empty())
622 return;
623
624 // Clear expired throttled players
625 for (PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.begin(); itr != GetAllThrottleMap.end();)
626 {
627 if (itr->second <= curTime)
628 itr = GetAllThrottleMap.erase(itr);
629 else
630 ++itr;
631 }
632
633 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
634
635 for (AuctionEntryMap::iterator it = AuctionsMap.begin(); it != AuctionsMap.end();)
636 {
637 // from auctionhousehandler.cpp, creates auction pointer & player pointer
638 AuctionEntry* auction = it->second;
639 // Increment iterator due to AuctionEntry deletion
640 ++it;
641
643 if (auction->expire_time > curTime + 60)
644 continue;
645
647 if (!auction->bidder && auction->bid == 0)
648 {
649 sAuctionMgr->SendAuctionExpiredMail(auction, trans);
650 sScriptMgr->OnAuctionExpire(this, auction);
651 }
653 else
654 {
655 //we should send an "item sold" message if the seller is online
656 //we send the item to the winner
657 //we send the money to the seller
658 sAuctionMgr->SendAuctionSuccessfulMail(auction, trans);
659 sAuctionMgr->SendAuctionWonMail(auction, trans);
660 sScriptMgr->OnAuctionSuccessful(this, auction);
661 }
662
664 auction->DeleteFromDB(trans);
665
666 sAuctionMgr->RemoveAItem(auction->itemGUIDLow);
667 RemoveAuction(auction);
668 }
669
670 // Run DB changes
671 CharacterDatabase.CommitTransaction(trans);
672}
673
675{
676 for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
677 {
678 AuctionEntry* Aentry = itr->second;
679 if (Aentry && Aentry->bidders.find(player->GetGUID()) != Aentry->bidders.end())
680 {
681 if (itr->second->BuildAuctionInfo(data))
682 ++count;
683
684 ++totalcount;
685 }
686 }
687}
688
690{
691 for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
692 {
693 AuctionEntry* Aentry = itr->second;
694 if (Aentry && Aentry->owner == player->GetGUID().GetCounter())
695 {
696 if (Aentry->BuildAuctionInfo(data))
697 ++count;
698
699 ++totalcount;
700 }
701 }
702}
703
705 std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
706 uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
707 uint32& count, uint32& totalcount, bool getall)
708{
709 LocaleConstant localeConstant = player->GetSession()->GetSessionDbLocaleIndex();
710 int locdbc_idx = player->GetSession()->GetSessionDbcLocale();
711
712 time_t curTime = GameTime::GetGameTime();
713
714 auto itr = GetAllThrottleMap.find(player->GetGUID());
715 time_t throttleTime = itr != GetAllThrottleMap.end() ? itr->second : curTime;
716
717 if (getall && throttleTime <= curTime)
718 {
719 for (AuctionEntryMap::const_iterator it = AuctionsMap.begin(); it != AuctionsMap.end(); ++it)
720 {
721 AuctionEntry* Aentry = it->second;
722 // Skip expired auctions
723 if (Aentry->expire_time < curTime)
724 continue;
725
726 Item* item = sAuctionMgr->GetAItem(Aentry->itemGUIDLow);
727 if (!item)
728 continue;
729
730 ++count;
731 ++totalcount;
732 Aentry->BuildAuctionInfo(data, item);
733
734 if (count >= MAX_GETALL_RETURN)
735 break;
736 }
737 GetAllThrottleMap[player->GetGUID()] = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY);
738 return;
739 }
740
741 for (AuctionEntryMap::const_iterator it = AuctionsMap.begin(); it != AuctionsMap.end(); ++it)
742 {
743 AuctionEntry* Aentry = it->second;
744 // Skip expired auctions
745 if (Aentry->expire_time < curTime)
746 continue;
747
748 Item* item = sAuctionMgr->GetAItem(Aentry->itemGUIDLow);
749 if (!item)
750 continue;
751
752 ItemTemplate const* proto = item->GetTemplate();
753
754 if (itemClass != 0xffffffff && proto->Class != itemClass)
755 continue;
756
757 if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass)
758 continue;
759
760 if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType)
761 {
762 // Cloth items can have INVTYPE_CHEST or INVTYPE_ROBE
763 if (!(inventoryType == INVTYPE_CHEST && proto->InventoryType == INVTYPE_ROBE))
764 continue;
765 }
766
767 if (quality != 0xffffffff && proto->Quality != quality)
768 continue;
769
770 if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax)))
771 continue;
772
773 if (usable != 0x00 && player->CanUseItem(item) != EQUIP_ERR_OK)
774 continue;
775
776 // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey)
777 // No need to do any of this if no search term was entered
778 if (!wsearchedname.empty())
779 {
780 std::string name = proto->Name1;
781 if (name.empty())
782 continue;
783
784 // local name
785 if (localeConstant != LOCALE_enUS)
786 if (ItemLocale const* il = sObjectMgr->GetItemLocale(proto->ItemId))
787 ObjectMgr::GetLocaleString(il->Name, localeConstant, name);
788
789 // DO NOT use GetItemEnchantMod(proto->RandomProperty) as it may return a result
790 // that matches the search but it may not equal item->GetItemRandomPropertyId()
791 // used in BuildAuctionInfo() which then causes wrong items to be listed
792 int32 propRefID = item->GetItemRandomPropertyId();
793
794 if (propRefID)
795 {
796 // Append the suffix to the name (ie: of the Monkey) if one exists
797 // These are found in ItemRandomSuffix.dbc and ItemRandomProperties.dbc
798 // even though the DBC names seem misleading
799
800 std::array<char const*, 16> const* suffix = nullptr;
801
802 if (propRefID < 0)
803 {
804 ItemRandomSuffixEntry const* itemRandSuffix = sItemRandomSuffixStore.LookupEntry(-propRefID);
805 if (itemRandSuffix)
806 suffix = &itemRandSuffix->Name;
807 }
808 else
809 {
810 ItemRandomPropertiesEntry const* itemRandProp = sItemRandomPropertiesStore.LookupEntry(propRefID);
811 if (itemRandProp)
812 suffix = &itemRandProp->Name;
813 }
814
815 // dbc local name
816 if (suffix)
817 {
818 // Append the suffix (ie: of the Monkey) to the name using localization
819 // or default enUS if localization is invalid
820 name += ' ';
821 name += (*suffix)[locdbc_idx >= 0 ? locdbc_idx : LOCALE_enUS];
822 }
823 }
824
825 // Perform the search (with or without suffix)
826 if (!Utf8FitTo(name, wsearchedname))
827 continue;
828 }
829
830 // Add the item if no search term or if entered search term was found
831 if (count < 50 && totalcount >= listfrom)
832 {
833 ++count;
834 Aentry->BuildAuctionInfo(data, item);
835 }
836 ++totalcount;
837 }
838}
839
840//this function inserts to WorldPacket auction's data
841bool AuctionEntry::BuildAuctionInfo(WorldPacket& data, Item* sourceItem) const
842{
843 Item* item = (sourceItem) ? sourceItem : sAuctionMgr->GetAItem(itemGUIDLow);
844 if (!item)
845 {
846 TC_LOG_ERROR("misc", "AuctionEntry::BuildAuctionInfo: Auction {} has a non-existent item: {}", Id, itemGUIDLow);
847 return false;
848 }
849 data << uint32(Id);
850 data << uint32(item->GetEntry());
851
852 for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
853 {
854 data << uint32(item->GetEnchantmentId(EnchantmentSlot(i)));
857 }
858
859 data << int32(item->GetItemRandomPropertyId()); // Random item property id
860 data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
861 data << uint32(item->GetCount()); // item->count
862 data << uint32(item->GetSpellCharges()); // item->charge FFFFFFF
863 data << uint32(item->GetUInt32Value(ITEM_FIELD_FLAGS)); // item flags
864 data << uint64(owner); // Auction->owner
865 data << uint32(startbid); // Auction->startbid (not sure if useful)
866 data << uint32(bid ? GetAuctionOutBid() : 0);
867 // Minimal outbid
868 data << uint32(buyout); // Auction->buyout
869 data << uint32((expire_time - GameTime::GetGameTime()) * IN_MILLISECONDS); // time left
870 data << uint64(bidder); // auction->bidder current
871 data << uint32(bid); // current bid
872 return true;
873}
874
876{
878 return std::max(cut, 0);
879}
880
883{
884 uint32 outbid = CalculatePct(bid, 5);
885 return outbid ? outbid : 1;
886}
887
889{
891
892 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION);
893 stmt->setUInt32(0, Id);
894 trans->Append(stmt);
895
896 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION_BIDDERS);
897 stmt->setUInt32(0, Id);
898 trans->Append(stmt);
899}
900
902{
904 stmt->setUInt32(0, Id);
905 stmt->setUInt8(1, houseId);
906 stmt->setUInt32(2, itemGUIDLow);
907 stmt->setUInt32(3, owner);
908 stmt->setUInt32(4, buyout);
909 stmt->setUInt32(5, uint32(expire_time));
910 stmt->setUInt32(6, bidder);
911 stmt->setUInt32(7, bid);
912 stmt->setUInt32(8, startbid);
913 stmt->setUInt32(9, deposit);
914 stmt->setUInt8(10, Flags);
915 trans->Append(stmt);
916}
917
919{
920 Id = fields[0].GetUInt32();
921 houseId = fields[1].GetUInt8();
922 itemGUIDLow = fields[2].GetUInt32();
923 itemEntry = fields[3].GetUInt32();
924 itemCount = fields[4].GetUInt32();
925 owner = fields[5].GetUInt32();
926 buyout = fields[6].GetUInt32();
927 expire_time = fields[7].GetUInt32();
928 bidder = fields[8].GetUInt32();
929 bid = fields[9].GetUInt32();
930 startbid = fields[10].GetUInt32();
931 deposit = fields[11].GetUInt32();
932 Flags = AuctionEntryFlag(fields[12].GetUInt8());
933
936 {
937 TC_LOG_ERROR("misc", "Auction {} has invalid house id {}", Id, houseId);
938 return false;
939 }
940
941 // check if sold item exists for guid
942 // and itemEntry in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems)
943 if (!sAuctionMgr->GetAItem(itemGUIDLow))
944 {
945 TC_LOG_ERROR("misc", "Auction {} has not a existing item : {}", Id, itemGUIDLow);
946 return false;
947 }
948
949 return true;
950}
952{
953 Item* item = sAuctionMgr->GetAItem(itemGUIDLow);
954 return Trinity::StringFormat("{}:{}:{}:{}:{}", itemEntry, item ? item->GetItemRandomPropertyId() : 0, response, Id, itemCount);
955}
956
958{
959 return Trinity::StringFormat("{:X}:{}:{}", guid.GetRawValue(), bid, buyout);
960}
961
962std::string AuctionEntry::BuildAuctionSoldMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 consignment)
963{
964 return Trinity::StringFormat("{:X}:{}:{}:{}:{}", guid.GetRawValue(), bid, buyout, deposit, consignment);
965}
966
967std::string AuctionEntry::BuildAuctionInvoiceMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 consignment, uint32 moneyDelay, uint32 eta)
968{
969 return Trinity::StringFormat("{:X}:{}:{}:{}:{}:{}:{}", guid.GetRawValue(), bid, buyout, deposit, consignment, moneyDelay, eta);
970}
#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:67
#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:327
@ FACTION_MASK_HORDE
Definition DBCEnums.h:328
@ 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_COPIED
This mail was returned. Do not allow returning mail back again.
Definition Mail.h:49
#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
virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field *fields, uint32 entry)
Definition Item.cpp:409
uint32 GetEnchantmentId(EnchantmentSlot slot) const
Definition Item.h:148
int32 GetSpellCharges(uint8 index=0) const
Definition Item.h:161
uint32 GetEnchantmentDuration(EnchantmentSlot slot) const
Definition Item.h:149
int32 GetItemRandomPropertyId() const
Definition Item.h:140
ItemTemplate const * GetTemplate() const
Definition Item.cpp:535
uint32 GetItemSuffixFactor() const
Definition Item.h:141
uint32 GetEnchantmentCharges(EnchantmentSlot slot) const
Definition Item.h:150
uint32 GetCount() const
Definition Item.h:119
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, size_t locale)
Definition ObjectMgr.h:1525
uint32 GetUInt32Value(uint16 index) const
Definition Object.cpp:249
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:24940
bool ModifyMoney(int32 amount, bool sendError=true)
Definition Player.cpp:22339
WorldSession * GetSession() const
Definition Player.h:1719
bool HasEnoughMoney(uint32 amount) const
Definition Player.h:1410
InventoryResult CanUseItem(Item *pItem, bool not_loading=true) const
Definition Player.cpp:11370
void setUInt32(uint8 index, uint32 value)
void setUInt8(uint8 index, uint8 value)
std::string const & GetName() const
Definition Object.h:382
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:900
@ CONFIG_AUCTION_GETALL_DELAY
Definition World.h:391
@ CONFIG_MAIL_DELIVERY_DELAY
Definition World.h:268
@ RATE_AUCTION_DEPOSIT
Definition World.h:459
@ RATE_AUCTION_CUT
Definition World.h:460
@ 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
uint32 RequiredLevel
std::string Name1
uint32 InventoryType