diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 392ce7f99..8f202d9d0 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -371,42 +371,41 @@ export const ServerMessageSchema = z.union([ // Client -const ClientBaseMessageSchema = z.object({ - type: z.enum(["winner", "join", "intent", "ping", "log", "hash"]), -}); - -export const ClientSendWinnerSchema = ClientBaseMessageSchema.extend({ +export const ClientSendWinnerSchema = z.object({ type: z.literal("winner"), winner: z.union([ID, TeamSchema]).nullable(), allPlayersStats: AllPlayersStatsSchema, winnerType: z.enum(["player", "team"]), }); -export const ClientHashSchema = ClientBaseMessageSchema.extend({ +export const ClientHashSchema = z.object({ type: z.literal("hash"), hash: z.number(), turnNumber: z.number(), }); -export const ClientLogMessageSchema = ClientBaseMessageSchema.extend({ +export const ClientLogMessageSchema = z.object({ type: z.literal("log"), severity: z.nativeEnum(LogSeverity), log: ID, persistentID: SafeString, }); -export const ClientPingMessageSchema = ClientBaseMessageSchema.extend({ +export const ClientPingMessageSchema = z.object({ type: z.literal("ping"), }); -export const ClientIntentMessageSchema = ClientBaseMessageSchema.extend({ +export const ClientIntentMessageSchema = z.object({ type: z.literal("intent"), intent: IntentSchema, }); // WARNING: never send this message to clients. -export const ClientJoinMessageSchema = ClientBaseMessageSchema.extend({ +export const ClientJoinMessageSchema = z.object({ type: z.literal("join"), + clientID: ID, + persistentID: SafeString, // WARNING: PII + gameID: ID, lastTurn: z.number(), // The last turn the client saw. username: SafeString, flag: SafeString.nullable().optional(), diff --git a/src/server/Worker.ts b/src/server/Worker.ts index 880dca263..0ce554f16 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -7,7 +7,7 @@ import { WebSocket, WebSocketServer } from "ws"; import { GameEnv } from "../core/configuration/Config"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; import { GameType } from "../core/game/Game"; -import { GameConfig, GameRecord } from "../core/Schemas"; +import { ClientMessageSchema, GameConfig, GameRecord } from "../core/Schemas"; import { archive, readGameRecord } from "./Archive"; import { Client } from "./Client"; import { GameManager } from "./GameManager"; @@ -263,7 +263,9 @@ export function startWorker() { try { // Process WebSocket messages as in your original code // Parse and handle client messages - const clientMsg = JSON.parse(message.toString()); + const clientMsg = ClientMessageSchema.parse( + JSON.parse(message.toString()), + ); if (clientMsg.type == "join") { // Verify this worker should handle this game