7.4 KiB
Legacy Auctions and Betting
This document covers the auction and betting systems: how auctions open/close,
what resources they trade, and how betting payouts are calculated. References
include legacy/hwe/sammo/Auction*.php, legacy/hwe/func_auction.php, and
legacy/hwe/sammo/Betting.php.
Auction Types and Entry Points
Auction(base class): shared bid logic and close-date extensions.AuctionBuyRice: sell rice for gold.AuctionSellRice: sell gold for rice.AuctionUniqueItem: bids use inheritance points for unique items.processAuction()inlegacy/hwe/func_auction.phpcloses finished auctions.registerAuction()creates neutral (NPC-hosted) buy/sell auctions.
Auction Data Model
Primary tables:
ng_auction: auction metadata (type, target, host, close date, detail).ng_auction_bid: per-bid rows for each auction.
The AuctionInfo and AuctionInfoDetail DTOs are serialized into
ng_auction columns for structured access.
Core Auction Flow
- Open
AuctionBasicResource::openResourceAuction()validates amounts and host resources, then insertsng_auction.AuctionUniqueItem::openItemAuction()checks availability, reserves unique items, and posts a global history notice.
- Bid
- Bids are inserted into
ng_auction_bidand can extend the close date. - Reverse auctions (when
detail->isReverse) sort by lowest bid.
- Bids are inserted into
- Close (
tryFinish()from the concrete auction)- If no bids:
rollbackAuction()returns resources to host and logs. - With bids:
finishAuction()transfers resources, logs, and sends messages.
- If no bids:
- Processing
processAuction()scans auctions pastclose_dateand finishes them.
Unique Item Auctions
- Currency: inheritance points (
ResourceType::inheritancePoint). - Host identity is obfuscated using
genObfuscatedName()and a deterministic shuffled name pool stored ingame_env.obfuscatedNamePool. - Limits and availability are enforced against
GameConst::$allItemsand existing ownership ingeneralrows.
Betting System
Betting is an independent KV-backed system:
Betting::openBetting()storesBettingInfoin KV storage (betting).Betting::bet()inserts or updatesng_bettingrows and charges either gold or inheritance points.Betting::giveReward()distributes payouts to winners (exclusive or shared).
Event actions controlling lifecycle:
OpenNationBetting: opens a nation-strength bet and registers aDESTROY_NATIONevent to close it.FinishNationBetting: evaluates winners when only N nations remain.
RNG Notes
Auction::genObfuscatedName()useshiddenSeed + 'obfuscatedNamePool'to shuffle the name pool once and reuse it fromgame_env.registerAuction()relies on an injected RNG to randomize neutral auctions.
Open Questions / Follow-ups
registerAuction()is called from legacyfunc_gamerule.phpafter the logical month changes. Its RNG is seeded before that change with the previous year/month, then consumes one power roll per nation and the conditional tournament roll before auction generation.- Scenario-specific overrides of unique auction timing have not been found.
Core2026 currently applies the legacy constants against
tickSeconds.
core2026 compatibility implementation (2026-07-25)
| Contract | core2026 path | Status |
|---|---|---|
| Resource auction list/open/bid | game-frontend/AuctionView.vue → game-api/router/auction → auctionOpen/auctionBid engine commands |
Confirmed by integration test |
| Resource reservation/refund/settlement | opener reserves host gold/rice; bidder reserves and refunds the previous top bid; finalizer transfers or returns resources | Confirmed by integration test |
| Unique list/detail privacy | API replaces host and bidder identity with deterministic aliases and never returns unique-auction general IDs | Confirmed by API implementation and typecheck |
| Unique open | engine validates configured quantity, slot ownership, one active host/item auction, and atomically inserts the host's first bid while deducting inheritance_point.previous |
Confirmed by integration test |
| Unique bid | row-locked auction, minimum max(1%, 10), conditional point deduction, previous-top-bid refund, slot/top-bid conflict checks |
Confirmed by integration test |
| Unique finalization | requested extension, per-slot and total ownership-limit extension, host neutralization, item inventory update | Confirmed by integration test |
| Timer/worker | API updates the Redis timer after open/bid; existing scheduler claims due rows and sends durable auctionFinalize commands |
Confirmed by integration test for timer seed and durable command finalization |
| Neutral merchant registration | neutralRegistrar.ts runs at the month boundary, reproduces the previous-month seed and preceding RNG consumption, and queues host 0 auctions in the same DB transaction as the month state |
Confirmed by PHP differential, engine boundary, PostgreSQL idempotency, and full integration tests |
Unique auction mutations deliberately run in the turn-engine database transaction. Point reads use row locks and deductions use conditional updates, so simultaneous requests cannot spend the same previous-season inheritance points twice. A failed open command creates neither an auction nor an orphaned inheritance request marker.
Neutral registration intentionally counts every historical host-0 resource
auction, including finished rows, because the legacy probability denominator
does not filter on finished. Average gold/rice includes only generals with
npcState < 2 and is clamped to 1,000..20,000. The month marker and generated
auction rows are persisted together under a PostgreSQL advisory transaction
lock, preventing the same month from producing duplicate merchant auctions
during daemon retry.