feat(api): add game:ready message for client state sync
- 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
This commit is contained in:
parent
4d4715b4ee
commit
6975384751
3 changed files with 38 additions and 2 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
import type { WebSocket } from "ws";
|
import type { WebSocket } from "ws";
|
||||||
import type { User } from "better-auth";
|
import type { User } from "better-auth";
|
||||||
import type { WsGameAnswer } from "@lila/shared";
|
import type { WsGameAnswer, WsGameReady } from "@lila/shared";
|
||||||
import { finishGame, getLobbyByCodeWithPlayers } from "@lila/db";
|
import { finishGame, getLobbyByCodeWithPlayers } from "@lila/db";
|
||||||
import { broadcastToLobby, getConnections } from "../connections.js";
|
import { broadcastToLobby, getConnections } from "../connections.js";
|
||||||
import { lobbyGameStore, timers } from "../gameState.js";
|
import { lobbyGameStore, timers } from "../gameState.js";
|
||||||
|
|
@ -52,6 +52,32 @@ export const handleGameAnswer = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handleGameReady = async (
|
||||||
|
ws: WebSocket,
|
||||||
|
msg: WsGameReady,
|
||||||
|
_user: User,
|
||||||
|
): Promise<void> => {
|
||||||
|
const state = await lobbyGameStore.get(msg.lobbyId);
|
||||||
|
if (!state) throw new NotFoundError("Game not found");
|
||||||
|
|
||||||
|
const currentQuestion = state.questions[state.currentIndex];
|
||||||
|
if (!currentQuestion) throw new NotFoundError("No active question");
|
||||||
|
|
||||||
|
ws.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: "game:question",
|
||||||
|
question: {
|
||||||
|
questionId: currentQuestion.questionId,
|
||||||
|
prompt: currentQuestion.prompt,
|
||||||
|
gloss: currentQuestion.gloss,
|
||||||
|
options: currentQuestion.options,
|
||||||
|
},
|
||||||
|
questionNumber: state.currentIndex + 1,
|
||||||
|
totalQuestions: state.questions.length,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const resolveRound = async (
|
export const resolveRound = async (
|
||||||
lobbyId: string,
|
lobbyId: string,
|
||||||
questionIndex: number,
|
questionIndex: number,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
handleLobbyLeave,
|
handleLobbyLeave,
|
||||||
handleLobbyStart,
|
handleLobbyStart,
|
||||||
} from "./handlers/lobbyHandlers.js";
|
} from "./handlers/lobbyHandlers.js";
|
||||||
import { handleGameAnswer } from "./handlers/gameHandlers.js";
|
import { handleGameAnswer, handleGameReady } from "./handlers/gameHandlers.js";
|
||||||
import { AppError } from "../errors/AppError.js";
|
import { AppError } from "../errors/AppError.js";
|
||||||
|
|
||||||
export type AuthenticatedUser = { session: Session; user: User };
|
export type AuthenticatedUser = { session: Session; user: User };
|
||||||
|
|
@ -60,6 +60,9 @@ export const handleMessage = async (
|
||||||
case "game:answer":
|
case "game:answer":
|
||||||
await handleGameAnswer(ws, msg, auth.user);
|
await handleGameAnswer(ws, msg, auth.user);
|
||||||
break;
|
break;
|
||||||
|
case "game:ready":
|
||||||
|
await handleGameReady(ws, msg, auth.user);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertExhaustive(msg);
|
assertExhaustive(msg);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,12 @@ export const WsLobbyStartSchema = z.object({
|
||||||
|
|
||||||
export type WsLobbyStart = z.infer<typeof WsLobbyStartSchema>;
|
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({
|
export const WsGameAnswerSchema = z.object({
|
||||||
type: z.literal("game:answer"),
|
type: z.literal("game:answer"),
|
||||||
lobbyId: z.uuid(),
|
lobbyId: z.uuid(),
|
||||||
|
|
@ -66,6 +72,7 @@ export const WsClientMessageSchema = z.discriminatedUnion("type", [
|
||||||
WsLobbyLeaveSchema,
|
WsLobbyLeaveSchema,
|
||||||
WsLobbyStartSchema,
|
WsLobbyStartSchema,
|
||||||
WsGameAnswerSchema,
|
WsGameAnswerSchema,
|
||||||
|
WsGameReadySchema,
|
||||||
]);
|
]);
|
||||||
export type WsClientMessage = z.infer<typeof WsClientMessageSchema>;
|
export type WsClientMessage = z.infer<typeof WsClientMessageSchema>;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue