Commit Graph

12 Commits

Author SHA1 Message Date
Thomas Hallock 01740afcb7 fix: make useArcadeSocket work without ArcadeErrorProvider
CRITICAL PRODUCTION FIX - Resolves runtime error breaking all arcade games.

**Problem:**
- Commit 59901c55 added useArcadeError() call to useArcadeSocket
- Pages not wrapped with ArcadeErrorProvider yet
- Runtime error: "useArcadeError must be used within ArcadeErrorProvider"
- Next.js error boundary shows generic error page with no navigation or recovery

**Solution:**
- Export ArcadeErrorContext from ArcadeErrorContext.tsx
- Use useContext directly in useArcadeSocket instead of useArcadeError hook
- Fall back to no-op function if provider not available
- Allows games to work without error provider (just won't show error toasts)
- Pages can be wrapped with provider incrementally in future commits

**Impact:**
- Fixes production runtime error immediately
- Games work normally (without error toasts)
- Error provider can be added to pages when ready

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 08:41:27 -06:00
Thomas Hallock e8c52561a2 feat: add comprehensive error handling for arcade games
Add user-facing error notifications and boundaries to prevent silent failures in arcade games.

**Problem:**
- Errors only logged to console (e.g., "Failed to fetch session")
- Users saw nothing when errors occurred
- Buttons stopped working with no feedback
- No way to recover from errors

**Solution: 4-Part Error Handling System**

1. **ErrorToast Component** - User-facing error notifications
   - Prominent red toast in bottom-right corner
   - Auto-dismisses after 10 seconds
   - Collapsible technical details
   - Mobile-responsive

2. **ArcadeErrorBoundary** - React error boundary
   - Catches component render errors
   - Shows user-friendly fallback UI
   - Provides "Try Again" and "Return to Lobby" buttons
   - Collapsible stack trace for debugging

3. **ArcadeErrorContext** - Global error management
   - Manages error state across the app
   - Renders error toasts
   - Auto-cleans up old errors

4. **Enhanced useArcadeSocket** - Automatic socket error handling
   - Connection errors: "Failed to connect to server"
   - Disconnections: "Connection lost, attempting to reconnect"
   - Session errors: "Failed to load/update session"
   - Move rejections: "Your move was not accepted"
   - No active session: "No game session found"
   - Can suppress toasts with `suppressErrorToasts: true`

**Files Created:**
- src/components/ErrorToast.tsx
- src/components/ArcadeErrorBoundary.tsx
- src/contexts/ArcadeErrorContext.tsx
- .claude/ERROR_HANDLING.md (integration guide)

**Files Modified:**
- src/hooks/useArcadeSocket.ts (automatic error toasts)

**Next Steps (TODO):**
- Wrap arcade game pages with error providers
- Test all error scenarios
- Add error recovery strategies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 07:31:34 -06:00
Thomas Hallock b36df3a40c fix(worksheets): ten-frames not rendering in mastery mode
Fixed two critical bugs preventing ten-frames from rendering:

1. **Mastery mode not handled** (typstGenerator.ts:61)
   - Code only checked for 'smart' | 'manual' modes
   - Mastery mode fell into manual path, tried to use boolean flags that don't exist
   - Resulted in all display options being `undefined`
   - Fix: Check for both 'smart' OR 'mastery' modes (both use displayRules)

2. **Typst array membership syntax** (already fixed in previous commit)
   - Used `(i in array)` which doesn't work in Typst
   - Changed to `array.contains(i)`

Added comprehensive unit tests (tenFrames.test.ts):
- Problem analysis tests (regrouping detection)
- Display rule evaluation tests
- Full Typst template generation tests
- Mastery mode specific tests
- All 14 tests now passing

Added debug logging to trace display rules resolution:
- displayRules.ts: Shows rule evaluation per problem
- typstGenerator.ts: Shows enriched problems and Typst data
- Helps diagnose future issues

The issue was that mastery mode (which uses displayRules like smart mode)
was being treated as manual mode (which uses boolean flags), resulting in
undefined display options.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:06:27 -06:00
Thomas Hallock cd75df7221 chore: restore stashed work from previous session
Recover all changes from stash including:
- Linter/formatter updates across codebase
- Settings permission updates for git checkout

This commit captures the complete state of work that was
stashed during the previous session's git operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 14:59:40 -06:00
Thomas Hallock 53bbae84af fix(complement-race): track physical car indices to prevent boarding issues
Fixed critical bug where passengers were missing boarding opportunities
due to array index confusion after deliveries.

Problem: After a passenger was delivered, the currentBoardedPassengers
array would compact but physical car positions wouldn't change. The
occupiedCars map was using array indices instead of physical car numbers,
causing cars to incorrectly appear occupied.

Example bug scenario:
- Train has 4 cars (indices 0-3)
- Kate boards Car 0, Frank Car 1, Mia Car 2, Charlie Car 3
- Kate delivers from Car 0
- Array compacts: [Frank, Mia, Charlie] at indices [0, 1, 2]
- occupiedCars map now shows cars 0,1,2 occupied (WRONG!)
- Physical Car 0 is actually EMPTY, but Alice can't board

Solution: Added carIndex field to Passenger type to track physical car
number (0-N) independently from array position.

Changes:
- Added carIndex: number | null to Passenger type
- Updated Validator to store physical carIndex when boarding
- Updated Provider to pass carIndex in BOARD_PASSENGER moves
- Updated useSteamJourney to use passenger.carIndex for all car
  position calculations and occupancy tracking
- Reordered frame processing so DELIVERY moves dispatch before BOARDING
  moves to prevent race conditions
- Fixed configuration mismatch: client now uses server's authoritative
  maxConcurrentPassengers instead of calculating locally
- Updated visual display to use server's maxConcurrentPassengers

Also includes improved logging for debugging train configuration and
passenger movement.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-17 17:36:06 -05:00
Thomas Hallock f9af0f169e chore: biome formatting fixes 2025-10-11 07:17:38 -05:00
Thomas Hallock bda5bc6c0e fix: prevent database imports from being bundled into client code
**Problem:**
- player-ownership.ts imported drizzle-orm and @/db at top level
- When RoomMemoryPairsProvider imported client-safe utilities, Webpack bundled ALL imports including database code
- This caused hydration error: "The 'original' argument must be of type Function"
- Node.js util.promisify was being called in browser context

**Solution:**
1. Created player-ownership.client.ts with ONLY client-safe utilities
   - No database imports
   - Safe to import from 'use client' components
   - Contains: buildPlayerOwnershipFromRoomData(), buildPlayerMetadata(), helper functions

2. Updated player-ownership.ts to re-export client utilities and add server-only functions
   - Re-exports everything from .client.ts
   - Adds buildPlayerOwnershipMap() (async, database-backed)
   - Safe to import from server components/API routes

3. Updated RoomMemoryPairsProvider to import from .client.ts

**Result:**
- No more hydration errors on /arcade/room
- Client bundle doesn't include database code
- Server code can still use both client and server utilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 11:40:46 -05:00
Thomas Hallock 2856f4b83f fix: implement shared session architecture for room-based multiplayer
Fixes state divergence issue where different room members saw different
game states (e.g., version 11 vs version 2).

## Problem
- Each user created their own session (one row per userId in arcadeSessions)
- When users made moves, they updated different database rows
- Broadcasting tried to sync but versions diverged
- Result: Complete state inconsistency between room members

## Solution
Implement shared session architecture where all room members access the
same session:

### Backend Changes (session-manager.ts)
- Add getArcadeSessionByRoom(): Look up session by roomId
- Modify createArcadeSession(): Check for existing room session first
- Modify applyGameMove(): Accept optional roomId parameter for room-based lookup

### Server Changes (socket-server.ts)
- Update game-move handler to accept roomId in payload
- Pass roomId to applyGameMove() for shared session access
- Update join-arcade-session to use room-based lookup when roomId provided

### Client Changes
- Update useArcadeSocket.sendMove() to accept and send roomId
- Update useArcadeSession.sendMove() to pass roomId to socket
- Fix sendMove interface type (playerId must be included, not omitted)

## Result
All room members now read/write to single shared session with consistent
version numbers. State stays synchronized across all clients.

## Note
Codebase has pre-existing TypeScript errors in unrelated files (abacus-react
imports, tutorial components) that are not addressed by this fix.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 08:40:13 -05:00
Thomas Hallock 8175c43533 feat: implement room-wide multi-user game state synchronization
Enable real-time game state sync across all users in a room playing
at /arcade/room. Previously only synced across one user's tabs, now
syncs across all room members.

Server changes (socket-server.ts):
- Accept optional roomId in join-arcade-session event
- Join socket to both arcade:userId and game:roomId rooms
- Broadcast move-accepted to both rooms when processing moves
- Log game room joins for debugging

Client changes:
- useArcadeSocket: Accept roomId parameter in joinSession()
- useArcadeSession: Accept roomId in options, pass to joinSession()
- ArcadeMemoryPairsContext: Get roomId from useRoomData() and wire up

How it works:
- User A joins game room → joins arcade:userA + game:room123
- User B joins game room → joins arcade:userB + game:room123
- User A makes move → broadcasts to both rooms
- User A's tabs receive on arcade:userA (reconcile optimistic update)
- User B's tabs receive on game:room123 (sync with server state)
- Optimistic update system handles both cases automatically

Architecture:
- Reuses existing optimistic update reconciliation
- Minimal changes (5 edits across 4 files)
- Backward compatible (roomId is optional)
- Solo play unaffected

See docs/MULTIPLAYER_SYNC_ARCHITECTURE.md for full details.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 11:16:27 -05:00
Thomas Hallock 60d70cd2f2 style: apply Biome formatting to entire codebase
Run Biome formatter on all files to ensure consistent code style:
- Single quotes for JS/TS
- Double quotes for JSX
- 2-space indentation
- 100 character line width
- Semicolons as needed
- ES5 trailing commas

This is the result of running: npx @biomejs/biome format . --write

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:48:26 -05:00
Thomas Hallock 2248c34215 chore: improve logging in arcade session management
Added detailed logging to help debug player ID vs user ID flow:
- useArcadeSocket: Log full move payload with JSON stringification
- session-manager: Log player ID, game state players, and phase info

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-06 13:27:19 -05:00
Thomas Hallock 22541df99f fix: use UUID player IDs in session creation fallback
- Changed CreateSessionOptions.activePlayers from number[] to string[]
- Updated socket-server.ts fallback from [1] to [data.userId]
- Added debug logging to validateFlipCard to diagnose turn validation issues

This ensures that when a session is created without explicit activePlayers,
it uses the actual UUID of the requesting player instead of the numeric value 1.
2025-10-06 13:04:33 -05:00