import { asRecord, normalizeTiles, normalizeWsType, readBoolean, readString, } from '../../../../game/chengdu/messageNormalizers' import type { RoomPlayerUpdatePayload, RoomTrusteePayload } from '../../../../game/actions' import { clearDingQuePending, clearReadyTogglePending, clearStartGamePending, clearTurnPending, completeDiscard, setTrustMode, syncCurrentUserId, } from '../session/sessionStateAdapter' import { setPlayerHandState, setPlayerMissingSuit, setPlayerReadyState, setPlayerTrusteeState } from '../store/gameStoreAdapter' import type { PlayerHandlerApi, SocketHandlerContext } from '../types' export function createPlayerHandlers(context: SocketHandlerContext): PlayerHandlerApi { function applyPlayerReadyState(playerId: string, ready: boolean): void { setPlayerReadyState(context.gameStore, playerId, ready) } function syncReadyStatesFromRoomUpdate(payload: RoomPlayerUpdatePayload): void { if (!Array.isArray(payload.players)) { return } for (const item of payload.players) { const playerId = (typeof item.PlayerID === 'string' && item.PlayerID) || (typeof item.player_id === 'string' && item.player_id) || '' const ready = typeof item.Ready === 'boolean' ? item.Ready : typeof item.ready === 'boolean' ? item.ready : typeof item.is_ready === 'boolean' ? item.is_ready : undefined if (!playerId || typeof ready !== 'boolean') { continue } applyPlayerReadyState(playerId, ready) } } function handlePlayerHandResponse(message: unknown): void { const source = asRecord(message) if (!source || typeof source.type !== 'string' || normalizeWsType(source.type) !== 'PLAYER_HAND') { return } const payload = asRecord(source.payload) if (!payload) { return } syncCurrentUserId(context.session, readString(source, 'target')) const roomId = readString(payload, 'room_id', 'roomId') || readString(source, 'roomId') if (roomId && context.gameStore.roomId && roomId !== context.gameStore.roomId) { return } const handTiles = normalizeTiles(payload.hand) if (!context.session.loggedInUserId.value || handTiles.length === 0) { return } clearTurnPending(context.session) setPlayerHandState(context.gameStore, context.session.loggedInUserId.value, handTiles) clearDingQuePending(context.session) completeDiscard(context.session) if (context.gameStore.phase !== 'waiting') { clearStartGamePending(context.session) } } function syncTrusteeState(payload: RoomTrusteePayload): void { const playerId = (typeof payload.player_id === 'string' && payload.player_id) || (typeof payload.playerId === 'string' && payload.playerId) || '' if (!playerId) { return } const trustee = typeof payload.trustee === 'boolean' ? payload.trustee : true setPlayerTrusteeState(context.gameStore, playerId, trustee) if (playerId === context.session.loggedInUserId.value) { setTrustMode(context.session, trustee) } } function handleReadyStateResponse(message: unknown): void { const source = asRecord(message) if (!source || typeof source.type !== 'string' || normalizeWsType(source.type) !== 'PLAYER_READY') { return } const payload = asRecord(source.payload) if (!payload) { return } const roomId = typeof payload.room_id === 'string' ? payload.room_id : readString(source, 'roomId') const userId = (typeof payload.player_id === 'string' && payload.player_id) || (typeof payload.user_id === 'string' && payload.user_id) || readString(source, 'target') const ready = readBoolean(payload, 'is_ready', 'ready', 'Ready') if (roomId && roomId !== context.gameStore.roomId) { return } if (ready !== null && userId) { applyPlayerReadyState(userId, ready) } if (userId && userId === context.session.loggedInUserId.value) { clearReadyTogglePending(context.session) } } function handlePlayerDingQueResponse(message: unknown): void { const source = asRecord(message) if (!source || typeof source.type !== 'string' || normalizeWsType(source.type) !== 'PLAYER_DING_QUE') { return } const payload = asRecord(source.payload) if (!payload) { return } const roomId = readString(payload, 'room_id', 'roomId') || readString(source, 'roomId') if (roomId && roomId !== context.gameStore.roomId) { return } const userId = readString(payload, 'user_id', 'userId', 'player_id', 'playerId') || readString(source, 'target') const suit = readString(payload, 'suit', 'Suit') if (!userId || !suit) { return } setPlayerMissingSuit(context.gameStore, userId, suit) if (userId === context.session.loggedInUserId.value) { clearDingQuePending(context.session) } } return { applyPlayerReadyState, syncReadyStatesFromRoomUpdate, handlePlayerHandResponse, handleReadyStateResponse, handlePlayerDingQueResponse, syncTrusteeState, } }