Improve MineGeo UI and application flows
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 2m37s
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 2m37s
This commit is contained in:
109
shared/tournaments.ts
Normal file
109
shared/tournaments.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { z } from "zod";
|
||||
import type { RoomView } from "./multiplayer";
|
||||
|
||||
const safeName = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3)
|
||||
.max(80)
|
||||
.refine(
|
||||
(value) =>
|
||||
!/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(value),
|
||||
"Names cannot contain hidden controls",
|
||||
);
|
||||
export const tournamentSettings = z
|
||||
.object({
|
||||
name: safeName,
|
||||
datasetId: z.string().uuid(),
|
||||
difficulty: z.union([
|
||||
z.enum(["easy", "normal", "hard"]),
|
||||
z.string().uuid(),
|
||||
]),
|
||||
playerLimit: z.number().int().min(2).max(32),
|
||||
roundCount: z.number().int().min(1).max(10),
|
||||
roundTimeSeconds: z.number().int().min(15).max(3000).multipleOf(15),
|
||||
timerMode: z.enum(["shared", "perMini"]).default("shared"),
|
||||
startsAt: z.string().datetime({ offset: true }),
|
||||
announceDiscord: z.boolean(),
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
(value) => value.timerMode !== "perMini" || value.roundTimeSeconds <= 600,
|
||||
"Mini-round timers allow at most 600 seconds",
|
||||
);
|
||||
export const tournamentEdit = z
|
||||
.object({
|
||||
startsAt: z.string().datetime({ offset: true }),
|
||||
playerLimit: z.number().int().min(2).max(32),
|
||||
announceDiscord: z.boolean(),
|
||||
})
|
||||
.strict();
|
||||
export type TournamentRecord = {
|
||||
id: string;
|
||||
name: string;
|
||||
owner_id: string | null;
|
||||
starts_at: string;
|
||||
server_timezone: string;
|
||||
player_limit: number;
|
||||
round_count: number;
|
||||
round_time_seconds: number;
|
||||
timer_mode: "shared" | "perMini";
|
||||
status: "scheduled" | "running" | "finished" | "cancelled";
|
||||
announce_discord: boolean;
|
||||
finished_at: string | null;
|
||||
winner_id: string | null;
|
||||
};
|
||||
export type TournamentView = {
|
||||
id: string;
|
||||
name: string;
|
||||
ownerId: string | null;
|
||||
datasetId: string;
|
||||
startsAt: string;
|
||||
serverTimezone: string;
|
||||
playerLimit: number;
|
||||
roundCount: number;
|
||||
roundTimeSeconds: number;
|
||||
timerMode: "shared" | "perMini";
|
||||
roundDeadline?: string | null;
|
||||
announceDiscord: boolean;
|
||||
status: TournamentRecord["status"];
|
||||
currentRound: number;
|
||||
finishedAt: string | null;
|
||||
winnerId: string | null;
|
||||
you: string | null;
|
||||
roomId: string | null;
|
||||
currentRoom?: RoomView | null;
|
||||
announcementState?:
|
||||
| "pending"
|
||||
| "sending"
|
||||
| "sent"
|
||||
| "skipped"
|
||||
| "failed"
|
||||
| null;
|
||||
serverNow: number;
|
||||
players: { accountId: string; name: string; total: number; rank: number }[];
|
||||
};
|
||||
|
||||
/** Interpret a datetime-local value in the advertised server zone, never the browser zone. */
|
||||
export function serverLocalToIso(value: string, zone: string) {
|
||||
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(value))
|
||||
throw new Error("Choose a valid start time");
|
||||
const target = Date.parse(value + ":00Z");
|
||||
if (!Number.isFinite(target)) throw new Error("Choose a valid start time");
|
||||
const formatter = new Intl.DateTimeFormat("sv-SE", {
|
||||
timeZone: zone,
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hourCycle: "h23",
|
||||
});
|
||||
let instant = target;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const shown = formatter.format(new Date(instant)).replace(" ", "T");
|
||||
if (shown === value) return new Date(instant).toISOString();
|
||||
instant += target - Date.parse(shown + ":00Z");
|
||||
}
|
||||
throw new Error("That local time does not exist in the server timezone");
|
||||
}
|
||||
Reference in New Issue
Block a user