docs: add plan for centralizing player ownership logic
Created comprehensive plan to consolidate scattered player ownership checking logic into a single, well-tested module accessible from both server-side and client-side code. Current problem: - Player ownership logic duplicated in 4+ places - Different implementations lead to bugs (e.g., recent userId bug) - Hard to maintain and test Proposed solution: - Create src/lib/arcade/player-ownership.ts with utilities - Server-side: buildPlayerOwnershipMap(roomId) - async DB queries - Client-side: buildPlayerOwnershipFromRoomData(roomData) - sync - Shared helpers: isPlayerOwnedByUser(), getPlayerOwner(), etc. Implementation plan: 7 commits, one per phase 1. Create utilities module with tests 2. Update session-manager.ts 3. Update player-manager.ts 4. Update RoomMemoryPairsProvider.tsx 5. Update UI components 6. Update API endpoints (if needed) 7. Add integration tests Benefits: - Single source of truth - Consistent behavior across codebase - Better testability - Type-safe shared types - Easier maintenance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
176
apps/web/.claude/PLAYER_OWNERSHIP_CENTRALIZATION_PLAN.md
Normal file
176
apps/web/.claude/PLAYER_OWNERSHIP_CENTRALIZATION_PLAN.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Player Ownership Centralization Plan
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Player ownership logic is currently scattered across multiple locations in the codebase, leading to bugs and inconsistencies. The same pattern appears in:
|
||||
|
||||
1. **Server-side** (`session-manager.ts:232-239`): Builds `playerOwnership` map from database
|
||||
2. **Client-side** (`RoomMemoryPairsProvider.tsx:370-403`): Builds `playerOwnership` from `roomData.memberPlayers`
|
||||
3. **UI Components** (`PlayerStatusBar.tsx:31`, `MemoryGrid.tsx:388`): Check `player.userId === viewerId`
|
||||
4. **Validation** (`MatchingGameValidator.ts:88-102`): Validates player ownership
|
||||
|
||||
## Current Implementations
|
||||
|
||||
### Server-Side (session-manager.ts)
|
||||
```typescript
|
||||
// Lines 232-238
|
||||
const players = await db.query.players.findMany({
|
||||
columns: { id: true, userId: true }
|
||||
})
|
||||
playerOwnership = Object.fromEntries(players.map(p => [p.id, p.userId]))
|
||||
```
|
||||
|
||||
### Client-Side (RoomMemoryPairsProvider.tsx)
|
||||
```typescript
|
||||
// Lines 370-403
|
||||
const buildPlayerMetadata = useCallback((playerIds: string[]) => {
|
||||
const playerOwnership = new Map<string, string>()
|
||||
if (roomData?.memberPlayers) {
|
||||
for (const [userId, userPlayers] of Object.entries(roomData.memberPlayers)) {
|
||||
for (const player of userPlayers) {
|
||||
playerOwnership.set(player.id, userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
// ... use playerOwnership to assign correct userId
|
||||
}, [players, roomData, viewerId])
|
||||
```
|
||||
|
||||
## Centralization Strategy
|
||||
|
||||
### Phase 1: Create Shared Utilities Module
|
||||
**File**: `src/lib/arcade/player-ownership.ts`
|
||||
|
||||
Create a module with:
|
||||
1. **Server-side utility**: `buildPlayerOwnershipMap(roomId?: string)`
|
||||
- Fetches from database
|
||||
- Returns `Record<playerId, userId>`
|
||||
- Used by `session-manager.ts`
|
||||
|
||||
2. **Client-side utility**: `buildPlayerOwnershipFromRoomData(roomData)`
|
||||
- Builds from `RoomData.memberPlayers`
|
||||
- Returns `Map<playerId, userId>` or `Record<playerId, userId>`
|
||||
- Used by React components
|
||||
|
||||
3. **Shared types**: `PlayerOwnershipMap = Record<string, string>`
|
||||
|
||||
4. **Helper functions**:
|
||||
- `isPlayerOwnedByUser(playerId, userId, ownershipMap)`
|
||||
- `getPlayerOwner(playerId, ownershipMap)`
|
||||
- `buildPlayerMetadata(playerIds, ownershipMap, playersMap)` - combines ownership + player data
|
||||
|
||||
### Phase 2: Update Server-Side Code
|
||||
**Files**:
|
||||
- `src/lib/arcade/session-manager.ts`
|
||||
- `src/lib/arcade/player-manager.ts`
|
||||
|
||||
Changes:
|
||||
1. Import utilities from `player-ownership.ts`
|
||||
2. Replace inline ownership building with `buildPlayerOwnershipMap()`
|
||||
3. Add new function to `player-manager.ts`: `getPlayerOwnershipMap(roomId)`
|
||||
|
||||
### Phase 3: Update Client-Side Providers
|
||||
**Files**:
|
||||
- `src/app/arcade/matching/context/RoomMemoryPairsProvider.tsx`
|
||||
- Any other game providers that need this logic
|
||||
|
||||
Changes:
|
||||
1. Import utilities from `player-ownership.ts`
|
||||
2. Replace `buildPlayerMetadata` with centralized version
|
||||
3. Use shared helper functions for ownership checks
|
||||
|
||||
### Phase 4: Update UI Components
|
||||
**Files**:
|
||||
- `src/app/arcade/matching/components/PlayerStatusBar.tsx`
|
||||
- `src/app/arcade/matching/components/MemoryGrid.tsx`
|
||||
- `src/components/PageWithNav.tsx`
|
||||
- `src/contexts/GameModeContext.tsx`
|
||||
|
||||
Changes:
|
||||
1. Use centralized `isPlayerOwnedByUser()` helper
|
||||
2. Consistent API across all components
|
||||
|
||||
### Phase 5: Add to API Endpoints (if needed)
|
||||
**Files**:
|
||||
- `src/app/api/arcade/rooms/[roomId]/route.ts`
|
||||
- Any endpoints that return player data
|
||||
|
||||
Changes:
|
||||
1. Include `playerOwnership` map in API responses where practical
|
||||
2. Document in API response types
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Single Source of Truth**: All ownership logic in one place
|
||||
2. **Consistency**: Same algorithm server-side and client-side
|
||||
3. **Testability**: Can unit test ownership logic in isolation
|
||||
4. **Type Safety**: Shared types across client/server boundary
|
||||
5. **Maintainability**: Bug fixes only need to be made once
|
||||
6. **Documentation**: Central location for ownership algorithm docs
|
||||
|
||||
## Implementation Order (One Commit Per Phase)
|
||||
|
||||
1. ✅ **Commit 1**: Create `src/lib/arcade/player-ownership.ts` with all utilities and tests
|
||||
2. ✅ **Commit 2**: Update `session-manager.ts` to use new utilities
|
||||
3. ✅ **Commit 3**: Update `player-manager.ts` to export ownership helper
|
||||
4. ✅ **Commit 4**: Update `RoomMemoryPairsProvider.tsx` to use utilities
|
||||
5. ✅ **Commit 5**: Update UI components to use helper functions
|
||||
6. ✅ **Commit 6**: Update API endpoints to include ownership data (if needed)
|
||||
7. ✅ **Commit 7**: Add comprehensive integration tests
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### Server vs Client Implementations
|
||||
|
||||
**Why separate implementations?**
|
||||
- Server uses database queries (async)
|
||||
- Client uses in-memory `RoomData` (sync)
|
||||
- Different data sources, same logic
|
||||
|
||||
**Shared interface:**
|
||||
```typescript
|
||||
type PlayerOwnershipMap = Record<string, string> // playerId -> userId
|
||||
|
||||
// Server-side (async)
|
||||
async function buildPlayerOwnershipMap(roomId: string): Promise<PlayerOwnershipMap>
|
||||
|
||||
// Client-side (sync)
|
||||
function buildPlayerOwnershipFromRoomData(
|
||||
roomData: RoomData
|
||||
): PlayerOwnershipMap
|
||||
```
|
||||
|
||||
### Type Consistency
|
||||
|
||||
Both return the same structure:
|
||||
```typescript
|
||||
{
|
||||
"player-id-1": "user-id-1",
|
||||
"player-id-2": "user-id-1",
|
||||
"player-id-3": "user-id-2"
|
||||
}
|
||||
```
|
||||
|
||||
This allows validators, helpers, and checks to work identically regardless of source.
|
||||
|
||||
## Migration Path
|
||||
|
||||
1. Create new module alongside existing code
|
||||
2. Add tests for new utilities
|
||||
3. Gradually migrate files one at a time
|
||||
4. Remove old implementations after migration complete
|
||||
5. Deprecate old patterns in documentation
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
1. **Unit tests** for utilities in isolation
|
||||
2. **Integration tests** for server-side flow
|
||||
3. **Component tests** for client-side usage
|
||||
4. **E2E tests** for full multiplayer scenarios
|
||||
|
||||
## Documentation
|
||||
|
||||
Add to existing docs:
|
||||
- Update `ARCADE_ARCHITECTURE.md` with new utilities section
|
||||
- Update `MULTIPLAYER_SYNC_ARCHITECTURE.md` with ownership flow
|
||||
- Add JSDoc comments to all exported functions
|
||||
Reference in New Issue
Block a user