From 1994fdf661b6447bc8a7003dc3cac5ce8bb4356e Mon Sep 17 00:00:00 2001 From: Tecc Date: Tue, 25 Aug 2026 10:25:11 +0000 Subject: [PATCH] fix(seller): reserve a share of each cycle for items without a price override GetItemsToSell() returned its four blocks concatenated, and Sell() only ever walks the first ItemsPerCycle entries of that list. Blocks 1 and 2 hold every sellable price override item - thousands of them - so index 200 never reached block 3. An item without a row in mod_auctionhousebot_priceOverride was therefore never listed at all, however well it passed the filters. The price override blocks and the bin blocks are now collected separately and woven together, so AuctionHouseBot.BinItemShare percent of every stretch of the list belongs to items with no override. Default 25. Setting it to 0 restores the old concatenation order. --- conf/mod_ahbot.conf.dist | 11 +++++++++ src/AuctionHouseBot.cpp | 44 ++++++++++++++++++++++++++++++++--- src/AuctionHouseBotConfig.cpp | 3 +++ src/AuctionHouseBotConfig.h | 1 + 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/conf/mod_ahbot.conf.dist b/conf/mod_ahbot.conf.dist index 4722973..21b7c5c 100644 --- a/conf/mod_ahbot.conf.dist +++ b/conf/mod_ahbot.conf.dist @@ -94,6 +94,16 @@ # Set to 1 to restore that behaviour. # Default 5 # +# AuctionHouseBot.BinItemShare +# Percentage of each cycle's candidate list reserved for items that have no +# row in "mod_auctionhousebot_priceOverride". The seller only ever works +# through the first AuctionHouseBot.ItemsPerCycle entries of that list, and +# the price override items alone number in the thousands, so without a +# reserved share those items are never reached and never listed. +# Set to 0 to append them after the price override items instead (they will +# then effectively never be listed). +# Default 25 +# # AuctionHouseBot.DivisibleStacks # Sell items in stack sizes which depends on the maximum amount per stack. # For example, an item with max stack size 20 will be sold in 5, 10 15 and 20 stacks, and not at random. @@ -131,6 +141,7 @@ AuctionHouseBot.ItemsPerCycle = 200 AuctionHouseBot.ConsiderOnlyBotAuctions = 0 AuctionHouseBot.DuplicatesCount = 0 AuctionHouseBot.RestockBatchSize = 5 +AuctionHouseBot.BinItemShare = 25 AuctionHouseBot.DivisibleStacks = 0 AuctionHouseBot.ElapsingTimeClass = 1 AuctionHouseBot.MaxItems = 15000 diff --git a/src/AuctionHouseBot.cpp b/src/AuctionHouseBot.cpp index 22a6ef8..7df70d1 100644 --- a/src/AuctionHouseBot.cpp +++ b/src/AuctionHouseBot.cpp @@ -1319,11 +1319,18 @@ std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGui // Queue every stack an item is short of, keeping the copies adjacent so a // sold out item is restocked as a batch instead of one stack per cycle. + // Blocks 1 and 2 (price override items) and blocks 3 and 4 (bin items) are + // collected separately so they can be woven together at the end. Concatenating + // them starved the bin items: a cycle only ever consumes the first + // ItemsPerCycle entries, and the override blocks alone are thousands long. + std::vector priorityItems; + std::vector binOnlyItems; + auto appendMissingStacks = [&](const std::vector& items) { for (auto const& itemID : items) { - allItemIDs.insert(allItemIDs.end(), missingStacks(itemID), itemID); + priorityItems.insert(priorityItems.end(), missingStacks(itemID), itemID); } }; @@ -1362,7 +1369,7 @@ std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGui itemsWithOverrides.push_back(itemID); } std::shuffle(itemsWithOverrides.begin(), itemsWithOverrides.end(), std::mt19937(std::random_device()())); - allItemIDs.insert(allItemIDs.end(), itemsWithOverrides.begin(), itemsWithOverrides.end()); + priorityItems.insert(priorityItems.end(), itemsWithOverrides.begin(), itemsWithOverrides.end()); // 3. Items without overrides that are not in the auction house (randomized order) addItems(greyItemsBin, true); @@ -1375,7 +1382,7 @@ std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGui // Randomize the collected items std::shuffle(tempItemIDs.begin(), tempItemIDs.end(), std::mt19937(std::random_device()())); - allItemIDs.insert(allItemIDs.end(), tempItemIDs.begin(), tempItemIDs.end()); + binOnlyItems.insert(binOnlyItems.end(), tempItemIDs.begin(), tempItemIDs.end()); // 4. Random items without price overrides (randomized order) tempItemIDs.clear(); @@ -1393,9 +1400,40 @@ std::vector AuctionHouseBot::GetItemsToSell(AHBConfig* config, ObjectGui allItemIDs.insert(allItemIDs.end(), tempItemIDs.begin(), tempItemIDs.end()); */ + // Weave the two sources together so that, over any stretch of the list, + // BinItemShare percent of the slots belong to items that have no price + // override. At 0 the bin items simply follow the priority ones, which is the + // old concatenation order. + uint32 share = std::min(config->BinItemShare, 100); + size_t nextPriority = 0; + size_t nextBin = 0; + uint32 credit = 0; + + allItemIDs.reserve(priorityItems.size() + binOnlyItems.size()); + + while (nextPriority < priorityItems.size() || nextBin < binOnlyItems.size()) + { + credit += share; + + if (credit >= 100 && nextBin < binOnlyItems.size()) + { + credit -= 100; + allItemIDs.push_back(binOnlyItems[nextBin++]); + } + else if (nextPriority < priorityItems.size()) + { + allItemIDs.push_back(priorityItems[nextPriority++]); + } + else if (nextBin < binOnlyItems.size()) + { + allItemIDs.push_back(binOnlyItems[nextBin++]); + } + } + // Log the number of items to sell if(config->TraceSeller) { + LOG_INFO("module", "AHBot [{}]: {} price override and {} bin only candidates woven at {}%", _id, priorityItems.size(), binOnlyItems.size(), share); LOG_INFO("module", "AHBot [{}]: restocking {} sold out and {} understocked price override items", _id, itemsWithOverridesNotListed.size(), itemsWithOverridesBelowTarget.size()); LOG_INFO("module", "AHBot [{}]: GetItemsToSell returning {} items", _id, allItemIDs.size()); } diff --git a/src/AuctionHouseBotConfig.cpp b/src/AuctionHouseBotConfig.cpp index 954998c..4eca72f 100644 --- a/src/AuctionHouseBotConfig.cpp +++ b/src/AuctionHouseBotConfig.cpp @@ -222,6 +222,7 @@ AHBConfig::AHBConfig(uint32 ahid, AHBConfig* conf) Bind_Quest_Item = conf->Bind_Quest_Item; DuplicatesCount = conf->DuplicatesCount; RestockBatchSize = conf->RestockBatchSize; + BinItemShare = conf->BinItemShare; ElapsingTimeClass = conf->ElapsingTimeClass; DivisibleStacks = conf->DivisibleStacks; DisablePermEnchant = conf->DisablePermEnchant; @@ -532,6 +533,7 @@ void AHBConfig::Reset() Bind_Quest_Item = false; DuplicatesCount = 0; RestockBatchSize = 5; + BinItemShare = 25; ElapsingTimeClass = 1; DivisibleStacks = false; @@ -2049,6 +2051,7 @@ void AHBConfig::InitializeFromFile() MarketResetThreshold = sConfigMgr->GetOption("AuctionHouseBot.MarketResetThreshold" , 25); DuplicatesCount = sConfigMgr->GetOption("AuctionHouseBot.DuplicatesCount" , 0); RestockBatchSize = sConfigMgr->GetOption("AuctionHouseBot.RestockBatchSize" , 5); + BinItemShare = sConfigMgr->GetOption("AuctionHouseBot.BinItemShare" , 25); DivisibleStacks = sConfigMgr->GetOption ("AuctionHouseBot.DivisibleStacks" , false); ElapsingTimeClass = sConfigMgr->GetOption("AuctionHouseBot.ElapsingTimeClass" , 1); ConsiderOnlyBotAuctions = sConfigMgr->GetOption ("AuctionHouseBot.ConsiderOnlyBotAuctions", false); diff --git a/src/AuctionHouseBotConfig.h b/src/AuctionHouseBotConfig.h index 0222d1a..cb928ff 100644 --- a/src/AuctionHouseBotConfig.h +++ b/src/AuctionHouseBotConfig.h @@ -203,6 +203,7 @@ class AHBConfig uint32 DuplicatesCount; uint32 RestockBatchSize; + uint32 BinItemShare; uint32 ElapsingTimeClass; bool DivisibleStacks;