Compare commits

...

3 Commits

Author SHA1 Message Date
semantic-release-bot
963d9ec618 chore(release): 2.10.2 [skip ci]
## [2.10.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.10.1...v2.10.2) (2025-10-09)

### Bug Fixes

* convert guestId to internal userId for player ownership check ([3a01f46](3a01f4637d))
2025-10-09 13:21:14 +00:00
Thomas Hallock
3a01f4637d fix: convert guestId to internal userId for player ownership check
The authorization check was failing because it was comparing two
different ID types:
- Player ownership map uses internal database userId (e.g., 'xlk...')
- Validation context was receiving guestId from cookie (e.g., 'ac9d...')

Solution:
- Call getUserIdFromGuestId() to convert guestId to internal userId
- Pass the internal userId to validator for room-based games
- Add logging to show which internal userId is being used
- Return error if user not found during conversion

This fixes the "You can only move your own players" error that was
incorrectly blocking legitimate moves from local players.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 08:20:18 -05:00
Thomas Hallock
97378b70b7 debug: add extensive logging to canFlipCard authorization check
Add detailed console logging to diagnose why tile clicks aren't working:
- Log all card flip attempts with game state details
- Show authorization check results (player found, isLocal value)
- Warn if current player not found in players map
- Log exact reason for each blocked attempt

This will help identify if the issue is:
- Game state (not active, processing, etc.)
- Card state (already flipped, matched, etc.)
- Authorization logic (player ownership check)
- Missing player data in the map

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 08:18:17 -05:00
4 changed files with 63 additions and 9 deletions

View File

@@ -1,3 +1,10 @@
## [2.10.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.10.1...v2.10.2) (2025-10-09)
### Bug Fixes
* convert guestId to internal userId for player ownership check ([3a01f46](https://github.com/antialias/soroban-abacus-flashcards/commit/3a01f4637d2081c66fe37c7f8cfee229442ec744))
## [2.10.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.10.0...v2.10.1) (2025-10-09)

View File

@@ -150,27 +150,62 @@ export function ArcadeMemoryPairsProvider({ children }: { children: ReactNode })
const canFlipCard = useCallback(
(cardId: string): boolean => {
if (!isGameActive || state.isProcessingMove) return false
console.log('[canFlipCard] Checking card:', {
cardId,
isGameActive,
isProcessingMove: state.isProcessingMove,
currentPlayer: state.currentPlayer,
hasRoomData: !!roomData,
flippedCardsCount: state.flippedCards.length,
})
if (!isGameActive || state.isProcessingMove) {
console.log('[canFlipCard] Blocked: game not active or processing')
return false
}
const card = state.gameCards.find((c) => c.id === cardId)
if (!card || card.matched) return false
if (!card || card.matched) {
console.log('[canFlipCard] Blocked: card not found or already matched')
return false
}
// Can't flip if already flipped
if (state.flippedCards.some((c) => c.id === cardId)) return false
if (state.flippedCards.some((c) => c.id === cardId)) {
console.log('[canFlipCard] Blocked: card already flipped')
return false
}
// Can't flip more than 2 cards
if (state.flippedCards.length >= 2) return false
if (state.flippedCards.length >= 2) {
console.log('[canFlipCard] Blocked: 2 cards already flipped')
return false
}
// Authorization check: Only allow flipping if it's your player's turn
if (roomData && state.currentPlayer) {
const currentPlayerData = players.get(state.currentPlayer)
// If current player is not local (isLocal === false), prevent flipping
console.log('[canFlipCard] Authorization check:', {
currentPlayerId: state.currentPlayer,
currentPlayerFound: !!currentPlayerData,
currentPlayerIsLocal: currentPlayerData?.isLocal,
})
// Block if current player is explicitly marked as remote (isLocal === false)
if (currentPlayerData && currentPlayerData.isLocal === false) {
console.log('[Client] Cannot flip - not your turn (current player is remote)')
console.log('[canFlipCard] BLOCKED: Current player is remote (not your turn)')
return false
}
// If player data not found in map, this might be an issue - allow for now but warn
if (!currentPlayerData) {
console.warn(
'[canFlipCard] WARNING: Current player not found in players map, allowing move'
)
}
}
console.log('[canFlipCard] ALLOWED: All checks passed')
return true
},
[

View File

@@ -161,8 +161,19 @@ export async function applyGameMove(userId: string, move: GameMove): Promise<Ses
// Fetch player ownership for authorization checks (room-based games)
let playerOwnership: Record<string, string> | undefined
let internalUserId: string | undefined
if (session.roomId) {
try {
// Convert guestId to internal userId for ownership comparison
internalUserId = await getUserIdFromGuestId(userId)
if (!internalUserId) {
console.error('[SessionManager] Failed to convert guestId to userId:', userId)
return {
success: false,
error: 'User not found',
}
}
const players = await db.query.players.findMany({
columns: {
id: true,
@@ -171,14 +182,15 @@ export async function applyGameMove(userId: string, move: GameMove): Promise<Ses
})
playerOwnership = Object.fromEntries(players.map((p) => [p.id, p.userId]))
console.log('[SessionManager] Player ownership map:', playerOwnership)
console.log('[SessionManager] Internal userId for authorization:', internalUserId)
} catch (error) {
console.error('[SessionManager] Failed to fetch player ownership:', error)
}
}
// Validate the move with authorization context
// Validate the move with authorization context (use internal userId, not guestId)
const validationResult = validator.validateMove(session.gameState, move, {
userId,
userId: internalUserId || userId, // Use internal userId for room-based games
playerOwnership,
})

View File

@@ -1,6 +1,6 @@
{
"name": "soroban-monorepo",
"version": "2.10.1",
"version": "2.10.2",
"private": true,
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
"workspaces": [