Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 39 additions & 40 deletions src/AuctionHouseBot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,11 @@ void AuctionHouseBot::Buy(Player* AHBplayer, AHBConfig* config, WorldSession* se
// Get price overrides
auto [avgPrice, minPrice] = config->GetPriceOverrideForItem(prototype->ItemId);

if (minPrice > avgPrice)
{
minPrice = avgPrice;
}

uint64 maxPrice = (avgPrice + ( avgPrice - minPrice ));
uint64 SellPriceValue = maxPrice > 0 ? maxPrice : prototype->SellPrice;
uint64 BuyPriceValue = avgPrice > 0 ? avgPrice : prototype->BuyPrice;
Expand Down Expand Up @@ -791,6 +796,20 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config)

uint32 itemID = itemsToSell[cnt];

// Priority-list picks (price-override items) must respect their per-item
// count override too, mirroring getElement()'s cap for random picks.
// A capped pick falls through to the rarity-tier selection below.
if (itemID != 0)
{
uint32 effectiveMax = config->DuplicatesCount;
uint32 countOverride = config->GetCountOverrideForItem(itemID);
if (countOverride > 0)
effectiveMax = countOverride;

if (effectiveMax > 0 && botItemCounts[itemID] >= effectiveMax)
itemID = 0;
}

// Update Auctions count for current Bot
uint32 botAuctionsCount = nbOfAuctions + cnt;

