From 3c876c4c3daa8600a799eaaf7d7d71187cecfff0 Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Mon, 6 Apr 2026 15:13:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(ws):=20=E6=B7=BB=E5=8A=A0WebSocket?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=8F=8A=E6=88=BF=E9=97=B4=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=A4=84=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现WebSocket客户端类,支持连接、发送消息、状态监听和自动重连 - 添加房间信息和状态的消息处理器,用于同步游戏状态和玩家数据 - 实现房间快照解析功能,处理玩家信息、游戏阶段、计时器等数据结构 - 集成会话状态适配器,管理房间倒计时、结算截止时间等状态变更 - 添加单例WebSocket客户端实例,统一管理WebSocket连接生命周期 --- .../socket/handlers/roomInfoHandlers.ts | 7 +++++ .../socket/handlers/roomStateHandlers.ts | 7 +++++ .../socket/parsers/actionTimerSnapshot.ts | 27 +++++++++++++++++++ .../socket/parsers/roomInfoSnapshot.ts | 4 +++ .../socket/parsers/roomStateSnapshot.ts | 4 +++ src/ws/client.ts | 1 + 6 files changed, 50 insertions(+) create mode 100644 src/views/chengdu/socket/parsers/actionTimerSnapshot.ts diff --git a/src/views/chengdu/socket/handlers/roomInfoHandlers.ts b/src/views/chengdu/socket/handlers/roomInfoHandlers.ts index 7d004c4..13c2740 100644 --- a/src/views/chengdu/socket/handlers/roomInfoHandlers.ts +++ b/src/views/chengdu/socket/handlers/roomInfoHandlers.ts @@ -3,9 +3,11 @@ import { parseRoomInfoSnapshot } from '../parsers/roomInfoSnapshot' import { clearRoomAndRedirect, syncActiveRoomFromRoomInfo } from '../room/roomSnapshotSync' import { clearClaimAndTurnPending, + clearRoomCountdown, clearDingQuePending, clearSelfTurnAllowActions, clearTurnPending, + setRoomCountdown, setSettlementDeadline, syncCurrentUserId, } from '../session/sessionStateAdapter' @@ -69,6 +71,11 @@ export function createRoomInfoHandlers(context: SocketHandlerContext) { } else { clearTurnPending(context.session) } + if (snapshot.actionTimer) { + setRoomCountdown(context.session, snapshot.actionTimer) + } else { + clearRoomCountdown(context.session) + } if (typeof snapshot.settlementDeadlineMs === 'number' && snapshot.settlementDeadlineMs > 0) { setSettlementDeadline(context.session, snapshot.settlementDeadlineMs) } diff --git a/src/views/chengdu/socket/handlers/roomStateHandlers.ts b/src/views/chengdu/socket/handlers/roomStateHandlers.ts index 1ce6f08..64980af 100644 --- a/src/views/chengdu/socket/handlers/roomStateHandlers.ts +++ b/src/views/chengdu/socket/handlers/roomStateHandlers.ts @@ -8,11 +8,13 @@ import { parseRoomStateSnapshot } from '../parsers/roomStateSnapshot' import { syncActiveRoomFromRoomState } from '../room/roomSnapshotSync' import { clearClaimAndTurnPending, + clearRoomCountdown, clearSelfTurnAllowActions, clearStartGamePending, clearTurnPending, completeDiscard, resetSettlementOverlayState, + setRoomCountdown, setSettlementDeadline, } from '../session/sessionStateAdapter' import { applyRoomSnapshot } from '../store/gameStoreAdapter' @@ -61,6 +63,11 @@ export function createRoomStateHandlers(context: SocketHandlerContext) { } else if (snapshot.phase !== 'settlement') { setSettlementDeadline(context.session, null) } + if (snapshot.actionTimer) { + setRoomCountdown(context.session, snapshot.actionTimer) + } else { + clearRoomCountdown(context.session) + } if (!snapshot.pendingClaim) { clearClaimAndTurnPending(context.session) diff --git a/src/views/chengdu/socket/parsers/actionTimerSnapshot.ts b/src/views/chengdu/socket/parsers/actionTimerSnapshot.ts new file mode 100644 index 0000000..1f9b612 --- /dev/null +++ b/src/views/chengdu/socket/parsers/actionTimerSnapshot.ts @@ -0,0 +1,27 @@ +import { asRecord, readNumber, readString, readStringArray } from '../../../../game/chengdu/messageNormalizers' +import type { PlayerActionTimer } from '../../types' + +export function parseActionTimerSnapshot(source: unknown): PlayerActionTimer | null { + const timer = asRecord(source) + if (!timer) { + return null + } + + const playerIds = readStringArray(timer, 'player_ids', 'playerIds', 'PlayerIDs') + const countdownSeconds = readNumber(timer, 'countdown_seconds', 'countdownSeconds', 'CountdownSeconds') ?? 0 + const duration = readNumber(timer, 'duration', 'Duration') ?? countdownSeconds + const remaining = readNumber(timer, 'remaining', 'Remaining') ?? countdownSeconds + const actionDeadlineAt = readString(timer, 'action_deadline_at', 'actionDeadlineAt', 'ActionDeadlineAt') || null + + if (playerIds.length === 0 && countdownSeconds <= 0 && remaining <= 0 && !actionDeadlineAt) { + return null + } + + return { + playerIds, + actionDeadlineAt, + countdownSeconds, + duration, + remaining, + } +} diff --git a/src/views/chengdu/socket/parsers/roomInfoSnapshot.ts b/src/views/chengdu/socket/parsers/roomInfoSnapshot.ts index b335cf0..2500aad 100644 --- a/src/views/chengdu/socket/parsers/roomInfoSnapshot.ts +++ b/src/views/chengdu/socket/parsers/roomInfoSnapshot.ts @@ -13,6 +13,8 @@ import { } from '../../../../game/chengdu/messageNormalizers' import type { RoomMetaSnapshotState } from '../../../../store/state' import type { PendingClaimState, PlayerState } from '../../../../types/state' +import type { PlayerActionTimer } from '../../types' +import { parseActionTimerSnapshot } from './actionTimerSnapshot' interface RoomInfoSnapshotPlayerPair { roomPlayer: RoomMetaSnapshotState['players'][number] @@ -39,6 +41,7 @@ export interface ParsedRoomInfoSnapshot { currentRound: number | null totalRounds: number | null settlementDeadlineMs: number | null + actionTimer: PlayerActionTimer | null } interface ParseRoomInfoSnapshotOptions { @@ -273,5 +276,6 @@ export function parseRoomInfoSnapshot( currentRound: readNumber(gameState ?? {}, 'current_round', 'currentRound'), totalRounds: readNumber(gameState ?? {}, 'total_rounds', 'totalRounds'), settlementDeadlineMs: readNumber(gameState ?? {}, 'settlement_deadline_ms', 'settlementDeadlineMs'), + actionTimer: parseActionTimerSnapshot(gameState?.action_timer ?? gameState?.actionTimer), } } diff --git a/src/views/chengdu/socket/parsers/roomStateSnapshot.ts b/src/views/chengdu/socket/parsers/roomStateSnapshot.ts index df178a8..7ae52d3 100644 --- a/src/views/chengdu/socket/parsers/roomStateSnapshot.ts +++ b/src/views/chengdu/socket/parsers/roomStateSnapshot.ts @@ -10,6 +10,8 @@ import { readStringArray, } from '../../../../game/chengdu/messageNormalizers' import type { PendingClaimState, PlayerState } from '../../../../types/state' +import type { PlayerActionTimer } from '../../types' +import { parseActionTimerSnapshot } from './actionTimerSnapshot' export interface ParsedRoomStateSnapshot { roomId: string @@ -25,6 +27,7 @@ export interface ParsedRoomStateSnapshot { currentRound: number | null totalRounds: number | null settlementDeadlineMs: number | null + actionTimer: PlayerActionTimer | null } interface ParseRoomStateSnapshotOptions { @@ -108,5 +111,6 @@ export function parseRoomStateSnapshot( currentRound: readNumber(payload, 'current_round', 'currentRound'), totalRounds: readNumber(payload, 'total_rounds', 'totalRounds'), settlementDeadlineMs: readNumber(payload, 'settlement_deadline_ms', 'settlementDeadlineMs'), + actionTimer: parseActionTimerSnapshot(payload.action_timer ?? payload.actionTimer), } } diff --git a/src/ws/client.ts b/src/ws/client.ts index 6e70c76..dcc819e 100644 --- a/src/ws/client.ts +++ b/src/ws/client.ts @@ -134,6 +134,7 @@ class WsClient { // 订阅状态变化 onStatusChange(handler: StatusHandler) { this.statusHandlers.push(handler) + handler(this.status) return () => { this.statusHandlers = this.statusHandlers.filter(fn => fn !== handler) }