feat(auction): complete matching engine and frontend components#147
Open
noahbclarkson wants to merge 153 commits intomasterfrom
Open
feat(auction): complete matching engine and frontend components#147noahbclarkson wants to merge 153 commits intomasterfrom
noahbclarkson wants to merge 153 commits intomasterfrom
Conversation
Co-authored-by: root <root@vmi3091932.contaboserver.net>
…slots, loan exploit) (#137) * fix: resolve hash mismatch between getItemHash and getMaterialHash Plain vanilla ItemStacks in Paper 1.21.4 always carry an ItemMeta even when no custom data is set, causing getItemHash() to hash just the material name (e.g. "STONE") while shop items stored via addItem() used getMaterialHash() which hashes "MATERIAL:STONE". The two hashes never matched, so getItemByStack() returned Optional.empty() for every plain item, silently breaking shop lookups, autosell pickup, and sell-GUI matching for all default material-based shop entries. Fix: when the ItemMeta carries no distinguishing data (no display name, lore, enchants, or custom model data) fall back to getMaterialHash(), keeping the two code paths consistent. * fix: restrict autosell inventory scan to storage slots only PlayerInventory.getSize() returns 41, which includes armor slots 36-39 and the off-hand slot 40. Both AutosellManager.sellInventory() and AutosellListener.scanAndSellInventory() iterated up to getSize() and used getItem(slot), meaning armor and off-hand items that match a shop entry (e.g. a diamond chestplate when DIAMOND is a shop material) could be silently sold and removed from the player. Fix: replace the getSize()-bounded loop with getStorageContents() in both call sites. getStorageContents() returns exactly slots 0-35 (the player's main inventory including hotbar), leaving armor and off-hand untouched. The slot index returned by getStorageContents() still maps 1:1 to setItem(slot, null), so item removal remains correct. * fix: remove loan repayment overpayment exploit and validate amounts Two related bugs in the loan repayment flow: 1. Overpayment money exploit repayLoanAsync() capped the actual withdrawal at paymentAmount = amount.min(currentBalance) to avoid overcharging, but then calculated overpayment = amount - currentBalance and deposited that back to the player. When amount > currentBalance the player was charged only currentBalance yet received (amount - currentBalance) as a 'refund' they never paid — a net gain of free money. Example: balance 00, player types '/loan repay 500'; they pay 00 but receive 00 back, effectively settling the loan for 00. Fix: remove the overpayment deposit entirely. paymentAmount is already capped at currentBalance so there is never a genuine overpayment to refund. 2. Non-positive amount bypass Neither LoanCommand.takeLoanWithTerm / repayLoan nor LoanManager.requestLoanInternal / repayLoanAsync validated that the supplied amount is positive. A negative loan amount passed the maxLoan ceiling check (negative < 100) and then deposited a negative value via economy.depositPlayer(), which many Vault implementations treat as a withdrawal. Similarly a negative repayment would pass economy.has() (any balance > negative) and then withdrawPlayer() with a negative value. Fix: guard at both the command layer (early return with general.invalid-amount) and the manager layer (LoanResult.error) for defense-in-depth. --------- Co-authored-by: root <root@vmi3091932.contaboserver.net>
…e on join (#138) Two bugs in PlayerListener.checkLoanWarning(): 1. Wrong placeholder key: used 'days' but messages.yml warning-due template expects 'time_left'. Result: players saw literal '<time_left>' in the message. Fix: use 'time_left' with proper time formatting (matching LoanManager.processWarnings). 2. Premature 'defaulted' message: sent 'loan.defaulted' ("Credit score reduced by X") for ACTIVE loans that are past-due but not yet processed by the scheduled tick. No penalty has been applied at that point, so this was misleading. Fix: add 'loan.overdue' message key and use it when loan is past due but ACTIVE. Co-authored-by: root <root@vmi3091932.contaboserver.net>
- ParameterPanel: add 4 quick-preset buttons (New Server, Established Market, Economy Crash, Economy Boom) that instantly fill all parameter sliders - Mobile layout: responsive Tailwind classes (sm:/lg:) across simulator page, header, price-preview, spread-chart — stacks cleanly on small screens, header nav collapses to icon-only below sm breakpoint, chart heights shrink on mobile (h-48 sm:h-64), padding adjusted throughout - README.md: added to web-optimizer/ covering project overview, local dev setup, Vercel deployment (CLI + dashboard), tech stack, and project structure
…io matrices Phase 1 of Auto-Tune Real-Price Calculator. Computes 'true' item prices from cross-server price ratio observations using least-squares optimization in log-space. ## Algorithm - Collect r[i][j] = P_i/P_j ratio matrices from m servers - Aggregate using weighted geometric mean in log-space - Build least-squares system: log(P_i) - log(P_j) ≈ log(r_ij) - Anchor one item to fix absolute scale - Solve via LU decomposition + exponentiate ## Crate Contents - src/solver.rs — compute_prices_from_servers() + compute_prices_with_config() - src/aggregation.rs — AggregationMethod enum + aggregate_ratios() - Handles sparse matrices (0.0 = missing ratio) - GeometricMean / ArithmeticMean / Median aggregation methods - src/validation.rs — validate_ratio_matrix() + find_inconsistencies() ## Tests - 14 unit tests (solver, aggregation, validation) - 14 integration tests (accuracy, sparse, weighted, error cases) - 2 doc tests - 30 total passing ## Dependencies - nalgebra 0.32 (LU decomposition) - thiserror 1.0 (error types) - approx 0.5 (test assertions)
- New /true-prices route with interactive calculator UI - PriceCalculator component for ratio matrix input - Client-side least-squares solver (browser-compatible) - Navigation updated with True Prices link - Build passes, static page generated at 3.83 kB This completes Phase 4 (Frontend Integration) scaffold. Next: API route + backend solver integration.
Phase 2+3 of the real-price-calculator system: **Phase 2: Server Authentication** - HMAC-SHA256 API key generation (32-byte random, hex-encoded) - Constant-time key comparison (timing-attack resistant) - Actix-web middleware: ApiKeyAuth validates Bearer tokens against DB - Keys stored hashed (SHA-256); raw key shown exactly once on registration **Phase 3: Data Collection API (actix-web + SQLx + PostgreSQL)** - POST /api/servers/register — register server, receive UUID + API key - POST /api/servers/:id/prices — submit ratio matrix (authenticated) - GET /api/prices/true — current true prices (least-squares solved) - GET /api/prices/history/:item — price history for any item - GET /api/servers/exchange-rates — per-server economy multiplier **Architecture:** - price-solver crate integrated as workspace dependency - JSONB storage for ratio matrices (simpler than double precision[][]) - Background tokio::spawn() for price recomputation after each submission - Weighted by player count (larger servers = more trusted data) - PostgreSQL migrations: servers, price_submissions, true_prices, price_history **Tests:** 12 passing (9 auth + 3 price computer confidence scoring) Workspace Cargo.toml created; target/ added to .gitignore
Overhauls the landing page and simulator UI with enhanced visualizations, animations, and detailed factor breakdowns. Synchronizes the TypeScript market engine logic with the core Java and Rust implementations, specifically adding distinct trader scaling to liquidity calculations. Includes a comprehensive instructions file to assist with development workflows and architectural understanding.
sweep.rs: - Player archetype lookup: use unwrap_or_else with panic message listing known archetypes instead of bare unwrap() - Sweep param grid read site: add expect() with invariant description instead of bare unwrap() on hardcoded keys (grid generator + read site must stay in sync) - Remove spurious * deref on run_single() call since params are now owned f64 values regression.rs: - Same archetype-map lookup fix as sweep.rs player.rs: - Normal::new(base, base*0.15): remove nested unwrap_or_else chain. Normal::new cannot fail for positive base prices. Add expect() with invariant documentation. All 5 regression scenarios still produce 0.000% delta after changes.
1. scanAndSellInventory snapshot bug (AutosellListener)
- Loop iterated over cloned inventory snapshot
- After successful sellPickup, items removed from actual inventory
- Next iteration re-saw same slot in cloned snapshot → duplicate sell
attempts (silently failing with "insufficient items")
- Fix: null out storageContents[slot] after each successful sell
2. Loan interest tax dead code (LoanManager)
- processInterest() calculated interest but never called
treasuryService.collectLoanInterestTax()
- Tax revenue never flowed to server treasury
- Fix: inject TreasuryService + call collectLoanInterestTax()
- Also updated LoanManagerTest with mock TreasuryService
…nsolidation, retry queue)
…tter error messages
…rver fix - docs: new MIGRATION.md covering all rewrite-2 breaking changes (config, database, commands, auction, API, market engine) with upgrade checklist - fix(build): WebServer.java — remove @SuppressWarnings annotation that caused javac compilation error (annotation on lambda expression confused parser) - docs(readme): fix remaining Unprotesting→noahbclarkson URLs (releases, actions) and replace outdated personal contact with GitHub Issues link - docs(web-optimizer): add missing /true-prices, /exchange-rates, /servers to pages table; update project structure to match current code
Replaces single-ratio circuit breaker (10x debt/GDP → full pause) with 3-tier graduated response: TIER1 (>3x): cap interest at 50% of normal TIER2 (>5x): cap interest at 25% of normal TIER3 (>10x): full pause (original behavior) Simulation evidence (2026-03-27): stressed-economy hit 23,196x debt/GDP because breaker fired too late — GDP already collapsed. Standard economy at 14.9x is stuck with 0% interest but compounding debt once active. Tiered approach allows partial interest accrual in the 3-10x range, preventing debt snowball before it reaches catastrophic levels. Config changes: - LoanConfig: replaces debt_gdp_circuit_breaker_ratio (10.0) with debt_gdp_tier1_ratio (3.0), tier2_ratio (5.0), tier3_ratio (10.0), tier1_interest_cap (0.5), tier2_interest_cap (0.25) Simulation: regression baselines regenerated (intentional behavior change). clippy clean. all 3 regression scenarios PASS.
…s in Java Replaces single-ratio circuit breaker (10x debt/GDP → full pause) with 3-tier graduated response in LoanManager: TIER1 (>3x): interest capped at 50% of normal rate TIER2 (>5x): interest capped at 25% of normal rate TIER3 (>10x): interest fully paused (original behavior) Simulation evidence (2026-03-27): stressed-economy hit 23,196x debt/GDP because the old single-threshold breaker fired too late — GDP had already collapsed from compound interest during the ramp-up. Tiered approach slows compounding in the 3-10x range before emergency pause kicks in. Config changes: - AutoTuneConfig.LoanConfig: replaces debtGdpCircuitBreakerRatio (double) with debtGdpTier1Ratio, debtGdpTier2Ratio, debtGdpTier3Ratio, tier1InterestCap, tier2InterestCap - ConfigManager.parseLoanConfig: reads new YAML keys with sensible defaults - config.yml: documents new tiered circuit breaker params - LoanManagerTest: updated for new LoanConfig signature
Also fixes the AuctionGui BUY_SLOTS layout bug where buy orders overflowed the right-hand pane for slots 23-35 due to incorrect column math using absolute gui coordinates instead of pane-relative values. New BUY_SLOTS use pane-relative positions directly. Changes: - CartConfirmGui: new class — 3-row confirmation dialog showing item, quantity, price per unit, subtotal, tax, and net total. Confirm button executes the transaction; Cancel returns to buy/sell GUI. Insufficient-funds state disables Confirm. - ShopGui: buy and sell button clicks now open CartConfirmGui instead of executing directly, eliminating accidental mis-clicks - AuctionGui: BUY_SLOTS and SELL_SLOTS redesigned to use pane-relative positions; buy pane repositioned at x=5, y=1 with width=3, height=4 (correct grid layout) - AutoTune: added getTreasuryService() accessor for CartConfirmGui
AnvilMinPriceGui opens a Paper anvil when player clicks an item in AutosellGui. The player types a price in the rename field; clicking the output slot reads the text via AnvilInventory.getRenameText() and sets it as the per-item autosell minimum. AutosellGui now: - Normal left/right click → opens anvil to set/edit min price - Shift+click → toggles item enabled/disabled (quick toggle preserved) - Lore updated to show both interactions Uses PrepareAnvilEvent + InventoryClickEvent (raw slot 2 = output). PlainTextComponentSerializer extracts plain text from the renamed item. Invalid input shows a red error message; blank text or "remove"/"cancel" clears the per-item override (reverts to global default).
…logic - Add guild_target_inventory field to PlayerAgent (HashMap<item, qty>) - Implement decide_guildbuyer(): buys when below target, holds when at/above target, sells only at 2x surplus - Wire GuildBuyer into decide() match block - Populate guild_target_inventory from pre-filled stock at factory time - Add guild_stability scenario (2 GuildBuyer + 4 Casual + 3 Farmer + 2 Trader) - Update player count println to include Newbie, AFKFarmer, GuildBuyer - Add guild-stability to regression and headless --all scenarios Key findings from guild_stability run: - GuildBuyers are net buyers (81 trades, 4% of volume) but reactive only - Economy still deflationary: avg price -68% after 14 days - Circuit breaker fires 6 times (debt/GDP climbs to 28x) - GuildBuyers do NOT stabilize prices — they lack proactive demand
- Home page now consumes livePrices from useAppContext() WebSocket hook - When price_update WebSocket message arrives, buyPrice/sellPrice update live between 30s polls; row flashes green for 1.5s - Top Movers card header shows pulsing 'Live' badge when WS connected - CLAUDE.md: fixed npm commands, updated simulation (8 archetypes, GuildBuyer, headless/CLI tools), added WebSocket live updates note
1. AutosellManager.sellInventory(): remove setStorageContents(contents) at end of loop. The stale snapshot was overwriting the players
GuildBuyer now buys when market price falls guild_price_dip_threshold (15-30%) below perceived value, acting as a proactive market stabilizer. This addresses the systemic underselling issue where prices settled 40-78% below base. Changes: - Add guild_base_inventory and guild_price_dip_threshold fields to PlayerAgent - guild_price_dip_threshold: 0.15-0.30 for GuildBuyer, 0.0 for all others - guild_base_inventory: copies guild_target_inventory at construction (fixed reference) - New decide_guildbuyer Phase 1: price-dip buying — buys up to base_inventory when buy_price < perceived * (1.0 - guild_price_dip_threshold) - Existing replenish logic becomes Phase 2 Simulation results (guild_stability scenario, 14d, 11 players): - GDP: 44,230 (vs 6,720 old — 6.6x improvement) - Debt/GDP: 17.1x (vs 28.2x — 40% better) - Buy ratio: 53.7% (vs 57.9% — more balanced) - Price change: -61.8% (vs -68.6% — prices less depressed) - Avg volatility: 0.0141 (STABLE) Regression baselines updated (guild_stability test now passes at 0.000% delta). All other scenarios (standard, spread_stability, low_player): unchanged.
…nflation) New ScoreboardManager: - Shows economy stats on player sidebar (GDP, Debt, Loans, Activity, Inflation) - Per-player scoreboard using Bukkit ScoreboardManager API - Refreshes every N seconds (configurable, default 30s) - Disabled by default; enable via config.yml → scoreboard.enabled: true - Gracefully degrades when disabled or no snapshot data available New ScoreboardConfig record in AutoTuneConfig: - enabled, title (max 32 chars), updateIntervalSeconds - Defaults: disabled, "Auto-Tune Economy", 30s Wired into AutoTune lifecycle: - ScoreboardManager.start() on plugin enable - ScoreboardManager.stop() on plugin disable - PlayerListener shows/hides scoreboard on join/quit Updated tests (MarketEngineTest, LoanManagerTest, EconomyManagerTest) to include ScoreboardConfig.defaults() in AutoTuneConfig constructors.
Mobile nav: header now has hamburger menu (md: hidden) with slide-down dropdown for all 6 nav links on small screens. ItemTable: wrapped in overflow-x-auto so the 7-8 column table scrolls horizontally instead of overflowing on phones. Added min-w-[640px] to prevent column crushing. Cleanup: removed 4 unused import/variable warnings across compare-chart, market-health-bar, stats-cards, transaction-feed.
EconomyManager (processCartAsync): - Phase 3: when sell items fail to remove, now refunds sell earnings (deposits back) for netCost < 0 mixed carts, not just refunding buy cost for netCost > 0. Prevents players from keeping sell earnings when items couldn't be removed. EconomyManager (processSellImmediate): - Comment expanded: try-catch covers executor shutdown (RejectedExecutionException), DB errors, interrupted threads, and any other unexpected failure from supplyAsync. AutosellManager: - Added playerMinPrices cache (ConcurrentHashMap) — eliminates blocking supplyAsync().join() for every item in inventory scans. - loadPlayer() now loads both enabled items AND all per-item min prices in a single DB round-trip. - setMinPrice/removeMinPrice keep cache in sync. - Added AutosellLoadResult record for clean async data passing. AutosellRepository: - Added getAllMinPrices() for batch-loading per-item thresholds.
…reaker init bug New scenarios: - sp08-moderate: sell_pressure=0.80, single LoanCascade at day 6. Tests tiered circuit breaker behavior (tier1=3x, tier2=5x, tier3=10x). Key finding: sp=0.80 alone hits tier3 by day 3-4 even without cascades. - buyer-heavy: 2 GuildBuyer + 3 Casual + 3 Hoarder + 2 Farmer + 2 Trader. Tests whether more Hoarders + GuildBuyers fix structural underselling. Result: GDP=103k, Debt/GDP=1.5x, buy ratio 49.2%. Spread tight (BPD=2.47%). vs guild_stability: GDP=44k, Debt/GDP=17.1x, buy ratio=53.7%. Bug fix: circuit breaker fired spuriously at tick 1 because GDP=0 at initialization (ratio=f64::MAX → TIER3). Fix: use -1.0 ratio when gdp==0, circuit breaker inactive until economy generates >0 GDP. Regression baselines regenerated (50149de).
…re-economy pattern
1. EconomyManager.removeItems(): was taking a snapshot, modifying in-place,
then calling setStorageContents() — which overwrites any items the player
received between snapshot and write-back (same stale-snapshot bug fixed in
sellInventory last cycle). Now modifies inventory array directly in-place,
no setStorageContents call needed since Paper returns the live slot array.
2. EconomyManager.processSellImmediate(): when removeItems() partially succeeded
(removed some items, returned false), the player lost items with no
restoration. preRemoval snapshot now restores when removeItems fails.
3. AuctionManager.recordFillAsync(): DB insert happened BEFORE economy ops
(seller Vault credit + buyer item delivery) — same processFill() bug that
was already fixed there but not applied here. Restructured to:
- Look up orders (sync, ForkJoinPool thread)
- Run seller credit + buyer item delivery on Bukkit main thread FIRST
via CountDownLatch/await, throwing if either fails (caller refunds escrow)
- Only after economy ops succeed: DB insert + order quantity updates
- DB failure after economy success: log for admin review (player already has
money and items — better outcome than DB showing fill that never delivered)
…restriction Phase 1 price-dip buying incorrectly required current_inventory < base before buying on a price dip. This prevented proactive buying when GuildBuyer inventory was at or above base level, contradicting the purpose of the mechanic. Fix: removed && current < base from the condition. GuildBuyers now buy on price dips regardless of current inventory level (up to target inventory). Behavioral change: GuildBuyers are now much more active market participants. guild_stability scenario results: GDP: 6,720 → 529,431 (+7,780%) Debt/GDP: 28.2x → 1.67x (-94%) Displ.: -68.6% → -55.5% (less depressed) Volatility: STABLE (0.0024 avg)⚠️ Trade concentration: 2 GuildBuyers now make 93.6% of all trades.⚠️ Buy ratio = 89.1% — extremely buyer-heavy economy. Regression baselines updated for guild_stability. clippy clean, cargo build clean.
MarketHistoryGui now shows timeframe buttons (green=selected, gray=others) in the header row. Clicking any timeframe reopens the detail view with history limited to that window: - 1H: last 12 rows (~1h at 5min intervals) - 24H: last 288 rows (full day) - 7D: last 2016 rows (7 days) - 30D: last 4320 rows (30 days) ItemRepository gains getPriceHistorySince(itemId, since, limit) for time-windowed queries. ShopCommand updated to pass default DAY timeframe for direct /shop history <item> calls.
840-config parameter sweep results as interactive filterable/sortable table. Filter by min buy ratio, max volatility, max debt/GDP, stable-only. Quick presets (Balanced economy, Low debt, Most stable). Key findings panel with insights from 840-config dataset. Static JSON export. Nav link in header. README updated. Also: - web/: 11 routes verified clean - web-optimizer/: 10 routes (added sweep-results) - Both static exports clean
Opens TransactionHistoryGui in ADMIN mode showing the most recent 200 transactions across all players. Optional player name argument filters to one player's transactions (name→UUID via Bukkit.getOfflinePlayerIfCached). The ADMIN mode already existed in TransactionHistoryGui (shows player names alongside each transaction row) but had no command to open it. Now admins have full audit visibility into economy activity. Minor cleanup: removed unused ClickEvent import.
- Add ConnectivityResult with Union-Find based graph connectivity detection - ConnectedComponent tracks internal edges and anchor-connection status - Per-item confidence: items in anchor component with many observations score highest; disconnected items get ≤10% confidence - LS system built only over anchored subgraph — prevents SingularMatrix from isolated items - New anchored bool in API response: shows if item is ratio-graph connected to the reference item - 4 new tests for connectivity and per-item confidence
…shboard - web-optimizer/ footer: github.com/Aetheraudios/Auto-Tune → noahbclarkson/Auto-Tune - web/ root layout: add Footer component with branding, GitHub link, version tag - Both builds verified clean
New /simulation-results page (Simulation Lab) displays structured analysis of all 15 market simulation runs from sim-output/. Each run shows: - Comparison table: GDP, debt, D/G ratio, buy%, BPD, SPD, default rate - Health badges (Healthy / Moderate / Unhealthy) based on D/G + buy ratio - Filter by max D/G ratio with quick presets - Sortable columns, pagination - Click-to-expand detail panel per scenario: - Price stability (top 10 items by range, with trend arrows) - Most active items (bar chart) - Player archetype breakdown - Loan activity summary (interest paid, defaults, circuit breaker config) New script: scripts/extract-sim-results.py — Python CLI that reads all simulation.db files from ../sim-output/ and emits structured JSON to web-optimizer/public/simulation-results.json. Replaces the Rust analyzer's text output with machine-readable data suitable for web consumption. New nav link: Sim Results (FlaskConical icon) in web-optimizer header.
- PMD: restrict to Error Prone rules only (193 violations vs 3873). Code Style, Design, Best Practices, Performance are non-blocking noise that buried real issues. Error Prone is what catches actual bugs. - ScoreboardManager: silent catch in removeScoreboard() now logs at DEBUG level instead of swallowing exceptions entirely. Scoreboard reset failure is non-fatal but should be visible in debug logs.
Targeted fixes (no suppress-as-annotation noise): - MarketHistoryGui: rename enum field label→tag (AvoidFieldNameMatchingMethodName), extract ZERO/ONE/CHART_MAX_TOP_BLOCK_ROWS/CHART_MIN_ROWS constants for chart math - ShopGui: extract SECTION_COLS_MAX/SECTION_ROWS_MAX/QTY_COLS_MAX for grid layout - EnchantmentPricing: extract MIN_ROMAN/MAX_ROMAN; fix duplicate import - WebServer: extract KEY_LIMIT/KEY_TIMESTAMP API field-name constants - AuctionGui/Command: fix toUpperCase/toLowerCase to use Locale.ROOT (UseLocaleWithCaseConversions), replace slot>=7 with BUY_SLOTS.length - AnvilMinPriceGui: extract ANVIL_OUTPUT_SLOT constant - AutosellGui: extract SECTION_COLS constant for grid wrap - CartConfirmGui: extract ENCHANT_PRICE_THRESHOLD constant - ChatSearchHandler: extract CANCEL_CMD constant Result: 193 → 170 PMD violations (−23)
MarketMaker posts both buy and sell orders around perceived fair value, earning from the bid-ask spread. Designed to counteract Farmer-dominated structural underselling. Key design: - Posts buy when inventory < target; sell when > target - Both sides simultaneously when at target (true market making) - Tight 2-5% buy/sell threshold (vs Farmer's 50%+) - mm_max_inventory: 30-80 per item, mm_target_inventory: 15-40 Sim results (guild-stability vs marketmaker-test, 14-day headless): - GDP: 396K → 687K (+73%) - Avg price change: -56% → -25% (halved underselling) - Debt: 9.5M → 1.3M (-86%) - Iron Ingot: -69% → +2% (key finding) - Volatility: 0.0041 → 0.0186 (still < 0.05 threshold) Also adds: Scenario::marketmaker_test(), archetype_map entry, GUI label/color (teal), player count display. clippy clean. pushed to rewrite-2
…esults fix - web-optimizer: new QuickStart landing section (3 install steps, player/admin command showcase) - web/: ItemsStatsBar above item table (item count, avg spread, market 24h, most volatile) - web-optimizer/simulation-results: fix React fragment key bug in tbody.map (bare <> replaced with explicit tr+key) Build: web/ 11 routes ✅ | web-optimizer/ 11 routes ✅
…item drops Sellers had no way to know when their auction listings were bought — the matching engine credited their balance but sent no message. Also fixed a bug where AuctionCommand.buy() dropped items on the ground for fills that AuctionManager.processFill() had already delivered via getInventory().addItem() — items were given twice. PMD: add @SuppressWarnings(PMD.AvoidFieldNameMatchingMethodName) to all model records. The rule fires on Java record component names (e.g. 'id' matches the 'id()' accessor) — normal for records, not a bug.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Auto-Tune Auction House
This PR completes the auction house infrastructure and frontend.
Backend Changes
Frontend Changes
/auctionpage routeOrderBookcomponent (bids/asks display)TradeHistorycomponentPlaceOrdercomponent with buy/sell tabsTests all pass across workspace.