Skip to content

feat(auction): complete matching engine and frontend components#147

Open
noahbclarkson wants to merge 188 commits intomasterfrom
rewrite-2
Open

feat(auction): complete matching engine and frontend components#147
noahbclarkson wants to merge 188 commits intomasterfrom
rewrite-2

Conversation

@noahbclarkson
Copy link
Copy Markdown
Owner

Auto-Tune Auction House

This PR completes the auction house infrastructure and frontend.

Backend Changes

  • Added WebSocket subscriptions for order book updates
  • Implemented tick size validation
  • Added Trade History endpoint
  • Added stub for /api/auction routes

Frontend Changes

  • Added new /auction page route
  • Added OrderBook component (bids/asks display)
  • Added TradeHistory component
  • Added PlaceOrder component with buy/sell tabs
  • Updated layout/header to include the Auction House

Tests all pass across workspace.

noahbclarkson and others added 30 commits February 18, 2026 22:51
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.
noahbclarkson and others added 30 commits March 28, 2026 15:11
…tandard_with_mm scenario

- Add standard_with_mm_fixed_guild (standard+MM + 2 GuildBuyers at 5% threshold)
- Add exploiter_stress scenario (standard+MM + 2 Exploiters from tick 0)
- Add --exploiter-stress-test: head-to-head standard+MM vs +Exploiters
- Add --fine-threshold-sweep: 1%, 3%, 5%, 7%, 10% GuildBuyer threshold sweep
- Add avg_spd to SimSummary for more complete spread analysis
- Add MarketMaker to regression archetype map; update baselines
- Remove guild_stability from regression (inherently non-deterministic: random threshold)
- Fix pre-existing clippy map_clone issue in api-server price_computer.rs
- 5/5 regression: PASS (avg displacement 0.000% across all scenarios)
Built the exchange rate fetching and display system:

- ExchangeRate model: server exchange rate vs global true-price baseline
  (rate > 1 = more expensive, < 1 = cheaper)
- ExchangeRateService: fetches from /api/servers/exchange-rates on the
  shared API server, stores in-memory cache, refreshes on configurable
  interval (default 15 min). Enabled only when price-reporter is configured.
- ExchangeRateConfig: exchange-rate.enabled + fetch-interval-minutes in
  config.yml (defaults to true / 15 min)
- TaskScheduler: schedules periodic fetch (30s delay on startup, then every N min)
- /at admin exchange: shows all servers' exchange rates, local server's own
  rate, and how many servers are contributing data

Also fixed: ExchangeRateService private record field name (serverId as String,
matching actual JSON UUID string from PostgreSQL/serde).
Bug fixes:
- compute_avg_volatility: was using query_row (1 row only) → fixed to
  prepare+query+query_map to collect all 14 daily price samples. Volatility
  was reporting 0.0000 for ALL scenarios; now correctly ~0.05-0.20.
- SPD display: sweep functions used avg_bpd for SPD column → fixed to avg_spd

New scenarios:
- guild_stability_mm_fixed_guild: guild_stability player mix + MM + 2 GB @ 7%.
  This is the best-performing config from prior runs (D/G 1.76x, vol 0.007,
  GDP 898K in single-seed). Deterministic 7% threshold replaces the
  non-deterministic random-threshold guild_stability.
- exploiter_cap_test: 1 Exploiter (=5% of 12 players). Tests whether a
  capped Exploiter population provides buy/sell balance without the
  hyperinflation seen at 2 Exploiters.

New CLI:
- --multi-seed-compare: runs guild_stability at 7% vs 10% threshold, 5 seeds
  each (10 total runs). Reports mean ± std for GDP, D/G, buy ratio, vol.

Regression suite:
- Replaced marketmaker_test (random GuildBuyer thresholds, non-deterministic)
  with guild_stability_mm_fixed_guild (fixed 7% threshold, fully deterministic).
  Regression suite is now fully deterministic across all scenarios.

analyzer.rs:
- analyze_dir: changed spread column from avg_bpd → avg_spd (more useful
  for spread health assessment).
1. MarketEngine: make loadOverrideCache() final — eliminates
   ConstructorCallsOverridableMethod (field/method not final was the root)
2. ShopManager: suppress ConstructorCallsOverridableMethod — false positive,
   private loadCache()->loadDefaultItems()->public addItem() only reachable
   after object fully constructed
