Files
mahjong-web/src/views/chengdu/socket/handlers/playerHandlers.ts
wsy182 e96c45739e feat(game): 添加成都麻将游戏页面和大厅功能
- 实现 ChengduGamePage.vue 组件,包含完整的麻将游戏界面
- 实现 HallPage.vue 组件,支持房间列表展示、创建和加入功能
- 添加 mahjong API 接口用于房间管理操作
- 集成 store 状态管理和本地存储功能
- 实现 ChengduBottomActions 等游戏控制组件
- 添加 websocket 连接和游戏会话管理逻辑
- 实现游戏倒计时、结算等功能模块
2026-04-03 20:46:50 +08:00

171 lines
5.1 KiB
TypeScript

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,
}
}