refactor(game): 重构游戏状态管理和WebSocket通信

- 定义统一的游戏动作类型GameAction替代原有发送函数
- 创建游戏状态管理store使用Pinia进行状态管理
- 实现游戏状态分发器处理各种游戏事件
- 重构WebSocket处理器支持多处理器注册
- 重命名状态类型文件统一使用State后缀
- 添加ACTION游戏阶段处理操作窗口逻辑
- 集成Pinia依赖管理应用状态
This commit is contained in:
2026-03-25 15:19:28 +08:00
parent 4a9b2f2db2
commit 2737971608
17 changed files with 334 additions and 178 deletions

View File

@@ -1,145 +1,55 @@
import type {ActionButtonState} from "./types.ts"
import {wsClient} from "../ws/client.ts"
import type {GameState, PendingClaimState} from "../types/state";
import type {Tile} from "../types/tile.ts";
export function sendGameAction(
params: {
type: ActionButtonState['type']
userID: string
roomId: string
selectedTile?: string | null
},
ctx: {
actionPending: { value: boolean }
logWsSend: (msg: any) => void
pushWsMessage: (msg: string) => void
/**
* 游戏动作定义(只描述“发生了什么”)
*/
export type GameAction =
// 初始化整局(进入房间 / 断线重连)
| {
type: 'GAME_INIT'
payload: GameState
}
// 开始游戏(发牌完成)
| {
type: 'GAME_START'
payload: {
dealerIndex: number
}
): void {
}
const {type, userID, roomId, selectedTile} = params
const {actionPending, logWsSend, pushWsMessage} = ctx
// 简单登录判断
if (!userID) {
console.log('当前用户未登录')
return
// 摸牌
| {
type: 'DRAW_TILE'
payload: {
playerId: string
tile: Tile
}
const payload: Record<string, unknown> = {}
}
// 出牌
if (type === 'discard' && selectedTile) {
payload.tile = selectedTile
payload.discard_tile = selectedTile
payload.code = selectedTile
| {
type: 'PLAY_TILE'
payload: {
playerId: string
tile: Tile
nextSeat: number
}
actionPending.value = true
const message = {
type,
sender: userID,
target: 'room',
roomId,
seq: Date.now(),
payload,
}
logWsSend(message)
wsClient.send(message)
pushWsMessage(`[client] 请求${type}`)
}
export function sendStartGame(params: {
userID: string
roomId: string
canStartGame: boolean
startGamePending: { value: boolean }
logWsSend: (msg: any) => void
pushWsMessage: (msg: string) => void
}): void {
const {
userID,
roomId,
canStartGame,
startGamePending,
logWsSend,
pushWsMessage
} = params
if (!canStartGame || startGamePending.value) {
return
}
if (!userID) {
return
}
startGamePending.value = true
const message = {
type: 'start_game',
sender: userID,
target: 'room',
roomId,
seq: Date.now(),
payload: {},
}
logWsSend(message)
wsClient.send(message)
pushWsMessage(`[client] 请求开始游戏`)
// 进入操作窗口(碰/杠/胡)
| {
type: 'PENDING_CLAIM'
payload: PendingClaimState
}
export function sendLeaveRoom(params: {
userID: string
roomId: string
wsError: { value: string }
leaveRoomPending: { value: boolean }
logWsSend: (msg: any) => void
pushWsMessage: (msg: string) => void
}): boolean {
const {
userID,
roomId,
wsError,
leaveRoomPending,
logWsSend,
pushWsMessage
} = params
if (!userID) {
wsError.value = '缺少当前用户 ID无法退出房间'
return false
// 操作结束(碰/杠/胡/过)
| {
type: 'CLAIM_RESOLVED'
payload: {
playerId: string
action: 'peng' | 'gang' | 'hu' | 'pass'
}
if (!roomId) {
wsError.value = '缺少房间 ID无法退出房间'
return false
}
leaveRoomPending.value = true
const message = {
type: 'leave_room',
sender: userID,
target: 'room',
roomId: roomId,
seq: Date.now(),
payload: {},
}
logWsSend(message)
wsClient.send(message)
pushWsMessage(`[client] 请求退出房间`)
return true
}