Космическая стратегия в реальном времени для браузера и Android.
Развивайте планеты, собирайте флоты, высаживайте армии и договаривайтесь с другими командирами. Война здесь — не только бой: маршрут, разведка, экономика и вовремя отданный приказ определяют, с какими силами вы встретите противника.
Браузерная сборка · Android-альфа · Документация · Состояние проекта
Аудиоэскизы: музыка и сигналы событий — материалы для прослушивания, пока не подключённые к игре.
Проект в активной разработке. В репозитории есть играбельные альфа-сборки; интерфейс, баланс и состав контента продолжают меняться. Наличие механики в дизайн-документе не означает, что она уже доступна игроку.
Этот монорепозиторий содержит основную стратегию и самостоятельную PvE-игру во вселенной Averion:
| Продукт | Направление |
|---|---|
| Averion: Galactic War | Сессионная космическая стратегия: развитие империи, войны и дипломатия, одиночные и сетевые матчи, корпорации. |
| Averion: Sector Zero | Самостоятельная PvE-игра с кампанией, собственным прогрессом и противником — Роем. Использует общее ядро, но не является форком или просто урезанным режимом основной игры. |
Историческое название проекта — Void Dominion. Имя репозитория MoonGame,
пакеты @void/* и файлы void-dominion*.html пока сохраняют прежние технические
идентификаторы. В командах ниже используются реальные имена из репозитория.
Матч идёт в непрерывном реальном времени, а не по ходам. На работающем сервере перелёты, строительство и бои продолжаются, даже когда игрок отключён. Приказы можно отдавать заранее; в одиночной игре доступны пауза и настройка скорости.
| Область | Что есть в текущей альфе |
|---|---|
| Карта и разведка | Провинции и маршруты между ними, туман войны, радары, шпионаж и контрразведка. Голографическая карта меняет детализацию при масштабировании. |
| Экономика и развитие | Ресурсы, постройки, производство, исследования с советом учёных, торговля на сессионной бирже. |
| Флот и армия | Корабли с модулями, герои, отдельные наземные дивизии и транспорт, орбитальные бои, бомбардировки и высадка десанта. |
| Приказы и дипломатия | Стоячие приказы, патрулирование, «Хранитель» для делегирования управления ИИ, объявления войны и взаимные соглашения. |
| Режимы и связь | Скирмиш с ботами, командные и сетевые матчи, чат и пинги на карте, интерфейс на русском и английском. |
| Между матчами | Прогрессия командира, арсенал, корпорации и войны корпораций. Кабинет корпорации подключён к серверному API; отдельные разделы ещё дорабатываются. |
Подробности механик — в описании игры. Реализация и известные ограничения — в снимке состояния; долгосрочный замысел — в GDD и документе метаигры.
Sector Zero развивает отдельную PvE-кампанию: операции, сюжетные миссии, герои и противостояние Рою. Его память, связь и адаптация — часть игрового дизайна, а не просто другое оформление обычного противника.
У игры своя сборка sector-zero.html. Общие правила мира развиваются в общем ядре;
миссии и прогресс кампании остаются на стороне Sector Zero. Состояние реализации и
планы собраны в роадмапе продукта, устройство Роя —
в документе его поведения.
В документах кампании есть сюжетные спойлеры. Сам README не раскрывает развязку.
Инструкции предназначены для участников проекта и пользователей, получивших разрешение правообладателя; условия использования приведены в LICENSE.
Нужны Git, Node.js 22 и pnpm. Версия pnpm закреплена в поле
packageManager файла package.json; Corepack использует её из
репозитория.
git clone https://github.com/MoonGameTechnology/MoonGame.git
cd MoonGame
corepack enable
pnpm install --frozen-lockfile
pnpm run prototypeСборка создаёт HTML-файлы в prototype/dist/:
| Файл | Назначение |
|---|---|
void-dominion.html |
Полный клиент для разработки и плейтестов, включая одиночную игру. |
sector-zero.html |
Отдельная сборка Sector Zero. |
void-dominion-player.html |
Сетевой клиент без одиночного скирмиша, тестового режима и управления ускорением времени. |
Для локального знакомства откройте prototype/dist/void-dominion.html или
prototype/dist/sector-zero.html в браузере. Для сетевого матча нужен запущенный
игровой сервер: статическая публикация HTML его не заменяет.
Из корня репозитория:
pnpm hostКоманда сама собирает клиент, запускает игровой хост и выводит адреса для
подключения. По умолчанию используется порт 8788; локальный адрес —
http://localhost:8788/. Хост слушает все сетевые интерфейсы, поэтому адрес из
локальной сети можно передать участникам плейтеста.
Это путь для разработки, не готовая конфигурация публичного сервера. Без
DATABASE_URL состояние хранится в памяти и теряется при перезапуске. Постоянному
сетевому серверу нужны Postgres, аутентификация, HTTPS/WSS и настроенное окружение.
Сценарии приглашения игроков и диагностика — в
ранбуке запуска. Docker Compose, TLS, резервное копирование
и эксплуатация — в руководстве развёртывания. Команда pnpm stack
запускает Compose, но не заменяет подготовку окружения по этому руководству.
Основной стек: TypeScript, pnpm workspaces, Node.js / Fastify / WebSocket, Postgres; для проверок — Vitest и браузерные сценарии Playwright. Android-сборка использует обёртку Capacitor.
packages/
shared-core/ Детерминированная симуляция и игровые модули
action-layer/ Валидация, авторизация и порядок действий
protocol/ Общий сетевой контракт клиента и сервера
server/ Комнаты матчей, транспорт, хранение и серверные сервисы
client/ Общие клиентские компоненты, транспорт, рендер и PWA-клиент
prototype/ Текущий игровой клиент, сборки и хост плейтестов
mobile/ Android-обёртка
decisions/ Общие чистые решения для клиентского поведения
data/ Игровые данные и контент
localization/ Тексты интерфейса
deploy/ Развёртывание и эксплуатация
scripts/ Проверки и вспомогательные инструменты
docs/ Дизайн, архитектура, состояние и планы
Название prototype/ историческое: именно здесь находится текущий клиент для
игроков. Пакет packages/client развивается рядом с ним, но не подменяет этот вход.
Важно различать серверные точки входа. pnpm host использует
prototype/netserver.ts. Dockerfile также собирает и запускает этот хост — как
packages/server/dist/proto-server.mjs. Команда pnpm dev:server запускает отдельный
вход packages/server/src/main.ts; изменение только этого файла не меняет поведение
контейнерного хоста. Подробная навигация — в CODE-MAP.md.
- Общее детерминированное ядро. Время и генератор случайных чисел передаются явно; одинаковые входные данные дают одинаковый результат симуляции.
- Данные и независимые модули. Контент описывается данными, правила расширяются модулями через события, хуки и возможности. Новая механика не требует переписывать всё ядро.
- Сервер определяет результат. В сетевой игре клиент отправляет намерение, действия проверяются сервером, а игрок получает только разрешённую ему проекцию состояния, включая ограничения тумана войны.
Подробности — в архитектуре, описании модульной системы и обосновании технического стека.
| Слой | Технологии | Зачем так |
|---|---|---|
| Язык | TypeScript везде, от ядра до клиента | Одно и то же ядро симуляции работает на сервере и в браузере без перевода между языками. |
| Ядро симуляции | Чистый TS, zod для схем данных, свой seeded RNG | Детерминизм: время и случайность передаются явно, Math.random() и Date.now() в ядре запрещены линтером. |
| Сервер | Node.js, Fastify (HTTP API), ws (WebSocket), jose (JWT) | Сырой ws даёт полный контроль над протоколом и рукопожатием; Fastify держит REST-часть рядом. |
| Хранилище | PostgreSQL + JSONB, драйвер pg без ORM | Состояние мира уже JSON и ложится в JSONB без преобразований; транзакции защищают от двойных начислений. |
| Уведомления | web-push, nodemailer | Push о событиях матча и письма восстановления пароля. |
| Клиент | Веб-клиент (PWA-first), карта на Canvas 2D | Одна кодовая база для браузера и мобильных; общее ядро даёт мгновенный предпросмотр приказа. |
| Мобильные | Capacitor (Android APK) | Тонкая нативная обёртка вокруг веб-клиента. |
| Инфраструктура | Docker (distroless-образ, non-root), Docker Compose, Caddy (TLS) | Минимальный образ без shell и пакетного менеджера; автоматические сертификаты. |
| Проверки | Vitest, Playwright, ESLint, Prettier | Модульные тесты рядом с кодом и сценарии в настоящем браузере. |
| CI/CD | GitHub Actions, cosign, набор сканеров безопасности | Гейт на каждый PR, подписанный образ для прода. |
Redis, очереди сообщений и микросервисная оркестрация сознательно не используются, пока игра живёт в одном процессе: меньше движущихся частей — меньше точек отказа. Условия, при которых стек расширяется, записаны в ADR стека.
Браузер / Android (Capacitor)
│ HTTPS — вход, профиль, список матчей, корпорации, магазин
│ WSS — игровой матч в реальном времени
▼
Caddy — TLS-терминация, заголовки безопасности
│
▼
Игровой хост (Node.js)
├─ Fastify: REST API, статика клиента, /health и /ready
├─ ws: соединения матчей, маршрутизация по id матча
├─ реестр матчей: много изолированных комнат в одном процессе
└─ драйвер часов: будит комнату к ближайшему запланированному событию
│
▼
PostgreSQL — снимки матчей, квитанции действий, аккаунты, мета-прогресс
Протокол описан один раз в пакете @void/protocol и импортируется обеими сторонами,
поэтому клиент и сервер не могут разойтись в формате сообщений. Клиент получает
только типы, серверный код в браузерную сборку не попадает.
Как живёт соединение.
- Клиент входит по логину и паролю (
POST /auth/login), выбирает матч и занимает место. Сервер выдаёт короткоживущий токен присоединения. - При открытии WebSocket сервер проверяет токен и
Origin, привязывает к соединению серверный идентификатор сессии и отправляетwelcomeс полным состоянием, видимым этому игроку. - Дальше идут дельты: только изменившиеся сущности и поля плюс доменные события.
Периодически к дельте прикладывается хеш состояния; клиент сверяет его со своей
копией, и при расхождении сервер присылает полный снимок (
state). - Приказы клиент шлёт конвертом
action.v1: идентификатор сессии, порядковый номерclientSeq, идентификатор действия и намерение. Ответ на отказ — стабильный код ошибкиE_*без внутренних деталей. - Пинги на карте, чат и служебные сообщения (
ping/pong, отчёт о рассинхроне) идут по тому же сокету отдельными типами сообщений.
Путь одного приказа:
сообщение → разбор и проверка формы (zod)
→ ActionGate: конверт → схема payload → сессия → идемпотентность → порядок clientSeq
→ комната матча: advanceTo(сейчас) → applyAction
→ атомарная запись снимка и квитанции в одной транзакции
→ рассылка: каждому игроку своя дельта с учётом тумана войны
- Комната матча (
MatchRoom) обрабатывает действия строго последовательно. Перед каждым приказом мир досчитывается до текущего момента, затем применяется приказ. Ядро — чистая функция: новое состояние строится на копии, исходное не меняется. - Сначала запись, потом рассылка. Результат приказа уходит игрокам только после того, как снимок матча и квитанция действия записаны в PostgreSQL одной транзакцией. Повтор того же действия возвращает сохранённую квитанцию, а не выполняется второй раз.
- Время без тиков. Ядро продвигает мир событиями: перелёт, стройка или бой — это запись в расписании, а ресурсы начисляются формулой за отрезок времени. Драйвер часов спит до ближайшего события, поэтому простаивающая партия почти не тратит CPU, а мир продолжает жить без подключённых игроков.
- Много матчей в одном процессе. Реестр держит изолированные комнаты. Матч без подключённых игроков выгружается из памяти и поднимается из базы к своему ближайшему событию или при первом подключении.
- Туман войны на сервере. Каждый игрок получает свою проекцию состояния: скрытые флоты, постройки и поля в его снимок и дельты не попадают вообще, а не просто не рисуются на карте.
- Отдельные сервисы. Корпорации и коммерция могут работать отдельными процессами со своими базами. Вызовы к ним идут через общий предохранитель зависимостей, поэтому отказ сервиса корпораций не останавливает матчи.
- Наблюдаемость. Пробы
/healthи/readyдля оркестратора, метрики для оператора закрыты токеном, журнал событий безопасности пишется отдельным потоком.
Действующий сервер для игроков собирается из prototype/netserver.ts (в образе — как
packages/server/dist/proto-server.mjs) поверх компонентов packages/server.
Ниже — общие принципы и меры, которые уже работают в коде. Подробные результаты аудитов в публичный README не выносятся.
- Авторитетный сервер. Клиент присылает намерение, а не состояние. Каждый приказ проходит проверку схемы, принадлежности сессии и игровых правил на сервере; результат боя, экономики и наград считает только сервер.
- Античит через детерминизм. Одинаковые входы дают одинаковый результат, поэтому спорную ситуацию можно воспроизвести на тех же данных. Расхождение хеша состояния у клиента фиксируется как сигнал.
- Идемпотентность и порядок. Квитанции действий хранятся долговременно, номер
clientSeqстрого растёт в пределах сессии: повтор или подмена порядка не дают второго списания или начисления. - Безопасный отказ. Любая ошибка — это отказ со стабильным кодом; внутренние детали остаются в серверных логах.
- Аккаунты. Пароли хранятся как хеши scrypt; сессии и токены присоединения — подписанные JWT с проверкой срока. Смена пароля отзывает выданные ранее сессии. Письма сброса пароля ограничены по частоте.
- Ограничения нагрузки. Лимиты частоты запросов к HTTP API, ограничение числа WebSocket-соединений с одного адреса, предел размера сообщения (с учётом распаковки сжатых кадров), квота на создание матчей.
- Веб-защита. Список разрешённых
Originдля WebSocket, заголовки безопасности и Content Security Policy на статике и API, HTTPS/WSS через Caddy. - Контейнер. Distroless-образ, процесс без root, базовые образы закреплены по дайджесту.
- Цепочка поставки. Каждый PR проверяют Semgrep, CodeQL, Trivy, OSV-Scanner,
Gitleaks, TruffleHog и zizmor (они же раз в неделю прогоняются по
main). Образ для прода не публикуется, если Trivy находит уязвимость с доступным исправлением; иначе он публикуется в GHCR и подписывается cosign без долгоживущих ключей; перед запуском хост проверяет подпись.
Модель угроз и подход — в документе по угрозам, дорожной карте честности игры и описании пайплайна. Как сообщить об уязвимости — в SECURITY.md.
| Workflow | Что делает |
|---|---|
ci.yml |
pnpm run check и тесты с настоящим PostgreSQL на каждый PR и в очереди слияния. |
security.yml |
Сканеры безопасности на PR и еженедельно по main. |
image.yml |
Сборка образа сервера, Trivy, публикация в GHCR, подпись cosign. |
android.yml |
Сборка Android APK через Capacitor. |
pages.yml |
Публикация браузерной сборки на GitHub Pages. |
automerge.yml |
Постановка готовых зелёных PR в очередь слияния. |
main защищён: изменения попадают туда только через PR с зелёным CI и merge queue.
| Команда | Назначение |
|---|---|
pnpm run check |
Основной гейт: ESLint, проверка типов, тесты и целостность документации. |
pnpm test |
Тесты Vitest. |
pnpm run prototype |
Сборка игровых HTML-файлов. |
pnpm run smoke:browser |
Проверка клиента в настоящем браузере. |
pnpm run smoke:sector-zero |
Браузерные сценарии Sector Zero и ракетных мин. |
pnpm run site |
Сборка и подготовка публичной статики в prototype/dist/site/. |
pnpm doctor |
Диагностика подключения к игровому хосту. |
Для браузерных проверок нужен Chromium; подготовка окружения описана в CONTRIBUTING.md. Проверки с Postgres требуют тестовую базу; CI поднимает её как сервис. Счётчики тестов и модулей не дублируются здесь — они живут в снимке состояния.
Изменения оформляются отдельным PR в main. Обязательные проверки выполняет
CI; постановкой готовых PR в очередь слияния управляет
штатный workflow репозитория. Правила участия — в
CONTRIBUTING.md, инженерные ограничения — в
CLAUDE.md, порядок работы агента — в AGENTS.md.
| Задача | Документ |
|---|---|
| Узнать, что реализовано и что ограничено | Готовность · Состояние проекта |
| Понять, что держит выход к игрокам | Путь к выпуску |
| Найти документ по теме | Индекс документации |
| Найти нужную часть кода | Карта кода |
| Выбрать следующую задачу | Роадмап · Бэклог |
| Разобраться с сетевой игрой | Мультиплеер · Запуск |
| Выкатывать сервер без ручных касаний | Zero Touch Prod |
| Разобраться с клиентами и платформами | Кросс-платформенный клиент |
| Проектировать корабли, персонажей, здания и визуальный стиль | Художественный фундамент PvP-игры |
| Работать над PvE-игрой | Sector Zero |
© 2026 Moonwuk Games. Все права защищены.
Проект проприетарный. Публичность репозитория не означает открытую лицензию на код, игровые данные, изображения или документацию. Условия и ограничения — в LICENSE.