3. SellGuiListener: suppress AssignmentToNonFinalStatic — Guice @singleton
   guarantees single instance, instance=this in constructor is safe
4. ExchangeRateService: rename field lastFetchedAt->lastFetchedAtInstant —
   eliminates AvoidFieldNameMatchingMethodName (field conflicted with accessor)

Violations: 104 → 100. Build passes clean.
Players can now click 'Set Alert' on any item to open a modal dialog
that generates the /autotune alert add command. Dialog includes:
- ABOVE/BELOW direction toggle (color-coded)
- Price input pre-filled with current buy price
- Live command preview
- Copy-to-clipboard button
- Expandable help explaining how alerts work

No more guessing the command syntax — click, set price, copy, paste in-game.
Players can no longer have orders locked in escrow forever.

Changes:
- AuctionOrder: add expiresAt field + EXPIRED status
- AuctionRepository: add expires_at to all queries; add findExpiredOrders()
- AuctionManager: inject AuctionConfig, wire expiresAt into order creation,
  add processExpiredOrders() for BUY refund and SELL item return
- TaskScheduler: register periodic expiration check (default every 15 min,
  configurable in config.yml)
- ConfigManager: parse auction config section
- AutoTuneConfig: add AuctionConfig record
- config.yml: add auction.{default-duration-hours, expiration-check-interval-minutes}
- V1__Initial_Schema.sql: add expires_at NOT NULL column
- Fix 3 test files to include AuctionConfig.defaults() in constructor

Buy orders: escrowed funds refunded on expiration. Sell orders: items
returned to inventory if player is online (or left as EXPIRED in DB for
later /auction reclaim). Players notified by in-game message.
Players who were offline when their sell orders expired couldn't get their
items back. Now /auction reclaim finds all EXPIRED sell orders for the player
and returns the items to their inventory (marks as RECLAIMED in DB).

New OrderStatus: RECLAIMED
New repo query: findExpiredSellOrdersByPlayer(playerUuid)
New manager method: reclaimExpiredOrders(Player) + getExpiredSellOrdersForPlayer
New command: /auction reclaim
New archetype: InsiderTrader (Archetype variant + decide logic + GUI panel)

**Design**: Mean-reversion player. Tracks rolling price history per item.
When price < rolling_mean * (1 - threshold) → BUY (expecting rebound).
When price > rolling_mean * (1 + threshold) → SELL (expecting pullback).
Near mean → neutral.

Distinct from:
- Exploiter: rides momentum (buy when rising), InsiderTrader fades it
- MarketMaker: posts around perceived fair value, InsiderTrader exploits
  deviations from recent price history
- ValueBuyer: uses static perceived_values, InsiderTrader uses live price history

Insight from testing:
- MM reduces avg_vol from ~0.19 (no MM) to ~0.006 (2 MM+GB) — 30x stabilization
- InsiderTrader with 2 ITs added to standard+MM: vol 0.0207→0.0310 (worse)
  → In Farmer-dominated sell-heavy economy, ITs buy dips which are plentiful,
    adding more buy pressure without creating new sell pressure
  → ITs work better in balanced or slightly buy-dominated economies
  → MM remains the primary stabilizing mechanism; IT is supplementary

Also:
- Multi-seed compare now tests GuildStability+MM (1MM+1GB) across 5 seeds
- insider_trader_test scenario added
- 5/5 regression PASS
…oard

Adds browser-accessible economy health diagnostics for remote admins
who can't be in-game to run /at admin health.

Java plugin:
- WebServer: inject ShopManager + LoanManager
- New GET /api/admin/health endpoint — mirrors in-game health command
  as JSON (GDP, debt, D/G ratio, circuit breaker tier, buy ratio,
  avg spread, volume multiplier, inflation label, top 5 volatile
  and undersold items)
- adminHealthData() refactored from AdminCommand into WebServer
  using the same calculations and thresholds

web/ frontend:
- New /admin page with health score badge (0–100), key metric cards
  (GDP, debt, D/G ratio, trade mix, avg spread, volume activity),
  top volatile + undersold items tables, circuit breaker tier legend
- Admin link added to header navigation (desktop + mobile)
- Auto-refreshes every 15s
- Graceful error state when API unreachable

