Files
MineGeo/shared/multiplayer.ts

85 lines
2.0 KiB
TypeScript

import { z } from "zod";
import { displayNameSchema } from "./account";
import type { Bounds, Difficulty } from "./protocol";
export const createRoomSchema = z.object({
datasetId: z.string().uuid(),
difficulty: z.enum(["easy", "normal", "hard"]),
displayName: displayNameSchema,
playerLimit: z.number().int().min(2).max(16),
});
export const joinRoomSchema = z.object({ displayName: displayNameSchema });
export const chatMessageSchema = z.object({
clientId: z.string().uuid(),
text: z
.string()
.trim()
.min(1)
.max(500)
.refine(
(text) =>
!/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(text),
"Messages cannot contain hidden controls",
),
});
export type RoomChat = {
messages: {
id: string;
playerId: string;
name: string;
text: string;
createdAt: string;
}[];
};
export const roomRoundSchema = z.object({
round: z.number().int().min(0).max(4),
});
export const roomGuessSchema = roomRoundSchema.extend({
x: z.number().finite(),
z: z.number().finite(),
});
export type RoomResult = {
playerId: string;
score: number;
distance: number | null;
timedOut: boolean;
actual: { x: number; z: number };
guess: { x: number; z: number } | null;
};
export type RoomView = {
id: string;
datasetId: string;
name: string;
difficulty: Difficulty;
playerLimit: number;
playerCount: number;
status: "lobby" | "loading" | "playing" | "results" | "finished" | "closed";
round: number;
hostId: string;
you: string | null;
serverNow: number;
transitionAt: string | null;
players: {
id: string;
name: string;
left: boolean;
ready: boolean;
submitted: boolean;
total: number;
rank: number;
}[];
rounds: {
number: number;
deadline: string | null;
closedAt: string | null;
results: RoomResult[];
}[];
myGuess: { x: number; z: number } | null;
};
export type RoomMedia = {
round: number;
map: string;
bounds: Bounds;
faces: string[];
};