TrinityCore
Loading...
Searching...
No Matches
AuctionHouseBotBuyer.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
19#include "GameTime.h"
20#include "DatabaseEnv.h"
21#include "Item.h"
22#include "ItemTemplate.h"
23#include "Log.h"
24#include "Random.h"
25
27{
28 // Define faction for our main data class.
29 for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i)
31}
32
36
38{
39 LoadConfig();
40
41 bool activeHouse = false;
42 for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i)
43 {
44 if (_houseConfig[i].BuyerEnabled)
45 {
46 activeHouse = true;
47 break;
48 }
49 }
50
51 if (!activeHouse)
52 return false;
53
54 // load Check interval
56 TC_LOG_DEBUG("ahbot", "AHBot buyer interval is {} minutes", _checkInterval / MINUTE);
57 return true;
58}
59
61{
62 for (int i = 0; i < MAX_AUCTION_HOUSE_TYPE; ++i)
63 {
64 _houseConfig[i].BuyerEnabled = sAuctionBotConfig->GetConfigBuyerEnabled(AuctionHouseType(i));
65 if (_houseConfig[i].BuyerEnabled)
67 }
68}
69
74
75// Makes an AHbot buyer cycle for AH type if necessary
77{
78 if (!sAuctionBotConfig->GetConfigBuyerEnabled(houseType))
79 return false;
80
81 TC_LOG_DEBUG("ahbot", "AHBot: {} buying ...", AuctionBotConfig::GetHouseTypeName(houseType));
82
83 BuyerConfiguration& config = _houseConfig[houseType];
84 uint32 eligibleItems = GetItemInformation(config);
85 if (eligibleItems)
86 {
87 // Prepare list of items to bid or buy - remove old items
88 PrepareListOfEntry(config);
89 // Process buying and bidding items
90 BuyAndBidItems(config);
91 }
92
93 return true;
94}
95
96// Collects information about item counts and minimum prices to SameItemInfo and updates EligibleItems - a list with new items eligible for bot to buy and bid
97// Returns count of items in AH that were eligible for being bought or bidded on by ahbot buyer (EligibleItems size)
99{
100 config.SameItemInfo.clear();
101 time_t now = GameTime::GetGameTime();
102 uint32 count = 0;
103
104 AuctionHouseObject* house = sAuctionMgr->GetAuctionsMap(config.GetHouseType());
105 for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = house->GetAuctionsBegin(); itr != house->GetAuctionsEnd(); ++itr)
106 {
107 AuctionEntry* entry = itr->second;
108
109 if (!entry->owner || sAuctionBotConfig->IsBotChar(entry->owner))
110 continue; // Skip auctions owned by AHBot
111
112 Item* item = sAuctionMgr->GetAItem(entry->itemGUIDLow);
113 if (!item)
114 continue;
115
116 BuyerItemInfo& itemInfo = config.SameItemInfo[item->GetEntry()];
117
118 // Update item entry's count and total bid prices
119 // This can be used later to determine the prices and chances to bid
120 uint32 itemBidPrice = entry->startbid / item->GetCount();
121 itemInfo.TotalBidPrice = itemInfo.TotalBidPrice + itemBidPrice;
122 itemInfo.BidItemCount++;
123
124 // Set minimum bid price
125 if (!itemInfo.MinBidPrice)
126 itemInfo.MinBidPrice = itemBidPrice;
127 else
128 itemBidPrice = std::min(itemInfo.MinBidPrice, itemBidPrice);
129
130 // Set minimum buyout price if item has buyout
131 if (entry->buyout)
132 {
133 // Update item entry's count and total buyout prices
134 // This can be used later to determine the prices and chances to buyout
135 uint32 itemBuyPrice = entry->buyout / item->GetCount();
136 itemInfo.TotalBuyPrice = itemInfo.TotalBuyPrice + itemBuyPrice;
137 itemInfo.BuyItemCount++;
138
139 if (!itemInfo.MinBuyPrice)
140 itemInfo.MinBuyPrice = itemBuyPrice;
141 else
142 itemInfo.MinBuyPrice = std::min(itemInfo.MinBuyPrice, itemBuyPrice);
143 }
144
145 // Add/update EligibleItems if:
146 // * no bid
147 // * bid from player
148 if (!entry->bid || entry->bidder)
149 {
150 config.EligibleItems[entry->Id].LastExist = now;
151 config.EligibleItems[entry->Id].AuctionId = entry->Id;
152 ++count;
153 }
154 }
155
156 TC_LOG_DEBUG("ahbot", "AHBot: {} items added to buyable/biddable vector for ah type: {}", count, config.GetHouseType());
157 TC_LOG_DEBUG("ahbot", "AHBot: SameItemInfo size = {}", (uint32)config.SameItemInfo.size());
158 return count;
159}
160
161// ahInfo can be NULL
162bool AuctionBotBuyer::RollBuyChance(BuyerItemInfo const* ahInfo, Item const* item, AuctionEntry const* auction, uint32 /*bidPrice*/)
163{
164 if (!auction->buyout)
165 return false;
166
167 float itemBuyPrice = float(auction->buyout / item->GetCount());
168 float itemPrice = float(item->GetTemplate()->SellPrice ? item->GetTemplate()->SellPrice : GetVendorPrice(item->GetTemplate()->Quality));
169 // The AH cut needs to be added to the price, but we dont want a 100% chance to buy if the price is exactly AH default
170 itemPrice *= 1.4f;
171
172 // This value is between 0 and 100 and is used directly as the chance to buy or bid
173 // Value equal or above 100 means 100% chance and value below 0 means 0% chance
174 float chance = std::min(100.f, std::pow(100.f, 1.f + (1.f - itemBuyPrice / itemPrice) / sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_FACTOR)));
175
176 // If a player has bidded on item, have fifth of normal chance
177 if (auction->bidder)
178 chance = chance / 5.f;
179
180 if (ahInfo)
181 {
182 float avgBuyPrice = ahInfo->TotalBuyPrice / float(ahInfo->BuyItemCount);
183
184 TC_LOG_DEBUG("ahbot", "AHBot: buyout average: {:.1f} items with buyout: {}", avgBuyPrice, ahInfo->BuyItemCount);
185
186 // If there are more than 5 items on AH of this entry, try weigh in the average buyout price
187 if (ahInfo->BuyItemCount > 5)
188 chance *= 1.f / std::sqrt(itemBuyPrice / avgBuyPrice);
189 }
190
191 // Add config weigh in for quality
192 chance *= GetChanceMultiplier(item->GetTemplate()->Quality) / 100.0f;
193
194 float rand = frand(0.f, 100.f);
195 bool win = rand <= chance;
196 TC_LOG_DEBUG("ahbot", "AHBot: {} BUY! chance = {:.2f}, price = {}, buyprice = {}.", win ? "WIN" : "LOSE", chance, uint32(itemPrice), uint32(itemBuyPrice));
197 return win;
198}
199
200// ahInfo can be NULL
201bool AuctionBotBuyer::RollBidChance(BuyerItemInfo const* ahInfo, Item const* item, AuctionEntry const* auction, uint32 bidPrice)
202{
203 float itemBidPrice = float(bidPrice / item->GetCount());
204 float itemPrice = float(item->GetTemplate()->SellPrice ? item->GetTemplate()->SellPrice : GetVendorPrice(item->GetTemplate()->Quality));
205 // The AH cut needs to be added to the price, but we dont want a 100% chance to buy if the price is exactly AH default
206 itemPrice *= 1.4f;
207
208 // This value is between 0 and 100 and is used directly as the chance to buy or bid
209 // Value equal or above 100 means 100% chance and value below 0 means 0% chance
210 float chance = std::min(100.f, std::pow(100.f, 1.f + (1.f - itemBidPrice / itemPrice) / sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYER_CHANCE_FACTOR)));
211
212 if (ahInfo)
213 {
214 float avgBidPrice = ahInfo->TotalBidPrice / float(ahInfo->BidItemCount);
215
216 TC_LOG_DEBUG("ahbot", "AHBot: Bid average: {:.1f} biddable item count: {}", avgBidPrice, ahInfo->BidItemCount);
217
218 // If there are more than 5 items on AH of this entry, try weigh in the average bid price
219 if (ahInfo->BidItemCount >= 5)
220 chance *= 1.f / std::sqrt(itemBidPrice / avgBidPrice);
221 }
222
223 // If a player has bidded on item, have fifth of normal chance
224 if (auction->bidder && !sAuctionBotConfig->IsBotChar(auction->bidder))
225 chance = chance / 5.f;
226
227 // Add config weigh in for quality
228 chance *= GetChanceMultiplier(item->GetTemplate()->Quality) / 100.0f;
229
230 float rand = frand(0.f, 100.f);
231 bool win = rand <= chance;
232 TC_LOG_DEBUG("ahbot", "AHBot: {} BID! chance = {:.2f}, price = {}, bidprice = {}.", win ? "WIN" : "LOSE", chance, uint32(itemPrice), uint32(itemBidPrice));
233 return win;
234}
235
236// Removes items from EligibleItems that we shouldnt buy or bid on
237// The last existed time on them should be older than now
239{
240 // now - 5 seconds to leave out all old entries but keep the ones just updated a moment ago
241 time_t now = GameTime::GetGameTime() - 5;
242
243 for (CheckEntryMap::iterator itr = config.EligibleItems.begin(); itr != config.EligibleItems.end();)
244 {
245 if (itr->second.LastExist < now)
246 config.EligibleItems.erase(itr++);
247 else
248 ++itr;
249 }
250
251 TC_LOG_DEBUG("ahbot", "AHBot: EligibleItems size = {}", (uint32)config.EligibleItems.size());
252}
253
254// Tries to bid and buy items based on their prices and chances set in configs
256{
257 time_t now = GameTime::GetGameTime();
258 AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(config.GetHouseType());
259 CheckEntryMap& items = config.EligibleItems;
260
261 // Max amount of items to buy or bid
262 uint32 cycles = sAuctionBotConfig->GetItemPerCycleNormal();
263 if (items.size() > sAuctionBotConfig->GetItemPerCycleBoost())
264 {
265 // set more cycles if there is a huge influx of items
266 cycles = sAuctionBotConfig->GetItemPerCycleBoost();
267 TC_LOG_DEBUG("ahbot", "AHBot: Boost value used for Buyer! (if this happens often adjust both ItemsPerCycle in worldserver.conf)");
268 }
269
270 // Process items eligible to be bidded or bought
271 CheckEntryMap::iterator itr = items.begin();
272 while (cycles && itr != items.end())
273 {
274 AuctionEntry* auction = auctionHouse->GetAuction(itr->second.AuctionId);
275 if (!auction)
276 {
277 TC_LOG_DEBUG("ahbot", "AHBot: Entry {} doesn't exists, perhaps bought already?", itr->second.AuctionId);
278 items.erase(itr++);
279 continue;
280 }
281
282 // Check if the item has been checked once before
283 // If it has been checked and it was recently, skip it
284 if (itr->second.LastChecked && (now - itr->second.LastChecked) <= _checkInterval)
285 {
286 TC_LOG_DEBUG("ahbot", "AHBot: In time interval wait for entry {}!", auction->Id);
287 ++itr;
288 continue;
289 }
290
291 Item* item = sAuctionMgr->GetAItem(auction->itemGUIDLow);
292 if (!item)
293 {
294 // auction item not accessible, possible auction in payment pending mode
295 items.erase(itr++);
296 continue;
297 }
298
299 // price to bid if bidding
300 uint32 bidPrice;
301 if (auction->bid >= auction->startbid)
302 {
303 // get bid price to outbid previous bidder
304 bidPrice = auction->bid + auction->GetAuctionOutBid();
305 }
306 else
307 {
308 // no previous bidders - use starting bid
309 bidPrice = auction->startbid;
310 }
311
312 BuyerItemInfo const* ahInfo = nullptr;
313 BuyerItemInfoMap::const_iterator sameItemItr = config.SameItemInfo.find(item->GetEntry());
314 if (sameItemItr != config.SameItemInfo.end())
315 ahInfo = &sameItemItr->second;
316
317 TC_LOG_DEBUG("ahbot", "AHBot: Rolling for AHentry {}:", auction->Id);
318
319 // Roll buy and bid chances
320 bool successBuy = RollBuyChance(ahInfo, item, auction, bidPrice);
321 bool successBid = RollBidChance(ahInfo, item, auction, bidPrice);
322
323 // If roll bidding succesfully and bid price is above buyout -> buyout
324 // If roll for buying was successful but not for bid, buyout directly
325 // If roll bidding was also successful, buy the entry with 20% chance
326 // - Better bid than buy since the item is bought by bot if no player bids after
327 // Otherwise bid if roll for bid was successful
328 if ((auction->buyout && successBid && bidPrice >= auction->buyout) ||
329 (successBuy && (!successBid || urand(1, 5) == 1)))
330 BuyEntry(auction, auctionHouse); // buyout
331 else if (successBid)
332 PlaceBidToEntry(auction, bidPrice); // bid
333
334 itr->second.LastChecked = now;
335 --cycles;
336 ++itr;
337 }
338
339 // Clear not needed entries
340 config.SameItemInfo.clear();
341}
342
365
388
389// Buys the auction and does necessary actions to complete the buyout
391{
392 TC_LOG_DEBUG("ahbot", "AHBot: Entry {} bought at {:.2f}g", auction->Id, float(auction->buyout) / float(GOLD));
393
394 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
395
396 // Send mail to previous bidder if any
397 if (auction->bidder && !sAuctionBotConfig->IsBotChar(auction->bidder))
398 sAuctionMgr->SendAuctionOutbiddedMail(auction, auction->buyout, nullptr, trans);
399
400 // Set bot as bidder and set new bid amount
401 auction->bidder = sAuctionBotConfig->GetRandCharExclude(auction->owner);
402 auction->bid = auction->buyout;
403
404 // Mails must be under transaction control too to prevent data loss
405 sAuctionMgr->SendAuctionSalePendingMail(auction, trans);
406 sAuctionMgr->SendAuctionSuccessfulMail(auction, trans);
407 sAuctionMgr->SendAuctionWonMail(auction, trans);
408
409 // Delete auction from DB
410 auction->DeleteFromDB(trans);
411
412 // Remove auction item and auction from memory
413 sAuctionMgr->RemoveAItem(auction->itemGUIDLow);
414 auctionHouse->RemoveAuction(auction);
415
416 // Run SQLs
417 CharacterDatabase.CommitTransaction(trans);
418}
419
420// Bids on the auction and does the necessary actions for bidding
422{
423 TC_LOG_DEBUG("ahbot", "AHBot: Bid placed to entry {}, {:.2f}g", auction->Id, float(bidPrice) / float(GOLD));
424
425 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
426
427 // Send mail to previous bidder if any
428 if (auction->bidder && !sAuctionBotConfig->IsBotChar(auction->bidder))
429 sAuctionMgr->SendAuctionOutbiddedMail(auction, bidPrice, nullptr, trans);
430
431 // Set bot as bidder and set new bid amount
432 auction->bidder = sAuctionBotConfig->GetRandCharExclude(auction->owner);
433 auction->bid = bidPrice;
435
436 // Update auction to DB
438 stmt->setUInt32(0, auction->bidder);
439 stmt->setUInt32(1, auction->bid);
440 stmt->setUInt8(2, auction->Flags);
441 stmt->setUInt32(3, auction->Id);
442 trans->Append(stmt);
443
444 // Run SQLs
445 CharacterDatabase.CommitTransaction(trans);
446}
std::map< uint32, BuyerAuctionEval > CheckEntryMap
@ CONFIG_AHBOT_BUYER_BASEPRICE_GRAY
@ CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GRAY
@ CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_ORANGE
@ CONFIG_AHBOT_BUYER_RECHECK_INTERVAL
@ CONFIG_AHBOT_BUYER_BASEPRICE_BLUE
@ CONFIG_AHBOT_BUYER_BASEPRICE_PURPLE
@ CONFIG_AHBOT_BUYER_BASEPRICE_YELLOW
@ CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_YELLOW
@ CONFIG_AHBOT_BUYER_BASEPRICE_ORANGE
@ CONFIG_AHBOT_BUYER_BASEPRICE_GREEN
@ CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_BLUE
@ CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_PURPLE
@ CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_WHITE
@ CONFIG_AHBOT_BUYER_CHANCEMULTIPLIER_GREEN
@ CONFIG_AHBOT_BUYER_BASEPRICE_WHITE
@ CONFIG_AHBOT_BUYER_CHANCE_FACTOR
#define MAX_AUCTION_HOUSE_TYPE
AuctionHouseType
#define sAuctionBotConfig
AuctionEntryFlag
@ AUCTION_ENTRY_FLAG_GM_LOG_BUYER
#define sAuctionMgr
@ CHAR_UPD_AUCTION_BID
@ MINUTE
Definition Common.h:29
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint32_t uint32
Definition Define.h:133
#define TC_LOG_DEBUG(filterType__,...)
Definition Log.h:156
float frand(float min, float max)
Definition Random.cpp:55
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:42
@ ITEM_QUALITY_UNCOMMON
@ ITEM_QUALITY_RARE
@ ITEM_QUALITY_NORMAL
@ ITEM_QUALITY_LEGENDARY
@ ITEM_QUALITY_POOR
@ ITEM_QUALITY_ARTIFACT
@ ITEM_QUALITY_EPIC
@ SILVER
@ GOLD
uint32 GetChanceMultiplier(uint32 quality)
void BuyAndBidItems(BuyerConfiguration &config)
void PrepareListOfEntry(BuyerConfiguration &config)
BuyerConfiguration _houseConfig[MAX_AUCTION_HOUSE_TYPE]
uint32 GetItemInformation(BuyerConfiguration &config)
bool Initialize() override
bool Update(AuctionHouseType houseType) override
bool RollBuyChance(BuyerItemInfo const *ahInfo, Item const *item, AuctionEntry const *auction, uint32 bidPrice)
bool RollBidChance(BuyerItemInfo const *ahInfo, Item const *item, AuctionEntry const *auction, uint32 bidPrice)
void PlaceBidToEntry(AuctionEntry *auction, uint32 bidPrice)
uint32 GetVendorPrice(uint32 quality)
void LoadBuyerValues(BuyerConfiguration &config)
void BuyEntry(AuctionEntry *auction, AuctionHouseObject *auctionHouse)
static char const * GetHouseTypeName(AuctionHouseType houseType)
AuctionEntry * GetAuction(uint32 id) const
AuctionEntryMap::iterator GetAuctionsEnd()
AuctionEntryMap::iterator GetAuctionsBegin()
bool RemoveAuction(AuctionEntry *auction)
Definition Item.h:62
ItemTemplate const * GetTemplate() const
Definition Item.cpp:535
uint32 GetCount() const
Definition Item.h:119
uint32 GetEntry() const
Definition Object.h:81
void setUInt32(uint8 index, uint32 value)
void setUInt8(uint8 index, uint8 value)
time_t GetGameTime()
Definition GameTime.cpp:42
ObjectGuid::LowType bidder
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
ObjectGuid::LowType owner
ObjectGuid::LowType itemGUIDLow
AuctionEntryFlag Flags
BuyerItemInfoMap SameItemInfo
AuctionHouseType GetHouseType() const