Skip to content

feat(draft): server-generated draft pools with per-queue policy - #42

Open
ChronoFinale wants to merge 1 commit into
Balatro-Multiplayer:mqttfrom
ChronoFinale:feat/server-draft-pools
Open

feat(draft): server-generated draft pools with per-queue policy#42
ChronoFinale wants to merge 1 commit into
Balatro-Multiplayer:mqttfrom
ChronoFinale:feat/server-draft-pools

Conversation

@ChronoFinale

@ChronoFinale ChronoFinale commented Jul 22, 2026

Copy link
Copy Markdown

Server-generated draft pools + weekly cocktail

Adds a server-side deck+stake draft pool service (consumed by the MultiplayerPvP / Speedrun ban-pick flow) plus an admin-tunable weekly cocktail. Opt-in per queue — a mod that never calls the endpoints keeps its fully client-side draft.

What's here

  • POST /api/matches/:matchId/draft-pool — idempotent, server-generated pool of {deck, stake} tuples for a match, per-queue policy.
  • Generator (generate-draft-pool.ts) — a port of Botlatro's TupleBans reworked as Efraimidis–Spirakis weighted sampling (per-deck/per-stake weights are a live tuning knob) with stake-minimum guarantees. Bounded retry so a greedy dead-end can never surface as a runtime error, and validateDraftPolicies() runs every seed policy at boot so a misconfigured policy fails at startup, never per-request.
  • Weekly cocktail — the composition the Cocktail deck merges in matchmaking. Admin-set via PUT /admin/weekly-cocktail, persisted in a weekly_cocktail table (decks as text[]), defaults to an in-code SEED when unset, and degrades to SEED rather than crashing boot if the table is missing.
  • Typed errorsValidationError/NotFoundError/ForbiddenError on the shared AppError.
  • Pool items are self-describing (a cocktail tuple carries its decks+name), so one pool fetch covers everything.

Tests

  • Full server suite green (430 tests): generator invariants (300-seed sweep + weight-proportionality), request validation, wire-contract, route tests.
  • tsc --noEmit clean; drizzle-kit generate reports no schema changes.
  • Did manually testing with a client side branch to verify it pulls properly and the rotation also works

Notes for review

  • Draft — opening for eyes on the shape; the client (mod) side lands separately.
  • The 0017 migration's snapshot is hand-chained to 0003 because the repo's drizzle meta is missing 00040016 snapshots (pre-existing, repo-wide) — drizzle-kit generate can't diff against a missing baseline. Flagging as a separate repo-health item, not introduced here.
  • A draft-event audit layer was intentionally left out (write-only, no consumer yet); parked for when the moderation service can consume it.

Related


Related: addresses BalatroMultiplayerAPI#13 (weighted tuple-pool policy per gamemode — the server's per-queue weighted sampling implements it).

Server-side deck+stake draft pool service (consumed by the MultiplayerPvP/Speedrun ban-pick flow) plus an admin-tunable weekly cocktail. Opt-in per queue.

- POST /api/matches/:matchId/draft-pool: idempotent, server-generated pool of {deck,stake} tuples per queue policy.
- Generator: Botlatro TupleBans port reworked as Efraimidis-Spirakis weighted sampling (per-deck/per-stake weights as a live knob) with stake-minimum guarantees; bounded retry so a greedy dead-end never surfaces at runtime, and validateDraftPolicies() checks every seed policy at boot.
- Weekly cocktail: admin-set via PUT /admin/weekly-cocktail, persisted (weekly_cocktail table, decks text[]), defaults to in-code SEED when unset and degrades to SEED rather than crashing boot.
- Typed errors (ValidationError/NotFoundError/ForbiddenError) on the shared AppError. Self-describing pool items (cocktail tuple carries its decks+name).
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