Skip to content
Merged
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
98 changes: 80 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,28 +66,47 @@ The sum of the percentage for these categories must always be 100, or otherwise

## Merkerhood customizations

This fork adds four behaviours on top of the stock buyer/seller bot, so the auction
house reads like a real ChromieCraft economy instead of quality-multiplier defaults.
Design detail lives in `docs/superpowers/specs/`; the Python tooling that generates the
data tables is in `tools/price-backfill/` (stdlib only, no dependencies).
This branch is a heavily modified fork. On top of the stock buyer/seller bot it adds a
dynamic market-price engine, real ChromieCraft base prices, lifelike listing quantities and
stack sizes, multi-character sellers, per-faction bidding, item filtering, and a set of
performance rewrites for large realms. The goal is an auction house that reads like a real
economy rather than quality-multiplier defaults.

Design detail for the recent pricing/stacks work is in `docs/superpowers/specs/`; the Python
tooling that generates the price and count tables is in `tools/price-backfill/` (stdlib only,
no dependencies).

### Dynamic market pricing

When `AuctionHouseBot.UseMarketPriceForSeller` is on, the seller prices items from what
players are actually paying instead of a static value. Sales are recorded in
`mod_auctionhousebot_auction_history`, and the bot computes a moving average per item:

- `UseAuctionCount` / `AuctionCount` averages over the last N auctions; otherwise `Days`
averages over a time window.
- `FilterOutliers` drops price outliers from the sample; `WeightRecent` weights newer
auctions more.
- `MarketResetThreshold` is how many auctions of an item must accumulate before the plain
computed price is adopted; below it the price is nudged by a heuristic, which smooths
oscillations. Old history is pruned automatically.

### ChromieCraft base prices

`mod_auctionhousebot_priceOverride` (`item`, `avgPrice`, `minPrice`) is the base-price
source. The bot prioritises listing items that have an override and falls back to the
fixed defaults otherwise. The table is generated by `tools/price-backfill/backfill.py`
from ChromieCraft's live auction data, scraped from wowauctions.net's JSON endpoint keyed
by item id. Price changes that swing far from the previous overrides land in a
`reports/deviations-*.csv` worklist for manual review. See the tool README to regenerate.
`mod_auctionhousebot_priceOverride` (`item`, `avgPrice`, `minPrice`) is the base "plain"
price: it is used directly when market pricing is off, and is the baseline the market engine
converges toward once enough auctions accumulate (see `MarketResetThreshold`). The bot
prioritises listing items that have an override and falls back to fixed defaults otherwise. The table is generated by
`tools/price-backfill/backfill.py` from ChromieCraft's live auction data, scraped from
wowauctions.net's JSON endpoint keyed by item id. Price changes that swing far from the
previous overrides land in a `reports/deviations-*.csv` worklist for manual review.

### Reagent-cost derivation

Craftable items that never traded on ChromieCraft (so have no market price) get a price
derived from their reagents. `tools/price-backfill/derive.py` parses the recipe graph from
`Spell.dbc` (committed as `recipes.json`), prices each item recursively from its reagents'
ChromieCraft prices, falls back to vendor sell price where a reagent is unpriced, and
applies a crafting margin. Derived rows are merged (add-only) into the same priceOverride
table.
Craftable items that never traded on ChromieCraft get a price derived from their reagents.
`tools/price-backfill/derive.py` parses the recipe graph from `Spell.dbc` (committed as
`recipes.json`), prices each item recursively from its reagents' ChromieCraft prices, falls
back to vendor sell price where a reagent is unpriced, and applies a crafting margin. Derived
rows are merged (add-only) into the same priceOverride table.

### Per-item listing count override

Expand All @@ -105,8 +124,51 @@ Glyphs, recipes, quest items, containers, keys, and cut gems (socketable,
`GemProperties != 0`) always list as quantity 1, regardless of the item's stackable value.
Other stackables use weighted human breakpoints `{1, 5, 10, 20, full}` clamped to the
per-quality maximum, never a random 7 or 13. This is the default when
`AuctionHouseBot.DivisibleStacks = 0`; setting it to `1` keeps the older divisible-multiples
mode.
`AuctionHouseBot.DivisibleStacks = 0`; `1` keeps the older divisible-multiples mode.
`AuctionHouseBot.MaxStackSize` caps the largest stack.

### Multiple seller characters

`AuctionHouseBot.GUIDs` is a comma-separated list of character GUIDs (all under
`AuctionHouseBot.Account`) that the bot lists as, instead of a single character, so listings
are spread across several names.

### Per-faction bidding

`AllianceBiddingInterval` / `HordeBiddingInterval` / `NeutralBiddingInterval` and the
matching `BidsPerInterval` values set how often, and how many, bids the buyer places per
faction auction house.

### Market population control

- `AuctionHouseBot.ConsiderOnlyBotAuctions` counts only the bot's own auctions when deciding
how full the market is, so it keeps a background of listings even when many players are
online (without it, player auctions count toward the total and the SQL max values must be
raised a lot).
- `MinItems` / `MaxItems` bound the total number of auctions; `DuplicatesCount` caps
duplicate stacks of a single item (per-item overrides can raise or lower this).

### Item filtering

`z_filter_disabled_and_trash.sql` builds an exclusion set (disabled, trash, and non-market
items) so the bot never lists junk. `AuctionHouseBot.DEBUG_FILTERS` traces what gets filtered.

### Performance

For large or busy realms the seller path was rewritten to avoid repeatedly scanning the
whole auction house: `GetItemsToSell` builds a set of item ids already listed, `getElement`
uses an in-memory item-count map instead of iterating the auction table, and item ordering is
deterministic rather than randomised. These are the `fix_slow_updates` / `fix_low_pop`
changes.

### Data tables and tooling

- World DB tables: `mod_auctionhousebot` (per-AH item counts, per-quality price/stack limits),
`mod_auctionhousebot_priceOverride`, `mod_auctionhousebot_countOverride`,
`mod_auctionhousebot_auction_history`. Cleanup runs from `mod_autionhousebot_cleanup.sql`.
- Module SQL auto-applies via the AzerothCore updater on server start.
- `tools/price-backfill/` regenerates the price and count tables from ChromieCraft data; see
its README for the full workflow.

## Credits

Expand Down
Loading