feat(games): add room types and game WS message schemas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
syntaxbullet
2026-04-02 13:23:11 +02:00
parent a5478dce2b
commit db10ebe220
2 changed files with 59 additions and 0 deletions

54
api/src/games/types.ts Normal file
View File

@@ -0,0 +1,54 @@
import { z } from "zod";
// --- Room types ---
export interface Room {
id: string;
gameSlug: string;
host: string;
players: string[];
spectators: Set<string>;
state: unknown;
status: "waiting" | "playing" | "finished";
createdAt: number;
}
export interface RoomSummary {
id: string;
gameSlug: string;
gameName: string;
host: string;
playerCount: number;
maxPlayers: number;
spectatorCount: number;
status: "waiting" | "playing" | "finished";
}
export interface PlayerInfo {
discordId: string;
username: string;
}
// --- Client → Server messages ---
export const GameWsClientSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal("CREATE_ROOM"), gameType: z.string() }),
z.object({ type: z.literal("JOIN_ROOM"), roomId: z.string(), as: z.enum(["player", "spectator"]) }),
z.object({ type: z.literal("LEAVE_ROOM"), roomId: z.string() }),
z.object({ type: z.literal("GAME_ACTION"), roomId: z.string(), action: z.record(z.unknown()) }),
]);
export type GameWsClientMessage = z.infer<typeof GameWsClientSchema>;
// --- Server → Client messages ---
export type GameWsServerMessage =
| { type: "ROOM_LIST_UPDATE"; rooms: RoomSummary[] }
| { type: "GAME_STATE"; roomId: string; state: unknown }
| { type: "GAME_UPDATE"; roomId: string; state: unknown }
| { type: "PLAYER_JOINED"; roomId: string; player: PlayerInfo; as: "player" | "spectator" }
| { type: "PLAYER_LEFT"; roomId: string; playerId: string }
| { type: "GAME_STARTED"; roomId: string; state: unknown }
| { type: "GAME_ENDED"; roomId: string; winner: string | null; reason: string }
| { type: "ROOM_CREATED"; roomId: string; gameSlug: string }
| { type: "ERROR"; message: string };

View File

@@ -110,6 +110,11 @@ export const WsMessageSchema = z.discriminatedUnion("type", [
z.object({ type: z.literal("PONG") }),
z.object({ type: z.literal("STATS_UPDATE"), data: DashboardStatsSchema }),
z.object({ type: z.literal("NEW_EVENT"), data: RecentEventSchema }),
// Game messages (client → server)
z.object({ type: z.literal("CREATE_ROOM"), gameType: z.string() }),
z.object({ type: z.literal("JOIN_ROOM"), roomId: z.string(), as: z.enum(["player", "spectator"]) }),
z.object({ type: z.literal("LEAVE_ROOM"), roomId: z.string() }),
z.object({ type: z.literal("GAME_ACTION"), roomId: z.string(), action: z.record(z.unknown()) }),
]);
export type WsMessage = z.infer<typeof WsMessageSchema>;