- Split chess and blackjack setup into guided creation steps
- Add chess time control presets and reusable lobby room metrics
- Improve room filtering, ordering, and live connection state
- Replace in-memory auth sessions with signed cookies and signed OAuth state
- Add auth route coverage and update panel/web server wiring
- Switch test script to per-file Bun processes and clean up type checks
- Remove max-w-4xl constraint to allow single-player tables to expand
- Add responsive container sizing: 95vw on mobile, up to 6xl on large screens
- Add horizontal scrolling for split hands with overflow-x-auto
- Increase spacing between split hands on larger screens (gap-1.5 -> sm:gap-2)
- Add padding to container for better spacing on small screens
- Fix incorrect PnL calculations where multipliers were treated as amounts
- Add proper net profit calculation: (multiplier × bet) - bet
- Update UI with gradient backgrounds, icons, and improved animations
- Add slide-in and pulse-slow keyframe animations
- Enhance dealer area with status-based styling
- Improve action buttons with colored gradients and hover effects
- Revise round result banner with better visual hierarchy
- GameServer.ts: Calculate netProfit = grossPayout - betAmount and send as 'net'
instead of sending gross payout labeled as net
- BlackjackGame.tsx: Fix PnL calculations to use net profit correctly
- Hand win/blackjack now shows net profit (payout minus bet)
- Lose correctly shows negative bet amount
- Round result banner displays roundNet (net profit) with appropriate colors
- Remove dead code (myPnl variable that was calculated but never used)
- Update color coding: green for profit, red for loss, blue for zero balance
- Added cumulativePnl field to PlayerSeat and PlayerSeatView types
- Added myCumulativePnl to PlayerView for UI display
- Track net profit/loss across rounds in the game state
- Update round result banner to show both round net and total balance
- Add player seat PnL indicator with color coding (green/red)
- Preserve cumulativePnl when players stay seated through rounds
- Initialize new players with cumulativePnl = 0
- Added comprehensive tests for cumulative PnL tracking
Missing TYPE_CONFIG entries caused both transaction types to fall back to
sign: "+", making bet deductions appear as credits in the admin panel.
The actual economy values were correct (negative for bets).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Transform blackjack from single-round to continuous-play table sessions with
round lifecycle (betting → playing → resolved → betting), split/double down
actions, per-hand bet tracking, leave/join table mid-session, and a responsive
felt-style table UI with arc-positioned player seats.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a full blackjack game with dealer AI, hit/stand/double-down actions,
and per-player payout multipliers (house-edge model). Extends the game
framework with manualStart support and a START_GAME WebSocket message so
hosts can begin when ready. Generalizes bet settlement transaction
descriptions from chess-specific to game-agnostic.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Solo mode is now a toggle in the chess room creation modal, available
to all users instead of admin-only. Betting lets players wager AU on
games with preset amounts, async deduction on game start, and automatic
payout/refund on game end.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Players subscribe to the room pub/sub channel and also receive direct
GAME_STATE messages. The GAME_STARTED and GAME_UPDATE broadcasts carry
the spectator view (no myColor/legalMoves), and were blindly overwriting
gameState — making isPlayerView() return false and disabling all
interaction. Now these broadcast handlers only update gameState for
spectators; players rely exclusively on the direct GAME_STATE message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace external SVG files loaded via <img> tags with inline React SVG
components. The <img> approach was a DOM-level problem — replaced elements
sit outside React's tree and interfere with dnd-kit's pointer event
pipeline. Inline SVGs are native JSX nodes that participate correctly
in event bubbling, matching how react-chessboard's default pieces work.
Removes panel/public/pieces/ (12 SVG files) in favor of a single
pieces.tsx module with the same cburnett artwork as JSX.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs fixed:
- Piece <img> elements were intercepting pointer events, preventing
dnd-kit drag handlers and square click handlers from firing. Added
pointerEvents: "none" so events pass through to the board framework.
- Solo test mode (fillRoom with duplicate player IDs) always resolved
to "white" in colorOfPlayer, making black moves impossible. Now
detects duplicate IDs and returns the current turn's color instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add chess as the first game plugin using the existing multiplayer framework.
Server-side game logic uses chess.js with server-authoritative clock management.
Client uses react-chessboard v5 with cburnett piece set, drag-and-drop + click-to-move,
configurable time controls (bullet/blitz/rapid/classical/none), draw offers,
resignation, and timeout detection. Extends the game framework with room creation
options to support per-game configuration. Includes 57 tests covering all code paths.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
formatAmount now strips the sign since the renderer already prepends
+/- based on TYPE_CONFIG. Raw DB values like "-180" were getting a
second "-" prefix, showing as "--180".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TYPE_CONFIG only had 6 of 14 transaction types. Missing types fell back
to sign: null which rendered as negative. Added all TransactionType enum
values and derived the filter dropdown from TYPE_CONFIG keys.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Classes: full CRUD with list + detail panel
- Quests: CRUD with search, trigger events and reward fields
- Lootdrops: stat cards, spawn form, filter tabs, cancel action
- Moderation: case list with filters, detail panel, create + resolve
- Transactions: color-coded amounts, type/user filters, pagination
- Rename player dashboard currency label from Gold to AU (Astral Units)
- Remove unused placeholders map from App.tsx
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the placeholder with a real leaderboard view showing top 10
players by level, wealth, and net worth. Reuses the existing /api/stats
endpoint which players already have access to.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend expected { items } but API returns { inventory } with nested
item objects. Fixes response key, aligns InventoryEntry type to actual
API shape, and separates error handling so a failed inventory fetch
shows an error instead of silently displaying "No items yet".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces NavGroup structure with labeled sections in the sidebar.
Admins see "Administration" and "Player" groups; players see a flat
list unchanged. Extracts SidebarNavItem, SidebarNavSection, and
SidebarUserProfile components from the monolithic sidebarContent blob.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Guild draft was initialized with defaults before the API response
arrived, then never updated because the !guildDraft guard prevented
overwriting. Gate initialization on !loading so saved values are used.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate room leave/delete event handling into RoomManager emitter,
remove redundant PLAYER_LEFT publishes from GameServer, and delete the
chess game plugin (board, types, tests) in favor of the new plugin
architecture. Add per-module CLAUDE.md files for leveling, guild-settings,
feature-flags, db, api, and panel to improve agent navigability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
react-chessboard v5 moved all props into an `options` object and
renamed several callbacks/style props. The v4-style props were silently
ignored, causing pieces to snap back, no legal-move highlights, and no
WS events on drop. Also adds a custom promotion dialog since v5 removed
the built-in one.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add localFen/localFenRef in ChessBoard for optimistic piece placement,
preventing snap-back while awaiting server confirmation
- Sync localFen from server state on each chess.fen update
- Guard GAME_STATE handler in useGameRoom from overwriting a finished
roomStatus, fixing the race where GAME_ENDED (pub/sub) arrives before
GAME_STATE (direct ws.send)
- Reset confirmForfeit immediately on forfeit dispatch for instant UI feedback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Fix session never being attached to ws.data at upgrade time
- Add GameServer class: connection registry, per-connection room tracking,
automatic room cleanup on disconnect via ws.data.rooms
- Replace ws-handler.ts with typed event-driven architecture using mitt
- Remove redundant subscription tracking from RoomManager
- Add JOIN_RESULT with player/spectator lists replacing error-as-control-flow
- Add SESSION_REPLACED for multi-tab same-account detection
- Add FILL_ROOM command for admin solo testing (fills empty slots with host)
- Fix dual-schema routing; remove game types from WsMessageSchema
- Per-player personalized views sent directly after each action
Chess plugin:
- Allow same-player (solo) mode: skip color/turn ownership checks
- Fix forfeit and disconnect handling in solo mode (winner: null)
Frontend:
- Click-to-move with legal move dots and last-move highlight
- Auto-scroll move history, forfeit confirmation, turn-reactive board border
- JOIN_RESULT initialises player/spectator lists immediately on join
- Contextual connecting state, player slot cards in waiting room
- Copy-invite button with Copied! flash, Back to Lobby CTA on finish
- Session-replaced warning banner with Rejoin here action
- Lobby passes preferAs intent through route state
- Admin waiting room shows Start Solo Test button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Prevent same player from joining as both white and black
- Add validation to reject duplicate players in RoomManager
- Fix spectator status not resetting when joining as player
- Use ref to track latest chess state in ChessBoard for accurate move validation
- Add role field to JOIN_ROOM message schema
- Allow admin users to join rooms and be added as players
- Update panel to pass user role when joining game rooms
- Fix chess move coordinates in tests (algebraic notation)
- Ensure admin users can make moves for both sides
Bun's ws.publish() excludes the sender, so the player making a move never
received the GAME_UPDATE with the new FEN — causing pieces to snap back.
Added ctx.send() alongside ctx.publish() for GAME_UPDATE and GAME_ENDED.
Also redesigned the panel for mobile: hamburger drawer sidebar, responsive
chess board sizing via ResizeObserver, stacked layouts on small screens,
and touch-friendly modals/controls across lobby and game pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap chess.js move validation in try-catch for invalid moves. Fix admin
self-play by detecting when both players share the same ID and allowing
either color's pieces to be dragged on the current turn.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Swap the custom move validation and Unicode piece grid for chess.js
(full rules engine with check/checkmate/castling/en passant/promotion)
and react-chessboard (drag-and-drop SVG board). Board styled to match
the purple dark theme and auto-orients to the player's color.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Disconnecting and reconnecting the WebSocket on every route change caused
a race condition: the old socket's async onclose handler would null out
globalWs after the new socket was created, causing JOIN_ROOM messages to
be silently dropped. The WebSocket is a global singleton — keep it alive
and let the built-in reconnection logic handle actual disconnects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace useState-based page switching with react-router-dom Routes.
Layout now renders admin or player nav items based on user.role.
Add stub pages for PlayerDashboard, GameLobby, and GameRoom.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split ItemStudio (1863->388), Settings (1445->355), and Users
(1062->164) into focused sub-components under pages/components/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>