PMD: 105 violations (all style — unchanged from pre-commit)
Adds guild economy dashboard as a new player-facing feature:
- /guild — shows your guild's economy stats (volume, net position, debt, etc.)
- /guild stats <tag> — view a specific guild's stats
- /guild top — top 10 guilds by trading volume

Guild detection: uses Vault Permission groups as guild identifiers.
Guild membership is synced on player join and persisted in at_players.guild_tag.
If no Vault permission provider is available, guild features silently degrade.

Schema: added guild_tag VARCHAR(64) NULL to at_players table.

Also adds:
- GuildService with GuildStats record aggregating member data
- Vault Permission setup in AutoTune (non-fatal if absent)
- Permission provider in AutoTuneModule for DI
- PlayerListener now syncs Vault group → guild_tag on join

Closes PLAN.md: guild economy dashboard (engagement driver)
…API docs page

- Expand feature-cards from 3 to 6: add Guild Economy Dashboard,
  Auction House, Price Alerts, and Cross-Server True Prices
- Add /api-docs page: full HTTP API reference covering server
  registration, price submission, true-prices, exchange-rates,
  error codes, rate limits, and auth
- Add API docs link to header navigation
Players can now look up any other player's portfolio via the web
dashboard. Shows holdings, average buy prices, unrealized P&L, vault
balance, active loans, and net worth — all computed from the last
90 days of transaction history.

Java:
- PortfolioService: aggregates transaction history → holdings with P&L
- PortfolioDto: data transfer record for the portfolio payload
- WebServer: new GET /api/portfolio/{playerName} endpoint
- EconomyManager: add getBalance(OfflinePlayer) overload for web context
- PlayerRepository: add findByName() lookup
- ItemRepository: expose getJdbi() for raw SQL queries

Web:
- /portfolio page with player search, summary cards, holdings table,
  and active loans panel
- Portfolio link in nav header
- TypeScript types + api.portfolio.get()
- Add MarketEvent model (types: DEMAND_SURGE, SUPPLY_GLUT, INFLATION_BOOST, DEFLATION_DROP, GOLD_RUSH, CUSTOM)
- Add MarketEventRepository (JDBI persistence)
- Add MarketEventService (event lifecycle, price multiplier application)
- Hook into MarketEngine.calculateNewPrice() to apply price change multipliers
- Add /at event command (list, active, trigger, cancel, info)
- Add at_market_events DB table
- Add market-events config section (YAML template + defaults)
- Fix test fixtures (MarketEngineTest, LoanManagerTest, EconomyManagerTest)
- Use Locale.ROOT for case-insensitive material matching

Market events amplify/dampen price velocity for matching items without overriding natural market forces. Players see chat broadcasts when events start/end. Admins can trigger events with custom materials, multipliers, and durations via command.

Co-authored-by: Arc <arc@noah.dev>
…cleanup

Issue: data.db grows unbounded because DatabaseCleanupManager only pruned
three of nine tables. The auction order book and fill history accumulated
forever, as did ended market events.

Changes:
- CleanupConfig: added auctionOrders, auctionFills, marketEvents fields
- ConfigManager.parseCleanupConfig: parse new cleanup sections from YAML
- AuctionRepository: add deleteOrdersOlderThan(), deleteFillsOlderThan(),
  countOrders(), countFills()
- MarketEventRepository: add deleteEndedOrCancelledOlderThan(), count()
- DatabaseCleanupManager: clean up auction orders (terminal status older than
  retention), auction fills (older than retention), market events (ENDED/
  CANCELLED older than retention); inject AuctionRepository, MarketEventRepository,
  DatabaseManager; update CleanupStats to include new table counts
- AutoTuneModule: provide DatabaseCleanupManager singleton
- config.yml: document new cleanup sections with default retention days
  (orders: 30d, fills: 60d, events: 7d)

Config defaults are conservative — admin can tune per-server needs.
Active OPEN/PARTIALLY_FILLED auction orders are never deleted by cleanup.
New src/events.rs — mirrors Java MarketEventService.java:
- 6 event types: DEMAND_SURGE, SUPPLY_GLUT, INFLATION_BOOST,
  DEFLATION_DROP, GOLD_RUSH, CUSTOM
