feat(queue): captain mode#639
Draft
garrappachc wants to merge 18 commits into
Draft
Conversation
Contributor
ebb8355 to
a9e39b8
Compare
Add captain mode as an alternative to auto queue mode. Captains are selected from opted-in eligible players, then pick teams sequentially using ABBA order with map banning before game launch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add full client-facing UI for captain mode: class columns with join/leave buttons, wants-captain toggle, draft board with team columns/pick order tracker/player pool, map ban panel, and real-time WebSocket sync. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add CSS for class columns, player cards, wants-captain toggle, draft board (team columns, pick-order dots, player pool, pick buttons), map ban panel, and selected map display. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Playwright e2e tests covering: join/leave class columns, multi-class offers, wants-captain toggle + crown icon, and full draft flow (ABBA picks, map bans, game launch). Includes a new /admin/queue settings page for switching queue mode and configuring captain min-games and pick timeout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `as 'safe'` casts and `!!` boolean coercion to suppress false-positive XSS scan errors for enum-derived strings and async component array rendering. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- emit queue/players:updated from setWantsCaptain so crown icon re-renders on player cards after toggling wants-captain - always render #draft-board in DOM so HTMX can OOB swap into it when state transitions from ready to draft - clear #captain-queue when broadcasting draft board on state change Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unchecked HTML checkboxes are excluded from form serialization, so untoggling the captain wish never sent the wantsCaptain value via ws-send. Switch to hx-vals so the value is always included. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
a9e39b8 to
1dc80eb
Compare
The ready-up dialog was never shown to captain mode players because queue-auto/sync-clients only finds players in queueSlots (empty in captain mode). Add a queue/state:updated handler for QueueState.ready in captain sync-clients that shows the dialog to unready queuePlayers. Also close the dialog after a successful ready-up in the captain gateway listener. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
queueState.last is only set by queue-auto/join, so in captain mode it is always undefined. Previously setState(QueueState.ready) would throw before emitting queue/state:updated, silently preventing the ready-up transition from completing. Make the pre-ready block conditional on last being present. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ready-up dialog (modal) is shown as soon as all 12 slots fill. Having the first two captain candidates join and opt in before the remaining 10 join ensures the toggle is checked while state is still 'waiting' and the modal hasn't appeared yet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The remainingSlots loop was checking `picked < total` for every slot in allSlots, pushing the full total count instead of total-picked. This made the pool appear too small and rejected valid picks. Also replaced the captain-slot subtraction heuristic (which assumed captains fill the last 2 classes) with wildcard captain entries. In create.ts captains fill the first unfilled class regardless of their offeredClasses, so they are effectively class-agnostic in the bipartite matching. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After all picks are made, reset currentTurn to blu so the map-ban UI shows the correct captain. Also persist currentTurn after each ban so the next banning captain is reflected in the draft board. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After the last ban, the view transitions to the selected-map panel so .map-ban-card.banned disappears. The selectedMap assertion that follows the loop is sufficient to confirm the ban was registered. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the server was already in captain mode from a previous test run, the fixture skipped setQueueMode and left captainMinGames at its persisted value, making test players ineligible. Now always call setQueueMode so captainMinGames=0 is enforced regardless of current queue mode. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After a captain-mode game is created the draft board was never cleared from connected clients' pages. Three changes: - Add auto-reset plugin: listens for game:created and calls reset(), which deletes captainDraft and queuePlayers (mirroring queue-auto's auto-reset for the captain-specific collections). - Add CaptainQueueSection component: renders the captain queue form so it can be sent over WebSocket when restoring the queue view. - Handle QueueState.waiting in sync-clients: clears <div id="draft-board"> and restores the captain queue form for all clients on the queue page. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The feasibility check in pick() was global (across both teams), so a captain could pick more than cls.count players of a class for their team as long as the global total was still valid. This produced invalid lineups (e.g. 3 soldiers on one team, 1 on the other). - Server: reject a pick if the picking team already has cls.count players for that class. - UI: hide pick buttons for classes the picking team has already filled, so captains can only click valid options. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an e2e test verifying that when the BLU captain fails to pick within the timeout, the queue reverts to the waiting state and the draft board is cleared. Also extends AdminPage.setQueueMode to accept captainPickTimeout, and makes the captain-mode fixture always reset the timeout to 60 s so tests don't interfere with each other. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/queue-captain/module with full captain mode queue logic: join/leave/ready-up/pick/ban-map/kick/reset operationswaiting → ready → draft → launching; captains selected from opted-in eligible players; ABBA pick order; 3-map pool with one ban per captain before game launchsrc/queue/plugins/handle-mode-change.tsthat atomically clears both queue collections on mode switch and emitsqueue/mode:changedisActiveflag — zero overhead when running in auto modequeue.captain_min_games,queue.captain_pick_timeout; newQueueState.draft; new DB collectionsqueue.playersandqueue.draftgames/create.tsextended with overload for captain draft → game slot conversionTest plan
pnpm build)pnpm lint)canFormTeamsbipartite matching: verify queue enters ready-up only when team can actually be filled from offered classes🤖 Generated with Claude Code