Competitive ranked ladder for Yet Another Bingo,
modeled on MCSR Ranked. See RANKED_ARCHITECTURE.md in the YAB repo for the
Phase 0 architecture document.
-
proto— shared kotlinx.serialization DTOs used by the backend, the match-server agent mod, and the client mod. -
backend— Ktor service: Mojang session auth, matchmaking queue with MMR-band expansion, Elo rating engine (behind aRatingSysteminterface), match records, leaderboard. Phase 1 runs on in-memory stores; the Postgres schema lives inbackend/src/main/resources/schema.sql. -
agent— server-side Fabric mod (MC 26.2) for match servers. Inert without theYABRANKED_*environment; otherwise it configures the YAB game (lockout, first-win, time limit, card seed), gates joins to the two matched players, auto-assigns teams, starts the game, and reports the result to the backend. Handles no-shows (void) and mid-match disconnects (forfeit after 120 s). -
docker/— ephemeral match-server image: Fabric 26.2 + YAB + agent.docker/fetch-mods.shstages the jars, thendocker build -t yabranked-match docker/. -
Orchestration lives in
backend(dev.yabranked.backend.orchestrator): enable withYABRANKED_ORCHESTRATE=1; one Docker container per match (host networking, ports 25600+), torn down when the match settles or is reaped after a ready timeout. -
client— client-side Fabric mod (MC 26.2). "Ranked" button on the title screen → ranked screen with login (real MojangjoinServerhandshake), queue toggle with live state, auto-connect on match found, post-match MMR delta, and a leaderboard view. Backend URL defaults tolocalhost:8080; override with-Dyabranked.url=…orYABRANKED_URL. The backend enforces a minimum client version whenYABRANKED_MIN_CLIENT_VERSIONis set (HTTP 426 → "update required" in the UI). Dev launch:./gradlew :client:runClient.
./gradlew test # unit + API tests
./gradlew :backend:run --args="--fake-auth" # local backend on :8080, no Mojang verify
./gradlew :backend:runMock # mock client: 2 fake players queue + match--fake-auth (or YABRANKED_FAKE_AUTH=1) accepts any username without
Mojang session verification — local development only.
Persistence: set YABRANKED_DATABASE_URL=jdbc:postgresql://host:5432/yabranked
(plus YABRANKED_DATABASE_USER / YABRANKED_DATABASE_PASSWORD) to run on
Postgres — the schema is applied automatically at startup and the current
season persists across restarts. Without it the backend uses in-memory stores
and loses all state on restart (fine for dev, not for a beta).
- Start Docker (
sudo systemctl start docker). - Build the pieces:
(cd ../bingo && ./gradlew :api:publishToMavenLocal :mc26.2:build) ./gradlew :agent:build :backend:installDist docker/fetch-mods.sh docker build -t yabranked-match docker/ - Run the backend with orchestration, offline mode for local clients.
On Docker Desktop (VM-based;
systemctl --user start docker-desktop) also setYABRANKED_HOST_NETWORK=false— containers then publish ports and reach the backend via host.docker.internal:YABRANKED_FAKE_AUTH=1 YABRANKED_ORCHESTRATE=1 YABRANKED_ONLINE_MODE=false \ YABRANKED_HOST_NETWORK=false \ ./backend/build/install/backend/bin/backend
- Two options to queue:
- Client mod (preferred): run two game instances with the
clientmod (./gradlew :client:runClient), press "Ranked" on the title screen, log in, queue on both. On match found each client auto-connects. - Mock:
YABRANKED_MOCK_PLAYER_A/B=<usernames>withrunMock, then direct-connect both clients to thematch_foundaddress (fake auth uses the vanilla offline-UUID formula, so usernames must match exactly).
- Client mod (preferred): run two game instances with the
- The agent assigns red/blue, starts lockout (first to 13 items), reports the winner, and the container is removed.
POST /v1/auth/session{username, serverId, clientVersion}→{token, profile}(MojanghasJoinedverification; 426 belowYABRANKED_MIN_CLIENT_VERSION, 403 if banned)WS /v1/queue?token=…—join_queue/leave_queue; server pushesqueue_state,match_foundGET /v1/players/{uuid}→ profile (rating, tier, season, W/L/D)GET /v1/players/{uuid}/matches?season=&limit=→ match historyGET /v1/leaderboard?season=&limit=25GET /v1/seasons/currentPOST /v1/reports{matchId, reason}— player token; accused = opponent; one report per matchPOST /v1/internal/matches/ready/.../result— agent-only,Authorization: Bearer <per-match server token>- Admin (header
X-Admin-Token, enabled byYABRANKED_ADMIN_TOKEN):POST /v1/admin/seasons/advance·GET /v1/admin/reports·POST|DELETE /v1/admin/bans/{uuid}
Tiers: Coal/Iron/Gold/Emerald/Diamond (divisions I–III) and open-ended
Netherite from 1800; Unranked during the 5 placement matches. Seasons scope
ratings, placements, leaderboard, and history; start season via
YABRANKED_SEASON, advance via the admin endpoint.