Skip to content

Implement production network-wide multi-currency economy - #121

Open
remdui wants to merge 63 commits into
mainfrom
agent/advanced-economy
Open

Implement production network-wide multi-currency economy#121
remdui wants to merge 63 commits into
mainfrom
agent/advanced-economy

Conversation

@remdui

@remdui remdui commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

Implements a production-grade, MySQL-authoritative Economy feature and integrates Lottery with explicit built-in or legacy Vault backends. The implementation has received a second financial-integrity review focused on cross-server consistency, replay safety, identity ownership, Redis outages, concurrent transactions, and failure recovery.

HauntedMC currency topology

The implementation supports the intended network model without currency-name special cases:

  • Crowns / Credits: GLOBAL — one balance shared across every gamemode and physical server.
  • Essence: gamemode-local SERVER scope — available everywhere, with a separate balance per logical gamemode.
  • Relics / Soulstones: gamemode-local SERVER scope — available everywhere, with a separate balance per logical gamemode.
  • Money: gamemode-local SERVER scope and the primary Vault currency for that gamemode.
  • Physical replicas can share one logical gamemode key, while unrelated gamemodes remain isolated.
  • Future currencies may independently use SERVER, GROUP, or GLOBAL scope.

An exact HauntedMC example configuration and a production deployment checklist are included in the feature documentation.

Authoritative transaction model

  • MySQL is the only balance authority. Redis and local memory never authorize a debit or credit.
  • Every balance mutation, transfer, account creation, payment setting, cooldown, daily limit, and journal entry is committed transactionally.
  • Transfers lock accounts and usage rows in deterministic order.
  • Starting balances are created once and are journaled.
  • Database failures fail closed; there is no local-file, Redis-authoritative, write-behind, or emergency balance fallback.
  • Post-commit cache or Redis failures cannot turn a committed transaction into an apparent failure that a caller may retry unsafely.

Idempotency and replay protection

  • Every native mutation is identified by (source, idempotency key).
  • A persisted request fingerprint binds the key to operation type, currency, scope, accounts, amount, actor, reason, metadata, and bypass flags.
  • Retrying the exact request returns the original committed result and changes no balance.
  • Reusing the same key for a different amount, recipient, currency, scope, or operation returns IDEMPOTENCY_CONFLICT and applies nothing.
  • Built-in Lottery retries uncertain temporary failures using the same deterministic key.
  • Audit metadata cannot change debit/credit semantics.

Network-wide synchronization

  • Redis balance messages contain only account coordinates and committed version hints; they never contain authoritative balances.
  • Receiving servers reload the account from MySQL before updating local cache state.
  • Cross-server payment messages contain only the committed operation ID and routing coordinates.
  • A recipient server verifies the immutable MySQL transfer journal, reloads the authoritative recipient balance, and only then displays the payment notification.
  • Duplicate or stale messages are ignored through operation/version tracking.
  • Periodic single-flight authoritative refresh repairs missed messages and Redis outages.
  • Cache entries are limited to active local player sessions; an in-flight refresh cannot repopulate a cache after the player changes servers.
  • A global payment made on gamemode A therefore updates one MySQL account exactly once and converges safely on gamemode B when the recipient is online there.
  • Gamemode-local currencies ignore updates for another gamemode scope.

Identity and configuration integrity

  • Economy permanently binds the canonical DataRegistry player ID to its UUID.
  • Conflicting identity ownership fails closed instead of moving value between players.
  • Accounts also enforce unique UUID/currency/scope ownership.
  • Mutable player names are display metadata only and cannot select a monetary account.
  • Currency-family records prevent the same currency ID from being global on one server and local on another.
  • Shared-scope fingerprints protect precision, starting/min/max balances, negative policy, rounding, payment defaults, payment limits, cooldowns, and related invariants from cross-server configuration drift.
  • Scope and persistent identifier lengths are validated before database storage.

Player and administration features

  • Per-currency commands for balance, payments, high-value confirmation, paytoggle, history, and leaderboards.
  • Administrative balance inspection, add/remove/set, payment settings, freeze/unfreeze, history, status, and read-only verification.
  • Database-wide payment cooldowns and optional daily send/receive limits.
  • Transactional recipient paytoggle and account freeze checks.
  • Canonical recipient identity is retained across high-value confirmation.
  • Expanded /economy verify detects invalid balances, invalid journal entries, orphan rows, identity mismatches, accounts without creation history, and transactions without entries.

Vault

  • Vault remains genuinely optional at class-load time.
  • Exactly one configured primary currency is exposed per Paper server.
  • HauntedMC config exposes gamemode-local Money through Vault; named currencies such as Crowns and Credits use the native API.
  • Synchronous Vault calls return only after the database transaction commits or fails.
  • Strong balance reads, safe offline account handling, safe error responses, unsupported bank behavior, and provider conflict policies (FAIL, SKIP, REPLACE) are implemented.

Lottery

  • Explicit economy.backend: BUILTIN|VAULT; there is no automatic backend fallback.
  • VAULT remains the compatibility default for legacy servers.
  • Built-in mode uses a selected native Economy currency and deterministic idempotent operations.
  • Legacy Vault compensation behavior remains available for external providers that cannot participate in the Lottery database transaction.
  • Economy is an optional feature dependency used for load ordering without blocking Vault-only installations.

Framework, tests, and documentation

  • Adds optional feature dependency ordering to the framework.
  • Adds the native asynchronous EconomyApi, cache-only PlaceholderAPI integration, ORM entities, permissions, configuration, and operational documentation.
  • Adds a production deployment checklist and exact HauntedMC topology example covering stable logical gamemode keys, physical replicas, Redis outage behavior, replay testing, global/local scope testing, and Vault validation.
  • Adds regression coverage for scope topology, replica scope overrides, API validation, request fingerprints, identity/schema invariants, non-authoritative Redis contracts, immutable model boundaries, health semantics, optional Vault classloading, feature dependencies, and built-in Lottery retries.
  • All GitHub code-quality review threads have been addressed and resolved.

Final validation

Validated on head ad5db54822060b044277c1a2f5617107d9a4c751 against current main (4900a55a21cc52ba826c82bb007447454d9a60df):

  • CI Checkstyle and shell lint: passed.
  • Full Java 25 Maven verify: passed.
  • Unit/integration tests and 80% JaCoCo coverage gate: passed.
  • Packaging and dependency convergence: passed.
  • Bundled Paper platform acceptance boot/runtime gate: passed.
  • Review threads: all resolved.
  • PR mergeability: clean; main did not advance during the final review.

@remdui remdui closed this Aug 6, 2026
@remdui remdui reopened this Aug 6, 2026
@remdui remdui changed the title Implement production multi-currency economy Implement production network-wide multi-currency economy Aug 6, 2026
@remdui
remdui marked this pull request as ready for review August 6, 2026 15:35
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