- WsGameReadySchema added to shared schemas and WsClientMessageSchema - handleGameReady sends current game:question directly to requesting client socket (not broadcast) — foundation for reconnection slice - router dispatches game:ready to handleGameReady handler
137 lines
3.8 KiB
TypeScript
137 lines
3.8 KiB
TypeScript
import * as z from "zod";
|
|
|
|
import { LOBBY_STATUSES } from "../constants.js";
|
|
import { GameQuestionSchema } from "./game.js";
|
|
|
|
export const LobbyPlayerSchema = z.object({
|
|
lobbyId: z.uuid(),
|
|
userId: z.string(),
|
|
score: z.number().int().min(0),
|
|
user: z.object({ id: z.string(), name: z.string() }),
|
|
});
|
|
|
|
export type LobbyPlayer = z.infer<typeof LobbyPlayerSchema>;
|
|
|
|
export const LobbySchema = z.object({
|
|
id: z.uuid(),
|
|
code: z.string().min(1).max(10),
|
|
hostUserId: z.string(),
|
|
status: z.enum(LOBBY_STATUSES),
|
|
createdAt: z.iso.datetime(),
|
|
players: z.array(LobbyPlayerSchema),
|
|
});
|
|
|
|
export type Lobby = z.infer<typeof LobbySchema>;
|
|
|
|
export const JoinLobbyResponseSchema = LobbySchema;
|
|
|
|
export type JoinLobbyResponse = z.infer<typeof JoinLobbyResponseSchema>;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// WebSocket: Client → Server
|
|
// ----------------------------------------------------------------------------
|
|
|
|
export const WsLobbyJoinSchema = z.object({
|
|
type: z.literal("lobby:join"),
|
|
code: z.string().min(1).max(10),
|
|
});
|
|
|
|
export type WsLobbyJoin = z.infer<typeof WsLobbyJoinSchema>;
|
|
|
|
export const WsLobbyLeaveSchema = z.object({
|
|
type: z.literal("lobby:leave"),
|
|
lobbyId: z.uuid(),
|
|
});
|
|
|
|
export type WsLobbyLeave = z.infer<typeof WsLobbyLeaveSchema>;
|
|
|
|
export const WsLobbyStartSchema = z.object({
|
|
type: z.literal("lobby:start"),
|
|
lobbyId: z.uuid(),
|
|
});
|
|
|
|
export type WsLobbyStart = z.infer<typeof WsLobbyStartSchema>;
|
|
|
|
export const WsGameReadySchema = z.object({
|
|
type: z.literal("game:ready"),
|
|
lobbyId: z.uuid(),
|
|
});
|
|
export type WsGameReady = z.infer<typeof WsGameReadySchema>;
|
|
|
|
export const WsGameAnswerSchema = z.object({
|
|
type: z.literal("game:answer"),
|
|
lobbyId: z.uuid(),
|
|
questionId: z.uuid(),
|
|
selectedOptionId: z.number().int().min(0).max(3),
|
|
});
|
|
|
|
export type WsGameAnswer = z.infer<typeof WsGameAnswerSchema>;
|
|
|
|
export const WsClientMessageSchema = z.discriminatedUnion("type", [
|
|
WsLobbyJoinSchema,
|
|
WsLobbyLeaveSchema,
|
|
WsLobbyStartSchema,
|
|
WsGameAnswerSchema,
|
|
WsGameReadySchema,
|
|
]);
|
|
export type WsClientMessage = z.infer<typeof WsClientMessageSchema>;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// WebSocket: Server → Client
|
|
// ----------------------------------------------------------------------------
|
|
|
|
export const WsLobbyStateSchema = z.object({
|
|
type: z.literal("lobby:state"),
|
|
lobby: LobbySchema,
|
|
});
|
|
|
|
export type WsLobbyState = z.infer<typeof WsLobbyStateSchema>;
|
|
|
|
export const WsGameQuestionSchema = z.object({
|
|
type: z.literal("game:question"),
|
|
question: GameQuestionSchema,
|
|
questionNumber: z.number().int().min(1),
|
|
totalQuestions: z.number().int().min(1),
|
|
});
|
|
|
|
export type WsGameQuestion = z.infer<typeof WsGameQuestionSchema>;
|
|
|
|
export const WsGameAnswerResultSchema = z.object({
|
|
type: z.literal("game:answer_result"),
|
|
correctOptionId: z.number().int().min(0).max(3),
|
|
results: z.array(
|
|
z.object({
|
|
userId: z.string(),
|
|
selectedOptionId: z.number().int().min(0).max(3).nullable(),
|
|
isCorrect: z.boolean(),
|
|
}),
|
|
),
|
|
players: z.array(LobbyPlayerSchema),
|
|
});
|
|
|
|
export type WsGameAnswerResult = z.infer<typeof WsGameAnswerResultSchema>;
|
|
|
|
export const WsGameFinishedSchema = z.object({
|
|
type: z.literal("game:finished"),
|
|
players: z.array(LobbyPlayerSchema),
|
|
winnerIds: z.array(z.string()),
|
|
});
|
|
|
|
export type WsGameFinished = z.infer<typeof WsGameFinishedSchema>;
|
|
|
|
export const WsErrorSchema = z.object({
|
|
type: z.literal("error"),
|
|
code: z.string(),
|
|
message: z.string(),
|
|
});
|
|
export type WsError = z.infer<typeof WsErrorSchema>;
|
|
|
|
export const WsServerMessageSchema = z.discriminatedUnion("type", [
|
|
WsLobbyStateSchema,
|
|
WsGameQuestionSchema,
|
|
WsGameAnswerResultSchema,
|
|
WsGameFinishedSchema,
|
|
WsErrorSchema,
|
|
]);
|
|
|
|
export type WsServerMessage = z.infer<typeof WsServerMessageSchema>;
|