Skip to content

feat(admin): ranked mod policy — banned/approved list with categories#44

Open
ChronoFinale wants to merge 2 commits into
Balatro-Multiplayer:mainfrom
ChronoFinale:feat/ranked-mod-policy
Open

feat(admin): ranked mod policy — banned/approved list with categories#44
ChronoFinale wants to merge 2 commits into
Balatro-Multiplayer:mainfrom
ChronoFinale:feat/ranked-mod-policy

Conversation

@ChronoFinale

@ChronoFinale ChronoFinale commented Jun 16, 2026

Copy link
Copy Markdown

What this adds

A staff-managed banned/approved mod list for ranked play. The game server polls it and colours opponents' mods red (banned) / green (approved) / white (unknown) in the lobby — automating the manual "check your opponent's mods against the banned list" workflow.

This PR is the website piece only. The game-server poller and the client-mod rendering are separate, forthcoming changes to their repos. The design:

  • Website (this PR) — source of truth + admin UI where staff add/edit entries (a human "push").
  • Game server — polls GET /api/mod-policy, caches in memory, and sends the policy to a client when it joins/creates a lobby.
  • Client mod — colours opponents' mods by status in the lobby list.

What's in here

  • Schema (0014_mod_policy): mod_policy_entries + mod_policy_categories + a many-to-many join (mod_policy_entry_categories) — a mod can belong to multiple categories.
  • Permission mod_policy.manage — granted to admin + owner; wired into the admin nav and the permissions-manager UI.
  • tRPC modPolicy router — CRUD for entries and categories; every procedure is permission-gated.
  • Public endpoint GET /api/mod-policy — a normalized record array [{ modId, name, url, status, versions, categories }] that the game server polls (Cache-Control: public, max-age=30).
  • Admin page /admin/mod-policy — add form + inline row editing, manageable categories (add/rename/delete), grouped banned/approved tables.
  • mod-policy-utils — id normalization (case-insensitive, punctuation-significant) + zod schema (id & name required), with bun:test coverage.

Note for reviewers — the fix(drizzle) commit

The first commit (fix(drizzle): repair 0011/0012 snapshot id collision) is a small, independent fix. 0011 is a data-only migration (no schema change), so drizzle-kit reused 0010's snapshot id for it — leaving 0011/0012 pointing at a colliding parent, which made drizzle-kit generate abort. This was pre-existing on main, unrelated to the feature, and blocked generating this migration's snapshot.

The fix re-mints 0011's id and repoints 0012.prevId. These ids are dev-time bookkeeping for drizzle-kit only — db:migrate never reads them — so no schema and no applied migration changes. Happy to split it into its own PR if you'd prefer.

Verification

  • tsc --noEmit, Biome, and bun test (10/10) all clean.
  • Full 0000 → 0014 migration chain applied on a fresh database; all tables present.

Testing

  • Tested locally end to end. here are some snap shots

When a user has a banned mod

Screenshot 2026-06-16 at 12 41 54 PM

UX for adding/remove banned approved mods

Screenshot 2026-06-16 at 12 40 59 PM

Display when user clicks on profile to see mods

Screenshot 2026-06-16 at 3 28 18 PM

🤖 Generated with Claude Code

ChronoFinale and others added 2 commits June 16, 2026 15:19
0011_snapshot.json reused 0010's snapshot id as its own id (and prevId),
so 0011 and 0012 pointed at a colliding parent and `drizzle-kit generate`
aborted with a collision error. Give 0011 a fresh id and point 0012's
prevId at it, restoring a linear 0010 -> 0011 -> 0012 chain.

Snapshot ids are dev-time bookkeeping for drizzle-kit only (db:migrate
never reads them), so this changes no schema and no applied migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Staff-managed banned/approved mod list, served to the game server which
polls it and colours opponents' mods red/green/white in the lobby.

- mod_policy_entries + mod_policy_categories + many-to-many join
  (mod_policy_entry_categories): a mod can be in multiple categories
- mod_policy.manage permission; tRPC CRUD (entries + categories) as modPolicy
- public GET /api/mod-policy: normalized record array the game server polls
- admin page /admin/mod-policy: add form + inline row editing, manageable
  categories (add/rename/delete), grouped banned/approved tables
- mod-policy-utils (id normalization + zod schema; id & name required) with
  bun:test coverage
- migration 0014_mod_policy

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ChronoFinale ChronoFinale force-pushed the feat/ranked-mod-policy branch from b8be821 to b70c10f Compare June 16, 2026 20:19
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