Expand Down Expand Up @@ -821,91 +840,91 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config)
itemTypeSelectedToSell = AHB_GREY_I;
itemID = getElement(config->GreyItemsVec, urand(0, config->GreyItemsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}
else if (itemID == 0 && !config->GreyTradeGoodsVec.empty() && (currentGreyTG < maxGreyTG))
if (itemID == 0 && !config->GreyTradeGoodsVec.empty() && (currentGreyTG < maxGreyTG))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this change cause problems since the item is handled by all buckets?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No — at most one bucket can produce the item per iteration. Every bucket after the first is gated on itemID == 0, so as soon as any getElement() returns a real item, all later buckets short-circuit. Fall-through only continues while nothing has been picked yet (bucket empty, percentage cap reached, or the random pick sat at its count-override cap). An item also can't appear in two buckets: the bins are partitioned by quality and class (items vs trade goods), so each item id lives in exactly one vector.

itemTypeSelectedToSell does get set optimistically before getElement() resolves, but it's overwritten by whichever bucket actually delivers, and if the loop exits empty the iteration is discarded before the value is read. This matches upstream azerothcore/master exactly — the else if chain was the deviation this PR reverts.

{
itemTypeSelectedToSell = AHB_GREY_TG;
itemID = getElement(config->GreyTradeGoodsVec, urand(0, config->GreyTradeGoodsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

// Normal

else if (itemID == 0 && !config->WhiteItemsVec.empty() && (currentWhiteItems < maxWhiteI))
if (itemID == 0 && !config->WhiteItemsVec.empty() && (currentWhiteItems < maxWhiteI))
{
itemTypeSelectedToSell = AHB_WHITE_I;
itemID = getElement(config->WhiteItemsVec, urand(0, config->WhiteItemsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

else if (itemID == 0 && !config->WhiteTradeGoodsVec.empty() && (currentWhiteTG < maxWhiteTG))
if (itemID == 0 && !config->WhiteTradeGoodsVec.empty() && (currentWhiteTG < maxWhiteTG))
{
itemTypeSelectedToSell = AHB_WHITE_TG;
itemID = getElement(config->WhiteTradeGoodsVec, urand(0, config->WhiteTradeGoodsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

// Uncommon

else if (itemID == 0 && !config->GreenItemsVec.empty() && (currentGreenItems < maxGreenI))
if (itemID == 0 && !config->GreenItemsVec.empty() && (currentGreenItems < maxGreenI))
{
itemTypeSelectedToSell = AHB_GREEN_I;
itemID = getElement(config->GreenItemsVec, urand(0, config->GreenItemsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

else if (itemID == 0 && !config->GreenTradeGoodsVec.empty() && (currentGreenTG < maxGreenTG))
if (itemID == 0 && !config->GreenTradeGoodsVec.empty() && (currentGreenTG < maxGreenTG))
{
itemTypeSelectedToSell = AHB_GREEN_TG;
itemID = getElement(config->GreenTradeGoodsVec, urand(0, config->GreenTradeGoodsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

// Rare

else if (itemID == 0 && !config->BlueItemsVec.empty() && (currentBlueItems < maxBlueI))
if (itemID == 0 && !config->BlueItemsVec.empty() && (currentBlueItems < maxBlueI))
{
itemTypeSelectedToSell = AHB_BLUE_I;
itemID = getElement(config->BlueItemsVec, urand(0, config->BlueItemsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

else if (itemID == 0 && !config->BlueTradeGoodsVec.empty() && (currentBlueTG < maxBlueTG))
if (itemID == 0 && !config->BlueTradeGoodsVec.empty() && (currentBlueTG < maxBlueTG))
{
itemTypeSelectedToSell = AHB_BLUE_TG;
itemID = getElement(config->BlueTradeGoodsVec, urand(0, config->BlueTradeGoodsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

// Epic

else if (itemID == 0 && !config->PurpleItemsVec.empty() && (currentPurpleItems < maxPurpleI))
if (itemID == 0 && !config->PurpleItemsVec.empty() && (currentPurpleItems < maxPurpleI))
{
itemTypeSelectedToSell = AHB_PURPLE_I;
itemID = getElement(config->PurpleItemsVec, urand(0, config->PurpleItemsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

else if (itemID == 0 && !config->PurpleTradeGoodsVec.empty() && (currentPurpleTG < maxPurpleTG))
if (itemID == 0 && !config->PurpleTradeGoodsVec.empty() && (currentPurpleTG < maxPurpleTG))
{
itemTypeSelectedToSell = AHB_PURPLE_TG;
itemID = getElement(config->PurpleTradeGoodsVec, urand(0, config->PurpleTradeGoodsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

// Legendary

else if (itemID == 0 && !config->OrangeItemsVec.empty() && (currentOrangeItems < maxOrangeI))
if (itemID == 0 && !config->OrangeItemsVec.empty() && (currentOrangeItems < maxOrangeI))
{
itemTypeSelectedToSell = AHB_ORANGE_I;
itemID = getElement(config->OrangeItemsVec, urand(0, config->OrangeItemsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

else if (itemID == 0 && !config->OrangeTradeGoodsVec.empty() && (currentOrangeTG < maxOrangeTG))
if (itemID == 0 && !config->OrangeTradeGoodsVec.empty() && (currentOrangeTG < maxOrangeTG))
{
itemTypeSelectedToSell = AHB_ORANGE_TG;
itemID = getElement(config->OrangeTradeGoodsVec, urand(0, config->OrangeTradeGoodsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

// Artifact

else if (itemID == 0 && !config->YellowItemsVec.empty() && (currentYellowItems < maxYellowI))
if (itemID == 0 && !config->YellowItemsVec.empty() && (currentYellowItems < maxYellowI))
{
itemTypeSelectedToSell = AHB_YELLOW_I;
itemID = getElement(config->YellowItemsVec, urand(0, config->YellowItemsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
}

else if (itemID == 0 && !config->YellowTradeGoodsVec.empty() && (currentYellowTG < maxYellowTG))
if (itemID == 0 && !config->YellowTradeGoodsVec.empty() && (currentYellowTG < maxYellowTG))
{
itemTypeSelectedToSell = AHB_YELLOW_TG;
itemID = getElement(config->YellowTradeGoodsVec, urand(0, config->YellowTradeGoodsVec.size() - 1), _id, config->DuplicatesCount, botItemCounts, config);
Expand All @@ -916,7 +935,7 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config)
if (itemID == 0)
{
loopBrk++;
return;
continue;
}

// Retrieve information about the selected item
Expand All @@ -931,7 +950,7 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config)
LOG_ERROR("module", "AHBot [{}]: could not get prototype of item {}", _id, itemID);
}

return;
continue;
}

Item* item = Item::CreateItem(itemID, 1, AHBplayer);
Expand All @@ -945,7 +964,7 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config)
LOG_ERROR("module", "AHBot [{}]: could not create item from prototype {}", _id, itemID);
}

return;
continue;
}

// Start interacting with the item by adding a random property
Expand All @@ -968,7 +987,7 @@ void AuctionHouseBot::Sell(Player* AHBplayer, AHBConfig* config)
}

item->RemoveFromUpdateQueueOf(AHBplayer);
return;
continue;
}

// Determine the price
Expand Down Expand Up @@ -1789,29 +1808,9 @@ void AuctionHouseBot::Initialize(AHBConfig* allianceConfig, AHBConfig* hordeConf
_hordeConfig = hordeConfig;
_neutralConfig = neutralConfig;

// Load price overrides once and make them globally available
static bool priceOverridesLoaded = false;
if (!priceOverridesLoaded)
{
_neutralConfig->LoadPriceOverrides(); // Load once using the neutral config
priceOverridesLoaded = true;
}

// Share the loaded price overrides across all configurations
_allianceConfig->itemPriceOverrides = _neutralConfig->itemPriceOverrides;
_hordeConfig->itemPriceOverrides = _neutralConfig->itemPriceOverrides;

// Load per-item count overrides once and make them globally available
static bool countOverridesLoaded = false;
if (!countOverridesLoaded)
{
_neutralConfig->LoadCountOverrides(); // Load once using the neutral config
countOverridesLoaded = true;
}

// Share the loaded count overrides across all configurations
_allianceConfig->itemCountOverrides = _neutralConfig->itemCountOverrides;
_hordeConfig->itemCountOverrides = _neutralConfig->itemCountOverrides;
// Price and count overrides are loaded and shared across configurations by
// AHBot_WorldScript::LoadSharedOverrides(), once per startup/reload cycle,
// before any bot is constructed.
}

// Helper function to join GUIDs into a comma-separated string
Expand Down
14 changes: 14 additions & 0 deletions src/AuctionHouseBotConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3494,6 +3494,10 @@ std::set<uint32> AHBConfig::getCommaSeparatedIntegers(std::string text)

void AHBConfig::LoadPriceOverrides()
{
// Full reload semantics: rows deleted from the table must disappear from
// the in-memory map too, not linger until restart.
itemPriceOverrides.clear();

QueryResult result = WorldDatabase.Query("SELECT item, avgPrice, minPrice FROM mod_auctionhousebot_priceOverride");

if (!result)
Expand All @@ -3509,6 +3513,12 @@ void AHBConfig::LoadPriceOverrides()
uint64 avgPrice = fields[1].Get<uint64>();
uint64 minPrice = fields[2].Get<uint64>();

if (minPrice > avgPrice)
{
LOG_WARN("module", "AHBConfig: price override for item {} has minPrice {} > avgPrice {}, clamping minPrice to avgPrice", itemId, minPrice, avgPrice);
minPrice = avgPrice;
}

itemPriceOverrides[itemId] = std::make_tuple(avgPrice, minPrice);
} while (result->NextRow());

Expand All @@ -3528,6 +3538,10 @@ std::tuple<uint64, uint64> AHBConfig::GetPriceOverrideForItem(uint32 itemId) con

void AHBConfig::LoadCountOverrides()
{
// Full reload semantics: rows deleted from the table must disappear from
// the in-memory map too, not linger until restart.
itemCountOverrides.clear();

QueryResult result = WorldDatabase.Query("SELECT item, targetCount FROM mod_auctionhousebot_countOverride");

if (!result)
Expand Down
19 changes: 19 additions & 0 deletions src/AuctionHouseBotWorldScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ void AHBot_WorldScript::OnBeforeConfigLoad(bool reload)
gHordeConfig->Initialize(gBotsId);
gNeutralConfig->Initialize(gBotsId);

LoadSharedOverrides();

// Start again the bots
PopulateBots();
}
Expand All @@ -145,6 +147,8 @@ void AHBot_WorldScript::OnStartup()
gHordeConfig->Initialize (gBotsId);
gNeutralConfig->Initialize (gBotsId);

LoadSharedOverrides();

//
// Starts the bots
//
Expand Down Expand Up @@ -182,6 +186,21 @@ void AHBot_WorldScript::DeleteBots()
}


void AHBot_WorldScript::LoadSharedOverrides()
{
// Load once using the neutral config, then share across all configurations.
// Called on startup and on every config reload, so .reload config keeps
// mod_auctionhousebot_priceOverride and mod_auctionhousebot_countOverride current.
gNeutralConfig->LoadPriceOverrides();
gNeutralConfig->LoadCountOverrides();

gAllianceConfig->itemPriceOverrides = gNeutralConfig->itemPriceOverrides;
gHordeConfig->itemPriceOverrides = gNeutralConfig->itemPriceOverrides;

gAllianceConfig->itemCountOverrides = gNeutralConfig->itemCountOverrides;
gHordeConfig->itemCountOverrides = gNeutralConfig->itemCountOverrides;
}

void AHBot_WorldScript::PopulateBots()
{
uint32 account = sConfigMgr->GetOption<uint32>("AuctionHouseBot.Account", 0);
Expand Down
1 change: 1 addition & 0 deletions src/AuctionHouseBotWorldScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class AHBot_WorldScript : public WorldScript
private:
void DeleteBots();
void PopulateBots();
void LoadSharedOverrides();

public:
AHBot_WorldScript();
Expand Down
Loading