/
docNemo
/
tactic-hack-core
Обзор
Документация
Войти
/
docNemo
/
tactic-hack-core
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/protocol.ts
125 строк
4 KB
neo
Implement rules engine: geometry, placement, attack reducer, replay, projection, deduction, protocol types
19 июл 2026, 00:03
19 июл 2026, 00:03
d1403fb
Код
Авторство
О чём код?
import { PlacementError } from "./placement.js"; import { AttackError } from "./match.js"; import { PlayerProjection } from "./projection.js"; import { Cell, PlayerId } from "./types.js"; /** Имена комнат Colyseus. */ export const ROOM_LOBBY = "lobby"; export const ROOM_GAME = "game"; /** Таймеры по умолчанию (конфигурируются на сервере). */ export const DEFAULT_TIMERS = { placementMs: 180_000, turnMs: 60_000, reconnectMs: 120_000, } as const; export type GamePhase = "placement" | "battle" | "finished" | "aborted"; export type FinishReason = | "allShipsDestroyed" | "surrender" | "placementTimeout" | "turnTimeout" | "disconnect" | "bothDisconnected"; export interface GameOutcome { winner: PlayerId | null; reason: FinishReason; } export interface TimerInfo { kind: "placement" | "turn"; /** Чей таймер (для placement — оба, у каждого свой остаток). */ player: PlayerId; remainingMs: number; } export interface PauseInfo { disconnected: PlayerId[]; reconnectRemainingMs: number; } /** Последнее событие — для анимации на клиенте; состояние уже в проекции. */ export type GameEvent = | { kind: "attack"; attacker: PlayerId; cell: Cell; outcome: "miss" | "hit"; digit?: number; } | { kind: "opponentReady" } | { kind: "battleStarted" } | { kind: "paused" } | { kind: "resumed" }; export type ErrorCode = | PlacementError | AttackError | "wrongPhase" | "invalidMessage"; // ── Сообщения клиент → сервер (игровая комната) ───────────────────────────── export const CLIENT_MSG = { /** Финальная расстановка одним сообщением: SubmitPlacementPayload. */ SubmitPlacement: "placement.submit", /** Атака: AttackPayload. */ Attack: "battle.attack", /** Сдача партии (без payload). */ Surrender: "game.surrender", } as const; export interface SubmitPlacementPayload { ships: Cell[]; } export interface AttackPayload { cell: Cell; } // ── Сообщения сервер → клиент ─────────────────────────────────────────────── export const SERVER_MSG = { /** Полное обновление/восстановление: GameUpdate. */ Update: "game.update", /** Ошибка валидации действия: GameError. */ Error: "game.error", } as const; export const LOBBY_MSG = { /** Матч найден: MatchFoundPayload (резервация места в игровой комнате). */ MatchFound: "lobby.matchFound", /** Подтверждение постановки в очередь (без payload). */ Queued: "lobby.queued", } as const; export interface MatchFoundPayload { /** Seat reservation Colyseus — передаётся в client.consumeSeatReservation. */ reservation: unknown; } /** * Единственное сообщение состояния: отправляется после каждого изменения * и при реконнекте (сообщение восстановления). Содержит только проекцию * получателя — полное состояние партии по сети не передаётся. */ export interface GameUpdate { phase: GamePhase; you: PlayerId; /** Проекция боя; null до начала фазы боя. */ projection: PlayerProjection | null; /** Отправленная своя расстановка (для восстановления в фазе расстановки). */ submittedShips: Cell[] | null; placementReady: { you: boolean; opponent: boolean }; timer: TimerInfo | null; pause: PauseInfo | null; outcome: GameOutcome | null; lastEvent: GameEvent | null; } export interface GameError { code: ErrorCode; }