- Pattern matching: exact, PREFIX_*, *_SUFFIX, *MIDDLE*
- apply_event_multiplier() — sums all matching event effects
- 10 unit tests, all passing

Integration:
- engine.rs: calculate_new_price() applies events after trend dampening
- simulation.rs: Simulation.events field, active events filtered per tick
- main.rs: Scenario.events field, market_event_test scenario,
  all 14 existing scenarios initialize events=Vec::new()

Regression: 5/5 PASS (0.000% delta) — non-event scenarios unaffected.
New market_event_test: 14-day standard+MM+GB economy with all 4
event types across different time windows.

Formula verification: Java computeEventEffect() behavior confirmed
(Java comments describe intent; actual implementation uses suppressed
vs extra as documented in MEMORY.md).
New /at admin audit command runs a comprehensive system health check
covering 5 areas and reports actionable issues to admins:

- Vault & dependencies: checks Vault Economy provider is registered
- Configuration: validates baseSpread, maxPriceChange, tick interval,
  loan interest rates, exchange rate fetch status, cleanup interval
- Economy state: GDP sanity, Debt/GDP ratio, stale snapshots, expired
  overrides, stale item prices (no history in 7 days)
- Database: table sizes, large-table warnings (>1M transactions or
  >5M market history rows), stuck market events count
- Circuit breaker: tier/badge, interest multiplier, overdue loans

Each check returns ✅ / ⚠️ / ❌ with a specific explanation. Summary
line at the end tells admins how many issues need attention.

Also adds LoanManager.getOverdueLoans() helper (no-side-effect read).
DatabaseCleanupManager.CleanupStats was already public — used directly.
…hart, events panel

web-optimizer now models the full Auto-Tune market event system:
- market-engine.ts: MarketEvent interface, EventType enum, matchesMaterialPattern(),
  computeEventEffect() (mirrors Java MarketEventService.computeEventEffect exactly),
  applyEventMultipliers(), getNetEventMultiplier(), simulatePriceWithEvents()
- MarketEventsPanel: add/remove events with type selector, multiplier, material
  search, and quick presets (ore families, gold family, building blocks, etc.)
- ProjectionChart: SVG price trajectory — shows baseline vs with-events lines,
  material selector synced with page state, 60-tick horizon (5h at 5min ticks)
- Simulator page: events panel in left column, projection chart above price preview
- computeEventEffect verified against Java: DEMAND_SURGE/SUPPLY_GLUT asymmetric,
  INFLATION_BOOST/DEFLATION_DROP always-positive/negative drift, GOLD_RUSH/CUSTOM
  symmetric amplification. Matches Rust events.rs exactly.
The shadow JAR minimize plugin was stripping META-INF/versions/ from
Guice's multi-release JAR, breaking Paper's classloader and causing
'zip file closed' errors on plugin load. Additionally, Guice's complex
SPI/service-loading behavior doesn't survive relocation in Paper's
plugin classloader hierarchy.

Fixes: 'zip file closed' / 'zip file error' on Paper 1.21.4 startup.
- next.config.js: add outputFileTracingRoot to tell Next.js the
  workspace root explicitly (silences the multiple lockfiles warning)
- package.json: add overrides.eslint-visitor-keys to ^3.0.0 — v5.x
  (pulled in by ESLint 9.x) requires Node.js 20.19+, but the env has
  20.10.0. The override pins a compatible v3 version.
The minimize{} block was causing 'zip file closed' errors in Paper's
plugin classloader. Even with selective excludes, it strips META-INF
entries and versioned classes that Paper's classloader depends on during
plugin initialization.

Relocation already provides namespace isolation from other plugins.
Size reduction is not worth the classloader instability. JAR grows from
~22MB to 30MB — negligible for a server plugin.
…d view toggle

web-optimizer/: ChangelogSection — timeline of 6 recent commits with icons, tags, dates
web-optimizer/: CompareSection — Auto-Tune vs Essentials/ShopGUI+/PlayerShops feature matrix
web/: ItemGrid component — card-based browse view with mini spread bars
web/items: table/grid view toggle (LayoutGrid/List icons in header bar)
Shadow JAR relocation (merging classes into relocated namespaces) is
incompatible with Paper's PaperPluginClassLoader on Paper 1.21.4.
The classloader's ZIP lifecycle management conflicts with shadow's merged
classpath, causing 'zip file closed' errors during class loading.

