WebSocket 协议
TuringHunt 前端通过单条 WebSocket 连接与后端实时通信。所有消息均为 JSON 格式。
连接地址
| 环境 | 地址 |
|---|---|
| 优先读取环境变量 | NEXT_PUBLIC_WS_URL |
| 未配置时自动推断 | ws(s)://{hostname}/ws/game |
消息格式
所有消息均遵循:
json
{ "type": "消息类型", "data": { /* 载荷 */ } }data 字段对于无载荷消息为 null。
服务端 → 客户端消息
GAME_STATE
完整游戏快照,通常在玩家首次连接或重连时发送。
ts
interface GameStateData {
players: Player[];
player_count: number;
phase: Phase;
leader_id: string | null;
required_team_size: number;
mission_number: number;
current_team: string[];
is_speaker: boolean;
current_speaker_id: string | null;
needs_vote: boolean;
input_locked: boolean;
}ROLE_ASSIGNED
游戏开始时告知本玩家的角色及其可见的其他玩家身份。
ts
interface RoleAssignedData {
role: Role;
visible_others: Record<string, string>; // user_id → role
}PHASE_CHANGED
阶段切换通知,前端据此更新 phase 并重置阶段相关状态。
ts
interface PhaseChangedData {
phase: Phase;
leader_id: string | null;
required_team_size: number;
mission_number: number;
current_speaker_id?: string | null;
}REQUEST_INPUT
服务端请求玩家进行特定输入,前端据此解锁对应 UI。
ts
interface RequestInputData {
input_type: "SPEECH" | "SUBMIT_TEAM" | "VOTE" | "ASSASSIN_CHOOSE";
phase: Phase;
mission_number: number;
}INPUT_LOCKED / INPUT_UNLOCKED
直接控制所有交互元素的禁用状态,data 为 null。
TEAM_PROPOSED
领袖提交队伍后广播给所有人。
ts
interface TeamProposedData {
leader_id: string;
team: string[];
mission_number: number;
}TEAM_VOTE_RESULT
所有人投票完成后广播结果。
ts
interface TeamVoteResultData {
votes: Record<string, boolean>; // user_id → 是否赞成
approved: boolean;
consecutive_vote_failures: number;
mission_number: number;
}MISSION_RESULT
任务执行完毕后广播结果(不公开投票人)。
ts
interface MissionResultData {
mission_number: number;
success: boolean;
fail_votes: number;
success_votes: number;
good_wins: number;
evil_wins: number;
}SPEECH_ADDED
某位玩家发言后实时广播,同时告知下一位发言者。
ts
interface SpeechAddedData {
player_id: string;
content: string;
next_speaker_id: string | null;
}SPEECH_HISTORY
发言阶段结束后广播本轮完整发言记录(用于后续阶段的侧边栏展示)。
ts
interface SpeechHistoryData {
speeches: Array<{ player_id: string; username: string; content: string }>;
}GAME_OVER
游戏结束,公开所有人身份。
ts
interface GameOverData {
winner: "good" | "evil";
reason: "good_missions" | "evil_missions" | "vote_failures" | "assassin_success";
role_reveal: Record<string, string>; // user_id → role
}ERROR
服务端返回错误(如非法操作),前端通过 Toast 展示。
ts
interface ErrorData { error: string }客户端 → 服务端消息
| type | data | 说明 |
|---|---|---|
START_GAME | null | 开始游戏 |
SPEECH | { content: string } | 提交发言内容 |
END_SPEECH | null | 结束本人发言 |
SUBMIT_TEAM | { team: string[] } | 领袖提交任务队伍 |
VOTE | { vote: boolean } | 投票(队伍或任务) |
ASSASSIN_CHOOSE | { target_id: string } | 刺客指定目标 |
重连机制
useWebSocket 实现指数退避自动重连:
- 最大重试次数:
5 - 重试延迟:
2^n秒(n = 重试次数) - 关闭码
4001(鉴权失败):不重试,触发WS_AUTH_ERROR
ts
const delay = Math.pow(2, retriesRef.current) * 1000;
retriesRef.current += 1;
setTimeout(connect, delay);