P2P Sync (Yjs)
WEFA uses Yjs CRDTs for realtime multiplayer sync. Documents are small and scoped to game sessions.
Transport Modes
| Mode | Provider | Use Case |
|---|---|---|
webrtc | y-webrtc | Local network play (peer discovery) |
websocket | WebSocket relay | Global play via relay server |
hybrid | Both | World mode: WebRTC with WebSocket fallback |
Transport resolution:
// From src/modules/yjs.ts
function resolveYjsTransport(mode: GameMode): TransportConfig {
if (mode === 'device') return null; // no sync needed
if (mode === 'local') return 'webrtc';
if (mode === 'world') return 'hybrid'; // graceful degradation
}
Yjs Document Structure
Each game session creates a Y.Doc with:
Y.Doc
├── Y.Map('game')
│ ├── type: string (game type)
│ ├── turn: number
│ ├── status: string
│ └── board: Y.Map (game-specific state)
├── Y.Array('players')
│ └── [{ id, name, creature, seat }]
├── Y.Array('moves')
│ └── [{ player, move, timestamp }]
└── Y.Map('setup')
├── p0: { playerId, creatureId, ready }
├── p1: { playerId, creatureId, ready }
├── gameType: string
└── startSignal: boolean
Setup State Sync
Before a match starts, the setup state syncs:
- Seat claim: Host claims
p0, guest claimsp1 - Game type sync: Host sets game type, guest reads it
- Creature selection: Each player sets their own seat's creature
- Start signal: Host signals when both players are ready
Seat takeover protection prevents a second peer from claiming an occupied seat.
Move Validation
All incoming remote moves are validated through the rules engine before being accepted into local state. Invalid moves are rejected.
Persistence
y-indexeddb provides offline persistence for Yjs documents, allowing interrupted sessions to resume.
Signaling
The Fly.io API server (packages/api) includes a WebSocket signaling relay for y-webrtc peer discovery. The endpoint is configurable via VITE_SIGNALING_URLS.
Key Files
src/modules/yjs.ts- Session creation, transport resolution, setup syncsrc/modules/yjs.test.ts- Transport and sync testssrc/hooks/yjs/useYjsSession.ts- React hook for session managementsrc/config.ts-config.yjstransport configuration