import { Socket } from "socket.io"; import { getLogin } from "./auth"; /** Pomůcky pro websocketové minihry (kreslení). Ostatní minihry jsou v samostatné aplikaci Lobby. */ /** Chyba určená hráči — posílá se zpět klientovi, nic se neloguje. */ export class GameError extends Error { } /** Hodiny a náhoda vyčleněné kvůli testům. */ export type Clock = { now: () => number, setTimeout: (fn: () => void, ms: number) => unknown, clearTimeout: (handle: unknown) => void, random: () => number, }; export const defaultClock: Clock = { now: () => Date.now(), setTimeout: (fn, ms) => setTimeout(fn, ms), clearTimeout: handle => clearTimeout(handle as NodeJS.Timeout), random: () => Math.random(), }; /** Ověří token z eventu a vrátí login, jinak vyhodí chybu. */ export function loginFromPayload(payload: unknown): string { const token = (payload as { token?: unknown } | undefined)?.token; if (typeof token !== 'string') { throw new GameError('Chybí přihlášení.'); } try { const login = getLogin(token); if (!login) throw new Error(); return login; } catch { throw new GameError('Neplatné přihlášení.'); } } /** * Vytvoří handler websocket eventu, který ověří přihlášení a herní chyby * pošle zpět jen danému klientovi jako `errorEvent`. */ export function gameHandler(socket: Socket, errorEvent: string, action: (login: string, payload: unknown) => void | Promise) { const onError = (e: unknown) => { if (e instanceof GameError) { socket.emit(errorEvent, e.message); } else { console.error(`Chyba minihry (${errorEvent})`, e); } }; return (payload: unknown) => { try { const result = action(loginFromPayload(payload), payload); if (result instanceof Promise) result.catch(onError); } catch (e) { onError(e); } }; } /** Minimální počet hráčů — lokálně lze hrát i sám. */ export const minPlayersForEnv = (prod: number) => process.env.NODE_ENV === 'development' ? 1 : prod;