Paper's plugin classloader already isolates plugins from each other.
Namespace relocation is unnecessary and harmful in this environment.
The JAR is now a flat merge at original package paths (30MB). Build
passes, plugin should load cleanly on Paper 1.21.4.
Root cause: 'zip file closed' errors on Paper 1.21.4 were caused by the
shadow JAR merging multiple multi-release JARs (sqlite-jdbc, mariadb)
into one bundle. The merged META-INF/versions/ entries combined with
Paper's ClassloaderBytecodeModifier create a race condition that closes
the JAR ZIP while classes are still being loaded.

Fix: Remove shadow JAR entirely. Dependencies are now declared in
paper-plugin.yml's libraries: section and Paper downloads+manages them
at runtime. The plugin JAR contains only plugin classes (~1.2MB vs
30MB before). This avoids all multi-release JAR conflicts and classloader
lifecycle issues in Paper's plugin classloader.

Breaking change: plugin JAR no longer bundles dependencies. Requires
Paper to download libraries from Maven Central on first load.
Revert to original shadow JAR with relocate+minimize (guice excluded).
This was the working configuration before recent misguided changes.
The libraries: section in paper-plugin.yml caused Paper to download
deps alongside the shadow JAR creating separate classloaders — not
a valid approach.
Runtime deps added to testImplementation so test classpath mirrors the
bundled JAR classpath (fixes test ClassNotFoundException failures).

Shadow JAR config unchanged: relocate + minimize (guice excluded).
paper-plugin.yml unchanged: no libraries: section (pure shadow).
These were the real plugin load failures, masked behind the zip-file-closed
classloader error. Two pre-existing Guice DI misconfigurations:

1. Server was not bound — provideGuildService requested Server but
   there was no binding. Added bind(Server.class).toInstance(plugin.getServer())
2. AutoTuneConfig was not injectable — provideScoreboardManager requested
   AutoTuneConfig but there was no binding. Added @provides method that
   returns plugin.getConfigManager().getConfig()
…patibility

Cloud 2.0.0-beta.14 has a Brigadier argument registration compatibility
issue with Paper 1.21.11 causing 'Argument is not declared in syntax'.
Disabled native Brigadier in CommandManager — commands still work via
Cloud's standard annotation parser.

Also fixed GuildCommand: had two @command('guild') methods and two
@command('guild stats') methods causing duplicate chain registration.
Collapsed to unique paths: /guild, /guild stats, /guild stats <guild>, /guild top.
createNative triggers Brigadier initialization which fails on Paper 1.21.11
due to breaking API changes in Paper's Brigadier integration (Cloud issue #132).

Using the standard constructor with SenderMapper.identity() avoids Brigadier
entirely — commands register via Cloud's standard annotation parser through
Paper's command dispatch without native Brigadier support. Commands still work.
…1.11

Paper 1.21.11 has breaking Brigadier API changes that affect
cloud-paper:2.0.0-beta.14's Brigadier integration, causing
"Argument is not declared in syntax" errors during command registration.

Use createNative() which nullifies brigadierManagerHolder, then
additionally remove BRIGADIER and NATIVE_BRIGADIER capabilities
via reflection to prevent Paper 1.21.11's Brigadier API from
interfering with Cloud's annotation parsing pipeline.
Cloud v2 requires all @argument annotations to have corresponding
<argument> syntax fragments in @command strings. Missing fragments cause
"Argument is not declared in syntax" errors during command registration.

Fixed commands:
- TreasuryCommand: treasury deposit <amount>, treasury withdraw <amount>
- AutosellCommand: autosell minprice <material> [price], autosell minprice remove <material>
- AuctionCommand: auction browse <material>, auction sell <price> <quantity>, auction buy <material> <price> <quantity>, auction cancel <orderId>, auction history <limit>
- PriceAlertCommand: alert add <material> <price> <type>, alert remove <identifier>, alert rearm <identifier>, alert toggle <identifier>
- AdminCommand: admin transactions [player], admin price set <material> <price> [hours], admin price remove <material>, admin item spread <material> <value>, admin item maxchange <material> <value>, admin item info <material>, admin item reset <material>

Also updates CommandManager to remove Brigadier capabilities via reflection
for Paper 1.21.11 compatibility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant