feat: skryté multiplayerové minihry přes websocket
CI / Generate TypeScript types (push) Canceled after 7s
CI / Server unit tests (push) Canceled after 0s
CI / Build server (push) Canceled after 0s
CI / Build client (push) Canceled after 0s
CI / Playwright E2E tests (push) Canceled after 0s
CI / Build and push Docker image (push) Canceled after 0s
CI / Notify (push) Canceled after 0s
CI / Generate TypeScript types (push) Canceled after 7s
CI / Server unit tests (push) Canceled after 0s
CI / Build server (push) Canceled after 0s
CI / Build client (push) Canceled after 0s
CI / Playwright E2E tests (push) Canceled after 0s
CI / Build and push Docker image (push) Canceled after 0s
CI / Notify (push) Canceled after 0s
Minihry se otevírají trojklikem na logo a napsáním kódu hry
("help" vypíše seznam do konzole prohlížeče):
- reakce: reakční hra, 3 kola, pořadí podle součtu časů
- snake: had pro více hráčů, simulaci drží server
- tetris: battle Tetris s odpadními řádky a lokálním tréninkem
- pong: Pong 1v1, ovládání myší i klávesami, bot se 4 obtížnostmi
- kresleni: kreslení a hádání jídel z dnešních menu
Ikony her se řídí zvoleným vzhledem. Stav her je v paměti serveru,
předpokládá se jedna replika.
This commit is contained in:
28 files changed
+5050
-2
No files matched your search
@@ -0,0 +1,62 @@
|
||||
import { Socket } from "socket.io";
|
||||
import { getLogin } from "./auth";
|
||||
|
||||
/** Sdílené pomůcky pro websocketové minihry (reakční hra, had). */
|
||||
|
||||
/** 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<void>) {
|
||||
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;
|
||||
Reference in new issue
Block a user