BeamNG.Earth
BeamNG.drive용 1:1 비율 실시간 지구, 실제 고도·위성 이미지·부동 원점·보조 서버에서 LOD 스트리밍
브라우저 기반 다중 사용자 우주 전략, 식민지·에너지 균형·중개 없는 거래·함대·라운드 전투·언어 모델이 제어하는 봇 지휘관
Браузерная мультиплеерная космическая стратегия: колонии и энергобаланс, биржа без посредников, флот и раундовые бои, боты-командиры под управлением языковой модели
게임TypeScript · JavaScriptGitHub @kovalenkoserhii2107-maker온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Браузерная мультиплеерная экономическая стратегия в сеттинге космоса: колонии на планетах, добыча и энергобаланс, дерево технологий, верфь, логистика флота между системами, биржа за криптогривну без посредников, синдикаты со своим Кошем, модулями, технологиями, Брамами и пактами, PvE-экспедиции, колонизация свободных планет, шпионаж с лестницей раскрытия, боты-командиры под управлением языковой модели, рейтинг по вложенным ресурсам и раундовые бои со скорострелом и боевыми технологиями.README 전문 보기 ↗
Браузерная мультиплеерная экономическая стратегия в сеттинге космоса: колонии на планетах, добыча и энергобаланс, дерево технологий, верфь, логистика флота между системами, биржа за криптогривну без посредников, синдикаты со своим Кошем, модулями, технологиями, Брамами и пактами, PvE-экспедиции, колонизация свободных планет, шпионаж с лестницей раскрытия, боты-командиры под управлением языковой модели, рейтинг по вложенным ресурсам и раундовые бои со скорострелом и боевыми технологиями.
Играть: spacemmo.fly.dev — регистрация по email без подтверждения, вход через Google или из мини-приложения Telegram. С телефона игра сама предложит поставить себя на домашний экран.
Все расчеты — на сервере. Клиент только рисует состояние и отправляет намерения.
Стек: Node.js + TypeScript (strict, ESM) · Express 5 · Socket.IO ·
PostgreSQL (Prisma 7 + @prisma/adapter-pg) · jose для проверки id_token
Google · HTML/JS/CSS без сборщика · Docker.
Нужны Node.js 24 (как в Dockerfile) и доступная база PostgreSQL (подойдет Neon или локальный Postgres).
npm install
Скопируй пример окружения и заполни его:
cp .env.example .env
| Переменная | Обязательна | Назначение |
|---|---|---|
DATABASE_URL | да | строка подключения к PostgreSQL |
JWT_SECRET | в production | секрет подписи токенов сессии |
PORT | нет | порт сервера, по умолчанию 3000 |
GOOGLE_CLIENT_ID | нет | client id приложения Google. Пустой — кнопки входа через Google просто нет. Секрет не нужен: для проверки id_token хватает client id |
AUTH_EXPOSE_RESET_TOKEN | нет | отдавать код смены пароля прямо в ответе API. Только для стенда: роут открытый, и с этим флагом любой, кто знает чужой email, заберет аккаунт. В production игнорируется |
GEMINI_API_KEY | нет | ключ языковой модели для ботов. Пустой — боты играют по статичному характеру и не отвечают на письма; это штатный режим, а не авария |
GEMINI_MODEL | нет | какую модель звать, по умолчанию gemini-3.5-flash-lite |
Примени миграции, сгенерируй галактику и подними dev-сервер:
npx prisma migrate dev
npm run generate
npm run dev
Клиент откроется на http://localhost:3000 — там же регистрация, создание командира и выдача стартовой колонии.
docker build -t space-mmo . && docker run --env-file .env -p 3000:3000 space-mmo
Сервер живет на Fly.io в регионе fra, конфигурация — в fly.toml.
fly deploy --ha=false
Флаг --ha=false обязателен каждый раз. Без него Fly поднимает вторую машину
для отказоустойчивости, а этой игре она противопоказана: игровой цикл держит
состояние командиров в памяти процесса, и два экземпляра заведут два тика
со своими копиями баз — они начнут затирать друг друга сбросами.
По той же причине выключено автомасштабирование и запрещено засыпание: мир живет тиком, а не запросами игроков. На остановленной машине не идет добыча, не летят флоты и не ходят боты.
База стоит рядом с приложением. Сначала был Neon, но тик ходит в базу раз в секунду, и бесплатные 5 ГБ трафика в месяц выбирались за трое суток — база отказывала, мир вставал целиком. Платный тариф при таком профиле стоил бы около $19 в месяц (засыпание при простое нам не помогает, простоя не бывает), своя база на соседней машине — $3, и сеть уходит из горячего пути.
Памяти базе нужно минимум 512 МБ: умолчательные 256 МБ postgres-flex
не хватает, он держит Postgres, repmgr и монитор в одной машине.
Локальная разработка — на отдельной базе. Следствие того же правила про
один цикл: npm run dev на боевой базе дает второй тик поверх живого мира.
Нужна вторая база на том же кластере; до нее с ноутбука ходят через
fly proxy 15432:5432 -a spacemmo-db.
Миграции гоняются с ноутбука (npm run migrate:deploy), а не из образа:
prisma лежит в devDependencies, а рантайм собирается через npm ci --omit=dev —
CLI внутри просто нет. База доступна отовсюду, так что с ноутбука работает не хуже.
Игра ставится на домашний экран. manifest.webmanifest и иконки в public/icons/
дают свой значок и запуск без адресной строки; iOS читает манифест не полностью,
поэтому имя, значок и режим продублированы мета-тегами. Service worker есть, но
не кеширует ничего: он нужен затем, что без зарегистрированного обработчика fetch
Chrome не считает страницу устанавливаемой и не выдает beforeinstallprompt.
Кеша нет намеренно — на приложении без сборщика он дает несвежий app.js.
| Команда | Назначение |
|---|---|
npm run dev | dev-сервер с автоперезапуском (tsx watch) |
npm run build / npm start | сборка и запуск production-версии |
npm run typecheck | строгая проверка типов |
npm run audit | поиск мертвого кода: неиспользуемые переменные и параметры |
npm run audit:client | проверка public/app.js на необъявленные имена: файл не попадает в typecheck, а node --check вызов удаленной функции пропускает |
npm run forecast -- 7 | прогон недели на семи ботах настоящими формулами: чем мерить правку экономики |
npm run migrate:copy | перенос базы целиком между серверами: схему на приемнике создают миграции, порядок таблиц не важен (проверки ключей выключаются), в конце сверка по строкам |
npm run migrate:deploy | применить миграции к боевой базе (гоняется с ноутбука: prisma в devDependencies и в рантайм-образ не попадает) |
npm run generate | генерация галактики: 10-15 систем, планеты и торговые хабы. Размер задается аргументом — npm run generate -- 25 расширяет уже существующую галактику |
npm run check:rules | sanity-check игровых формул (добыча, энергия, бой, экспедиции) |
npm run prisma:migrate | миграции БД |
npm run prisma:generate | перегенерация Prisma-клиента |
npm run legacy:password | задать пароль аккаунту, мигрировавшему до появления авторизации |
npm run test:stand | подготовить стенд для тестов и напечатать их конфигурацию |
npm run test:cleanup | удалить одноразовые аккаунты тестов и вернуть их колонии в галактику |
npm run test:storage | тесты лимита склада, офлайн-добычи и формулы грабежа |
npm run test:commander | тесты симулятора, шаблонов флотов и старения разведданных |
npm run test:mail |
Аккаунт и командир. Регистрация и вход по email с паролем (scrypt с солью,
сравнение постоянным по времени), сессия — JWT: один и тот же токен для REST
и для рукопожатия WebSocket. Смена пароля одноразовым кодом с истечением; ответ
одинаков для существующего и несуществующего email. Код смены пароля наружу
не отдается: роут открытый, и вернуть код в ответе значит раздать любой аккаунт
тому, кто знает адрес владельца. На стенде это включается флагом
AUTH_EXPOSE_RESET_TOKEN=true, в production он игнорируется. Из внешних провайдеров
оставлен один — Google. Кнопку рисует Google Identity Services, id_token
проверяется на сервере по JWKS: подпись, издатель, срок, aud и обязательный
email_verified. Без GOOGLE_CLIENT_ID кнопки нет вовсе.
Второй провайдер — Telegram, и устроен он наоборот. Google опознается публичным
client_id, который уезжает в браузер; Telegram — секретом, токеном бота,
которым подписан initData мини-приложения. Поэтому токен лежит в отдельном
поле secret, а не в clientId: поля с этим именем отдаются клиенту.
Проверка целиком локальная, на node:crypto: из строки убирается hash,
пары сортируются и склеиваются переводом строки, ключ считается как HMAC
от имени «WebAppData» с токеном бота в роли ключа, сравнение хешей постоянное
по времени. Подпись живет сутки. Почты Telegram не дает вовсе, поэтому адрес
собирается служебный — tg-<id>@telegram.local. Клиент внутри Telegram входит
сам, без экрана логина. Apple и Facebook убраны намеренно — первый требует
платной подписки и ротации секрета раз в полгода, второй ревью приложения
с непредсказуемым сроком.
Аккаунты по совпавшему email не связываются: email при регистрации не проверяется,
поэтому кто угодно мог бы заранее занять чужой адрес и остаться в аккаунте после
того, как владелец войдет через Google. Аккаунт ищется по паре «провайдер + sub»,
а на занятый email вход через Google отвечает отказом с объяснением. Учетная запись отделена от игровой
личности: User отвечает за вход, Commander — за позывной, аватар, криптогривну
и счетчики боев. Достижения («Первый шаг», «Архитектор», «Гроза пиратов») выдаются
идемпотентно после сохранения состояния.
Ресурсы. Руда (основной строительный материал), Полимеры (электроника и высокие технологии), Плазма (топливо и реагент), Антиматерия (топливо гиперпрыжков) и Криптогривна как валюта. У каждого ресурса своя SVG-иконка: в интерфейсе рядом с цифрой стоит иконка, а название остается в подсказке — так строки стоимости и балансов читаются быстрее.
Иллюстрации. Карточка начинается с обложки: картинка объекта, под ней название,
а еще ниже метка — уровень, «В ангаре: 3» или «На позиции: 4». Метка стоит отдельной
строкой, а не рядом с названием: в строку они не помещаются, и «Колониальный транспорт»
рвался по слогам на три строки. Остальное содержимое идет ниже на всю ширину, поэтому
сетка карточек не зависит от размера картинки и сворачивается в одну колонку на телефоне.
Растровые иллюстрации лежат в public/assets/ — buildings/, ships/, defense/,
tech/ для карточек и planets/, systems/, bg/ для карт — и называются по типу
объекта в нижнем регистре (ore_mine.webp). Обложка карточки —
строгий квадрат, под ним название, уровень и стоимость; карточки в ряду держат
одну высоту, кнопка прижата к низу.
Пока файла нет, на месте баннера стоит заглушка с инициалами — верстка от этого
не ломается, и карточки не прыгают, когда картинка появляется.
Список ожидаемых файлов — в public/assets/README.md.
Центр управления. Вкладка по умолчанию и все о колониях в одном месте: паспорт активной базы, энергобаланс развернуто (выработка, потребление, остаток, мощность шахт), сводка четырех очередей разом, флоты в полете и уровни всех построек с технологиями. Паспорт раньше стоял над каждой панелью сразу: на широком экране это занимало полосу сверху, а на телефоне разворачивалось на весь экран, и до карточек шахт приходилось листать мимо него каждый раз.
Склад показан тремя ярусами на ресурс: название с текущей добычей, полоса, разбор под ней. Полоса разбита на два отрезка — несгораемый запас, который прячет само хранилище, и то, что вывезет победитель. Общей суммы по трем складам нет намеренно: лимиты поресурсные, и «занято 38 030 из 43 311» не отвечает ни на один вопрос — руда может стоять на полном складе с остановленной добычей, пока итог показывает свободное место.
Раскладка интерфейса. Экран разложен на три постоянные зоны и рабочую середину.
Сверху закрепленная шапка: командир с тегом синдиката и переключатель колоний
с координатами X:Y:орбита, по центру ресурсы: у каждого своя скорость
добычи, а на полном складе вместо нее краснеет «склад полон» — метка адресная,
потому что и склады раздельные. Справа центр связи с бейджем непрочитанного
и выход. Слева меню разделов по группам: развитие, армия, командование,
операции, инструменты и — только администратору — сервер. Справа сводка, которая
появляется на картах: карточка планеты, отправка флота и очередь полетов с таймерами.
В центре управления паспорт активной базы в три колонки: портрет планеты тем же артом, что и на карте, адрес и склад слева, богатство недр посередине, гарнизон справа. Богатство показано не голым множителем, а полосой со шкалой и словесной оценкой от «скудно» до «изобилия»: у множителя нет ни нуля, ни потолка, и сравнить его не с чем. Ожидание везде одно и то же: стройка, исследование, верфь и оборона показывают название, время до конца, полосу и строку деталей.
На узком экране раскладка складывается ступенями: правая сводка уходит под контент, строка ресурсов переезжает на вторую строку шапки, а на телефоне меню превращается в выезжающую панель с бургером и карточки встают в одну колонку.
Панель биржи. Три блока сверху вниз, и порядок в них — порядок решений. Сверху состояние рынка: цена каждого ресурса с изменением за сутки, обороты, число сделок, перекос спроса полосой и бартер отдельной карточкой; клик по цене разворачивает дневную динамику за 30 суток. В середине стакан: форма заявки с кнопкой «макс» (она считает по-разному для сторон — продажа упирается в остаток на складе, покупка в кассу и свободное место разом), фильтры по стороне, ресурсу и позывному, сортировка по любой колонке. Внизу журнал сделок с подкачкой по пятьдесят строк.
Стакан отсортирован по цене, а не по времени: он затем и нужен, чтобы лучшее предложение было первым. Новая заявка встает по своей цене и там же подсвечивается. Сторона сделки закодирована трижды — цветом, стрелкой направления и формой метки, — потому что одной заливкой в столбце одинаковых меток их не развести. Ресурс опознается значком, а не цветной точкой.
Темп игры. Экономика рассчитана на месяц до полного открытия контента: первая сессия 30–60 минут, дальше несколько коротких заходов в день. Добыча растет медленнее цены уровней, время постройки идет по степенному закону — первые уровни ставятся за минуту-две, поздние занимают часы и сутки. Главные ворота месяца — наука: поздние уровни технологий изучаются днями. Флот при этом основная инвестиция: эскадра из двадцати линкоров стоит суток добычи, поэтому терять ее больно, а базу хочется проверять.
Опорные точки: истребитель — первый час, крейсер — вторые сутки, фрегат — четвертые, колонизация и бомбардировщик — вторая неделя, линкор — третья, авианосец и «Перун» — конец месяца.
Колонии и экономика. Планеты с индивидуальными коэффициентами руды, полимеров,
плазмы и инсоляции. Одиннадцать типов построек:
три шахты, энергостанция, научный центр, верфь, фабрика антиматерии,
крипто-ферма и три раздельных хранилища. Здания тратят энергию: при дефиците
добыча всех шахт падает пропорционально (efficiency = output / usage).
Крипто-ферма — единственная постройка, производящая деньги, и конкурирует
с шахтой за ту же энергию. Дерево из пятнадцати технологий
с иерархией требований: энергетика, вычислительная техника, оружейная, щитовая
и бронебойная, горное дело, реактивный двигатель, гиперпространственная физика,
гипердвигатель, астрофизика, робототехника, криптоинженерия, бункерование,
шпионаж и сжатие времени. Верфь строит корабли и стационарную оборону
собственными очередями.
Тиковый движок. Стройка, исследования и очереди считаются от абсолютных меток времени: процессы идут, пока игрок офлайн, и переживают перезапуск сервера. Добыча за офлайн (до 24 ч) начисляется по отрезкам между завершениями таймеров. Тик раз в секунду, пакетная запись в БД, push состояния клиенту по WebSocket.
Карта системы. Круговая микро-карта: звезда или черная дыра в центре, планеты
на концентрических орбитах, торговый хаб на своем кольце и точка выхода в глубокий
космос снаружи. Тела берут картинки из assets/planets/ по биому, пока файла нет —
цветной круг. Размер планеты зависит от ее типа: газовый гигант крупнее всех,
скалистый огрызок мельче всех, и по карте видно, где добыча богаче, еще до разведки.
Орбиты начинаются у самой звезды, а их шаг зависит от числа планет: система из трех
выглядит компактной, из пяти — просторной, и тела никогда не наезжают друг на друга.
Название стоит под планетой, а своя колония отмечена не кольцом, а зеленой точкой
под подписью. Звезда, черная дыра, туманность и станция хаба не обрезаются кругом
и растворяются в фоне через mix-blend-mode, планеты остаются непрозрачными шарами
с круглой обрезкой. Фон карты — тайл звездного поля под сильным затемнением: яркая
подложка съела бы свечение тел.
Карта галактики. Системы нарисованы миниатюрами галактик и туманностей
из assets/systems/ — четыре варианта, и вариант выбирается по координатам, чтобы
система не меняла вид при каждой перерисовке. Шаг сетки одинаков по обеим осям,
а размер холста считается от разброса координат: расстояние между системами на карте
соответствует расстоянию между ними в игре, а не форме панели. Клик по системе
открывает ее микро-карту — с тем же туманом войны, что и своя.
Информация при наведении. Рядом с телом разворачивается HUD-карточка: сначала от него протягивается линия, затем на ее конце проявляется панель — около 0.4 секунды на все. Цифры разложены по сетке, а не в столбец, поэтому карточка не растет вниз на пол-карты; постройки в ней опущены — полный разбор открывается кликом в панели справа. Наведение на звезду показывает ее класс, температуру и число планет; у черной дыры температуры нет. На сенсорном экране карточка не показывается: наведения там нет, а панель закрыла бы саму карту.
Туман войны и логистика. Своя планета видна целиком и в реальном времени, чужая — только название, орбита, тип и размер; после прилета зонда сохраняется стареющий снимок разведки. Скорость флота задает самый медленный корабль, грузоподъемность и топливо считаются по составу и времени в пути. Руда, полимеры и плазма делят один трюм: плазма и топливо, и товар, поэтому колонии перебрасывают его между собой обычным транспортом. Внутри системы флот идет на плазме, между системами — гиперпрыжком на антиматерии, и прыжок требует «Гипердвигателя». Топливо берется за дорогу туда и обратно; рейс без возврата платит только за один конец. Прилеты обрабатываются глобальным сканом: груз доходит и корабли возвращаются, даже если обе стороны офлайн.
Отправка флота. Цель выбирается кликом по круговой карте или вводом координат «система X:Y и орбита» — по ним цель находится и в чужой системе, которую карта еще не открывала. Дальше панель ведет по трем шагам.
Первый — действие: не выпадающий список, а меню кнопок. Действий у цели три-четыре, они разные по смыслу, и от выбора зависит остальная форма; в свернутом списке игрок не видит, что вообще можно сделать с планетой. Набор решает не наличие владельца, а состояние планеты, и состояний четыре:
| Планета | Действия |
|---|---|
| своя колония | транспортировка, дислокация |
| чужая колония | атака, отправить груз или помощь, шпионить зондом |
| свободная | разведка, основать колонию (с основателем в составе) |
| не разведана | разведка, атака, транспорт, колонизация |
Последний случай отдельный: у неразведанной планеты владелец скрыт туманом войны, и пустой владелец там не значит «свободна». Пункты в ней не мертвые, а именно неизвестные — игрок может знать о планете от союзника, и сервер откажет, только если догадка не подтвердится. По умолчанию подставляется безопасное действие, а не первое в списке: у чужой колонии первая — атака, и она объявляет войну.
Второй шаг — состав: поля по классам кораблей с остатком на базе, кнопки «Выбрать все» и «Очистить», плюс сохраненные шаблоны. Третий — груз и расчет маршрута: дистанция, время, топливо с остатком на складе и занятость трюмов; нехватка подсвечивается до вылета. Справа же очередь активных полетов с таймерами до прибытия.
Миссии: транспортировка, разведка зондом, атака, дислокация, доставка и вывоз на хабе, экспедиция, переработка обломков и колонизация. Переработка появляется, только когда над планетой висит поле и в составе есть переработчик.
Война объявляется атакой. Отдельный поход в раздел дипломатии больше не нужен: если войны с целью нет, вылет объявляет ее сам. Членство в синдикате личную войну не запрещает — запрет приводил к тому, что кнопка «Атака» у половины игроков давала отказ вместо боя. Вместо запрета предупреждение: предпросмотр маршрута говорит, что войны нет и что синдикат цели может ответить всем составом. Считает его сервер — только он знает дипломатию, а отдавать ее клиенту ради строки текста незачем.
Помощь союзнику. Транспорт на чужую колонию можно отправить с флажком «без возврата»: тогда у получателя остается не только груз, но и сами корабли — так передают флот. Обычный рейс, как и раньше, выгружает груз и возвращается. Атака и разведка возвращаются всегда: флот, брошенный у чужой колонии, достался бы противнику даром.
Колонизация. Расширяться можно одноразовым «Колониальным транспортом»: он садится на свободную планету и разбирается на первую инфраструктуру новой базы. Рейс односторонний — конвой и груз остаются колонии, обратно летит только тот, кто не понадобился. Сколько колоний можно держать, задает «Астрофизика»: одна на старте и еще одна за каждые два уровня. Свобода планеты и наличие слота проверяются дважды — при вылете и на прилете, потому что за время полета планету могли занять. Сорвавшийся рейс возвращает флот целиком и даром: обратный путь при вылете не оплачивался. Причина всегда приходит письмом.
Арсенал. Двенадцать классов кораблей и пять классов обороны с украинскими позывными. Легкая линия — «Око», «Чайка», «Чумак», «Сокіл»; средняя — «Гайдамака», «Отаман», «Характерник»; лейтгейм — «Булава», «Гетьман» и «Січ». Оборона идет от массового «Града» через «Промінь» и «Скіф» к батарее «Сварог» и ультимативному излучателю «Перун».
Роли разведены матрицей скорострела: тяжелый истребитель косит мелочь, крейсер — истребителей, линкор — крейсера и бомбардировщики, авианосец — весь легкий и средний класс, а бомбардировщик разбирает планетарную оборону очередями. По «Перуну» очереди нет ни у кого: ультимативную установку берут только массой.
Сила «Перуна» — не корпус, а щит: 3000 восстанавливаются к каждому раунду, и эскадра с суммарным залпом ниже этого числа не наносит ему вообще ничего. Восемь линкоров бьют на 8000, но теряют по кораблю в раунд и к шестому уже не пробивают щит: бой кончается ничьей, а она остается за защитником. Порог пробоя — двенадцать линкоров на один излучатель.
Лаборатории колоний. Технологии общие для всех колоний, и изучается одновременно одна. Лаборатория любой другой колонии может присоединиться к идущему исследованию и взять на себя его долю: лаборатория того же уровня, что ведущая, или выше — половину, слабее — долю от половины по отношению уровней (шестая при ведущей двенадцатого — четверть). Взятая доля срезается со всего срока исследования и доплачивается той же долей всей его цены со склада помогающей колонии. Опоздавшая берет только остаток и платит только за него. При отмене каждая база получает назад свое.
Технологии скорости. «Робототехника» ускоряет постройку зданий и сборку кораблей с обороной — на исследования она не влияет, там работает уровень лаборатории. «Сжатие времени» открывается в самом конце дерева и каждым уровнем сокращает вдвое вообще все сроки: стройку, верфь, оборону и науку. Плата — энергия: каждый следующий уровень потребляет вдвое больше предыдущего, а дефицит энергии режет добычу всей базы. На средней энергетике включенное сжатие второго уровня роняет доход втрое, на развитой не отнимает ничего — поэтому ускорение окупается только тому, кто заранее вложился в станции.
Антиматерия и аномалии. «Фабрика антиматерии» — самая дорогая постройка с огромным расходом энергии, синтез идет на порядки медленнее обычной добычи. Аномалия «Черная дыра» в 1-2 системах дает «Искажение времени»: синтез антиматерии +50%, стройка (включая верфь) и исследования на 30% дольше.
Биржа и криптогривна. Торговый хаб — нейтральная станция на орбите у звезды. Товар попадает на склад только транспортным флотом, что не влезло — возвращается домой в трюмах; обратный рейс забирает купленное домой, где оно и превращается в постройки. Расширение склада на хабе платится криптогривной.
Посредников нет: станция не покупает и не продает ничего, торгуют между собой игроки. Встречные заявки сводятся автоматически и по средней цене: продажа по 10 против покупки по 12 исполняется по 11 на меньший из объемов, остаток большей заявки висит по своей прежней цене. Середина делит выигрыш поровну — отдать сделку по цене одной из сторон значило бы подарить всю разницу тому, кто выставился вторым. Комиссия 0.5% с продавца и 0.6% с покупателя.
Цену назначает рынок: она средневзвешенная по объему за последние двадцать сделок. Затравочные 10 за руду и 14 за полимеры участвуют ровно один раз — пока сделок нет вовсе. Рядом со стоимостью показан перекос стакана: спрос выше предложения — цена пойдет вверх, и вкладываться выгоднее в добычу именно этого ресурса.
Кроме ордеров есть бартер — обмен ресурса на ресурс без денег. Предложение берется целиком: дробить обмен незачем, а частичное исполнение усложнило бы расчет вдвое ради удобства, которое достигается вторым предложением.
Криптогривна берется единственным способом — с крипто-фермы, а уходит пятью стоками (см. ниже). Все сделки между игроками это переводы, поэтому массу держат кран и стоки, а не эмиссия. Ферма конкурирует с шахтой за одну и ту же энергию и дает на единицу энергии меньше, чем шахта, если добытое удается продать, — это выбор между деньгами сразу и сырьем с логистикой. Равновесие держится само: отдача фермы задана в гривне и от цен не зависит, поэтому дешевеющий товар делает ферму выгоднее, а дорожающий — шахту.
Денежная масса держится на одном кране и пяти стоках. Кран — крипто-ферма, единственная постройка, производящая деньги. Стоки: комиссия биржи, разовое расширение склада на хабе, постоянная плата за место на нем, спешка и траты казны синдиката на Кіш — основание, модули, технологии, Брамы и оборону.
Плата за хаб берется за вместимость, а не за занятое: место арендуется целиком, пустая полка стоит столько же, сколько полная. Первый уровень бесплатен. Не хватило на всю плату — не списывается ничего: долгов в игре нет. Собирается раз в минуту по всем командирам сразу, включая офлайновых.
Спешка — единственное, на что криптогривну тратят по своей воле: стройку можно доделать немедленно за деньги. Цена привязана к рыночной стоимости самой работы и к доле оставшегося времени, поэтому дорожает вместе с инфляцией сама — любая постоянная цена устарела бы за сутки.
Отдача фермы и ставка аренды подобраны вместе: это один рычаг на две константы.
Мерить такую правку положено прогоном (npm run forecast -- 7), а не на глаз.
Биржа общая на весь сервер. Хаб есть в каждой системе, но стакан, цены, бартер и журнал сделок одни на всех, и боты торгуют со всеми. Привезенное флотом лежит на складе того хаба, куда его привезли, и забирается только там; купленное и полученное обменом ложится на общий склад, который забирается с любого хаба. Размер склада и аренда — одни на все хабы. Продажа берет товар сначала со склада хаба своей системы, потом с общего, а снятая заявка возвращает его туда, откуда он ушел, — бесплатно переслать ресурсы между системами нельзя.
Синдикаты. Название, тег, описание, главарь и состав; основание стоит криптогривну. Вместо трех ролей — ранги: главарь создает до восьми, у каждого место в иерархии и набор прав (заявки, исключение, назначение рангов, рассылка, выдача из казны с дневным лимитом, налог, кодекс, правила набора, дипломатия, развитие Коша, Академия). Действовать можно только на тех, чей ранг ниже своего. Кодекс кандидат читает и принимает до вступления; набор бывает открытым, по заявке и закрытым, с минимальным рейтингом и вступительным взносом. Налог с крипто-фермы (до 30%) уходит в казну; повышение вступает в силу через сутки и приходит письмом. Атаковать участников своего синдиката нельзя, после выхода вступить в другой можно через сутки, а при роспуске казна сгорает. Раздел «Синдикат» — про людей и деньги: шапка с описанием, казна со сводкой поступлений и расходов за неделю, вклад каждого участника, журнал с фильтром, устав, состав и ранги.
Кіш — хаб синдиката, стоящий в системе главаря. Он выбирается в переключателе колоний, после счетчика колоний, и разделы игры показывают его сторону: «Мостик» (тревоги, казна, состояние, перенос), «Отсеки» (модули), «Академия» (технологии синдиката), «Флотилия» (караул и налеты в пути) и «Бастион» (оборона), а в шапке видна казна. На первом уровне Кіш дает три места в составе, дальше по одному за уровень. Ресурсная казна хранит руду, полимеры, плазму и антиматерию; привозят их рейсом «Доставка в Кіш» (любой участник, в заслуги), вывозят рейсом «Вывоз из казны» (ранг с правом выдачи, в общий с гривной дневной лимит). Флот к Кошу можно отправить прямо с его карты, выбрав колонию вылета.
Модули Коша строятся из казны по времени, одна стройка на синдикат: цена списывается при запуске, отмена возвращает ее целиком, уровень меняется по завершении. Карточки модулей и технологий раскрываются таблицей уровней вперед с ценой и сроком. Скарбниця бережет при налете 20% ресурсной казны и по 5% за уровень, не больше 80%. Академия открывает технологии синдиката: добыча, скорость стройки, трюмы, комиссия биржи — по 3% за уровень; несгораемая доля склада; контрразведка +1 к «Шпионажу» в обороне за каждые три уровня; «Инженерный корпус» — стройка в Коше и изучение на 10% быстрее за уровень. Каждый уровень технологии втрое дороже предыдущего, уровень не выше уровня Академии, изучение одно за раз, бонусы — участникам, пробывшим в синдикате двое суток. Дозор показывает участникам вражеские атаки на них в радиусе от Коша; разведку он не видит и состав флота не раскрывает. Брама — врата в системах с колониями участников: между системами со своими Брамами флот прыгает без «Гипердвигателя» и за треть антиматерии, не больше 200 кораблей в час за уровень врат. Кіш переносится к своей Браме за антиматерию казны (500 за единицу расстояния), не чаще раза в сутки.
Война синдикатов. Удержание — флот встает на орбиту колонии участника или у своего Коша на 1, 4, 8 или 24 часа и защищает ее в бою; уцелевшие делятся по вкладу, отозвать флот можно раньше срока. Налет на Кіш — атака чужого Коша от лица синдиката, только в войне синдикатов (если ее нет, вылет объявляет войну, нужно право дипломатии). Кіш защищают оборона из казны и караул на удержании; победитель уносит до 90% незащищенной руды, полимеров и плазмы в пределах трюмов, гривну не грабят. Разбитые корабли оставляют у Коша осколки, их собирает переработчик. Пока к Кошу летит налет, переносить его нельзя. Совместная атака — флоты синдиката и союзников на одну колонию прилетают одновременно и дерутся одним боем (до 8 флотов); уцелевшие и добыча делятся по вкладу и трюмам.
Пакты — ненападение, союз и торговое соглашение. Предлагает и принимает ранг с правом дипломатии, расторгнуть может любая сторона, но пакт действует еще сутки, и об этом узнают все участники. Под ненападением и союзом нельзя атаковать другую сторону, налетать на ее Кіш и объявлять ей войну; мириться пактом во время войны нельзя. Союз вдобавок открывает Брамы союзника, показывает его колонии в Дозоре и позволяет присоединяться к его атакам. Торговое соглашение вдвое снижает комиссию биржи между участниками двух синдикатов и возможно даже во время войны.
Рейтинг. Счет командира — это ресурсы на руках плюс стоимость всего, что построено и не потеряно: флот (в том числе тот, что сейчас в полете), оборона, здания и технологии. Здания и наука считаются суммой по всем уровням с первого: десятый уровень стоил владельцу и всех девяти предыдущих. Считается наличие, а не история трат — потерянный в бою флот из счета уходит, иначе таблица превратилась бы в список тех, кто дольше играет. Все ресурсы идут один к одному по единицам. Рядом с таблицей своя строка с разбивкой по слагаемым: по ней видно, чем игрок силен. Синдикаты ранжируются четырьмя таблицами: общий счет (состав плюс вложенное из казны в Кіш, модули и технологии), военный (уничтоженный флот противника), экономический (взносы и налог в казну) и научный (сумма уровней технологий синдиката). Военный и экономический счета копятся в момент события и не переходят с участником в другой синдикат.
Экспедиции и войны. Раздел со сводкой командира сверху — победы с долей, поражения, войны, занятые слоты экспедиций, достижения — и вкладками «Бои», «Экспедиции», «Дипломатия», «Достижения». Бои идут короткими строками с фильтром и раскрываются полным отчетом, экспедицию можно отправить одной кнопкой — карта откроется уже с глубоким космосом целью, дипломатия синдиката разложена карточками войн, пактов и синдикатов галактики.
Экспедиции (PvE). Вылет в «глубокий космос» — абстрактную 16-ю позицию, которая есть у каждой системы. «Астрофизика» задает число одновременных экспедиций, повышает находки и дает шанс уйти от засады, а заодно открывает колониальный транспорт и слоты под колонии. Событийный движок бросает кубик по весам: мертвая тишина, заброшенный груз, уклонение или бой с пиратами. Генератор случайных чисел передается снаружи, поэтому любой исход воспроизводим в тестах.
Три хранилища, по одному на ресурс. «Рудный склад», «Склад полимеров» и «Плазмохранилище» строятся и качаются независимо. Уровень 0 — колониальный резерв на 2 500, уровень 1 дает 3 500, дальше вместимость растет в 1.5 раза за уровень. Полный склад останавливает добычу только своего ресурса — и в тике, и в догоне офлайна, так что неделя отсутствия не приносит больше, чем влезает. Антиматерия хранится в магнитных ловушках и под лимиты не попадает вовсе.
Общий лимит на три ресурса был здесь раньше, и он создавал состояние без выхода: обильный ресурс занимал место, дефицитный переставал добываться вместе с ним, а все постройки требовали именно дефицитного. Выбраться можно было только вывозом излишка в хаб — то есть при уже построенных транспортниках, которых у новичка нет. Раздельные лимиты убирают саму возможность такого состояния.
Переполнить склад сверх лимита все же можно — возвратным рейсом, трофеями экспедиции или отменой биржевого ордера; такие ресурсы не пропадают, но и не защищены.
Поля обломков. Бой оставляет экономический след: 30% стоимости уничтоженных кораблей — и нападавшего, и защитника — оседает обломками на орбите планеты. Разбитая оборона обломков не дает: она восстанавливается на месте, а не улетает в поле. Поле видно всем игрокам и не скрывается туманом войны, поэтому за крупным полем начинается гонка. Собрать его может только «Переработчик» — безоружный корабль с трюмом на 20 000, который ползет медленнее всех и жжет плазма как десять транспортов. Миссия «Переработка» доступна над любой планетой, даже чужой и необитаемой. Обломки достаются тому, чей флот долетит первым: списание атомарно, и опоздавший либо добирает остаток, либо возвращается пустым — о чем прямо пишет отчет, иначе исчезнувшее поле выглядело бы багом.
Боты-командиры. Бот — обычный командир в БД: своя учетная запись с ролью
BOT, стартовая колония, место в рейтинге. Войти в его учетную запись нельзя
ни паролем, ни через Google. Ходит он теми же методами игрового цикла
и сервисов, что и живой игрок, поэтому платит те же цены и ждет те же таймеры.
Характеров два: торговец качает экономику и торгует, агрессор растит флот
и расширяется — но война для него средство, а не занятие.
Решения идут двумя контурами. Рутину ведет код: раз в 45 секунд чистая функция «снимок → намерения» выбирает, что достроить, дозаказать и куда отправить флот. Языковая модель зовется редко — раз в два часа по расписанию или когда случилось нестандартное: бой, объявленная война, потеря колонии, резкий сдвиг рынка. Вызов на каждый заход стоил бы почти два миллиона токенов в сутки против четырех тысяч и отдал бы модели арифметику, которую код считает точнее.
Модель задает курс — доли бюджета, порядок науки, приоритеты застройки, состав эскадры — и отдает разовые поручения: набег, мир, заявка на бирже, колонизация, сбор обломков, письмо игроку. Поручение это просьба, а не исполнение: выполняет его тот же игровой цикл, поэтому правила остаются в силе. Проверяются поручения по снимку — модель может распорядиться только тем, что ей показали.
Границы модель не отменяет. Щит новичка: бот не трогает живых командиров младше трех суток ни при какой формулировке (на других ботов щит не распространяется — иначе мир замирает). Пол безнадежности: лететь на цель втрое сильнее нельзя. Доля развития не опускается ниже трети — иначе флот съедает базу, которая его кормит.
Против серийного агрессора соседи скидываются сами: три набега за сутки по одной жертве делают из соседа угрозу, и любой бот рядом сдвигает бюджет во флот с обороной и летит на него. Договора между союзниками нет — согласие это когда сосед действительно поднял флот, а не строка в базе. Жертва рассылает призыв письмом, и живым игрокам оно тоже приходит: это единственный способ узнать, что рядом кто-то зарвался.
Норма обращений к модели общая на всех ботов и считается по попыткам: отказ сервиса стоит у провайдера столько же, сколько удачный ответ. Потолок переписки стоит в коде, а не в просьбе к модели, — шесть писем в сутки на бота и два одному адресату. Без ключа все работает: бот играет по статичному характеру и не отвечает на письма, и это штатный режим, а не авария.
Панель администратора. Отдельная роль ADMIN на учетной записи и защищенный
раздел /api/admin/*: список командиров с поиском по позывному, полный стейт игрока
и принудительная правка ресурсов, криптогривны, зданий, технологий, флота и обороны.
Значения выставляются целиком, а не прибавляются — в форме уже стоят текущие числа,
и повторная отправка не удваивает выданное. Каждое изменение пишется в лог сервера.
Вкладка появляется только у администратора, но доступ решает сервер: запрос обычного
игрока получает 403 независимо от интерфейса. Роль выдается скриптом
npm run admin:grant -- <email>, игрового пути к повышению прав нет.
Сверху сводка по серверу: сколько игроков сейчас в сети (по живым сокетам, а не по времени последнего входа), сколько заходило сегодня и за неделю, сколько новых, колоний, флотов в полете и синдикатов. У каждого игрока — карточка учетной записи: почта, способ входа, даты регистрации и последнего входа, состояние доступа.
Действия над аккаунтом отделены от правки игровых чисел: у них разные последствия,
и складывать их в одну кнопку «Сохранить» значит однажды заблокировать игрока,
поправляя ему руду. Пароль пульт не задает и не показывает — админ выдает
одноразовый код, а новый пароль игрок вводит себе сам. Блокировка закрывает вход
и игровой канал, но не трогает колонии: разблокированный находит мир таким, каким
оставил. Удаление необратимо и требует ввести позывной — каскад уносит колонии
(планеты освобождаются), флоты и синдикат, если игрок им руководил.
Администратора нельзя ни заблокировать, ни удалить. Письмо от гейм-мастера
приходит игроку отдельным типом ADMIN, чтобы не теряться среди автоотчетов.
Центр связи. Внутриигровая почта с фильтрами по типам писем. Личные сообщения отправляются по позывному, ранги синдиката с правом рассылки рассылают объявление всему составу разом. Все системные отчеты приходят сюда же: бой, разведка, экспедиция и логистика доставляются письмами от «Центра связи». Доставка груза уведомляет обе стороны — отправитель видит, что выгрузился, владелец колонии узнает, кто прислал ресурсы, иначе они появлялись бы на складе молча. Флот, вернувшийся с добычей, тоже пишет; пустой возврат молчит, о таком рейсе уже был свой отчет. Один бой дает два зеркальных письма — атакующий читает про свои трофеи, защитник про свои потери. В шапке висит иконка почты с бейджем непрочитанного, который обновляется через Socket.IO в момент доставки, а не по опросу. Ящик строго адресный: чужое письмо нельзя ни прочитать, ни удалить даже по прямому идентификатору.
Боевой отчет. Письмо о бое разворачивается не текстом, а полноценным отчетом: исход (победа, поражение или ничья) глазами того, кто его открыл, место боя с системой и орбитой, дата. Дальше итоги крупно — сколько обломков осело на орбите и сколько ресурсов вывезено, — и потери обеих сторон таблицей: было, потеряно, осталось, с иконкой каждого класса. Сторона, от которой ничего не осталось, подписывается «Флот уничтожен». Тем же компонентом рисуется предпросмотр в симуляторе, поэтому прогноз и настоящий отчет читаются одинаково. Пораундового лога пока нет: сводки «было / стало» хватает, чтобы понять итог.
Инструменты командира. Боевой симулятор прогоняет любой состав атакующих против любого флота и обороны защитника через тот же боевой движок, что и настоящий бой, и ничего не пишет в базу: видно потери, пробитие слоев и предполагаемую добычу с учетом хранилища и трюмов уцелевших. Состав защитника подставляется из отчета разведки одним выбором. Шаблоны флотов — сохраненные составы («Фарм-отряд»: 10 крейсеров, 5 транспортов), которыми заполняется форма отправки; шаблон только подставляет числа, наличие кораблей проверяется при вылете.
Старение разведданных. Снимок зонда не обновляется сам, поэтому у него есть срок годности: до часа данные свежие, от часа до суток помечаются как неточные, а после суток сервер вообще не отдает флот, оборону и склад — остаются только уровни построек, владелец и богатство планеты. Возраст снимка виден рядом с цифрами, чтобы атаку не планировали по вчерашней разведке.
Шпионаж решает разница уровней, а не свой уровень. Технология сразу наступательная и оборонительная: вложившийся видит больше и показывает меньше, поэтому нужна она и торговцу — для него это контрразведка. Зонд без нее не собрать вовсе.
Лестница раскрытия по разнице d между уровнем шпиона и уровнем цели:
d | дрон | что привозит |
|---|---|---|
| −2 и ниже | сбит (99%) | ничего |
| −1 | 50% сбит | только постройки и недра |
| 0 | цел | флот числом; склад числом с шансом 4/5 |
| +1 | цел | и оборона числом |
| +2 | цел | и оборона по типам |
| +3…+4 | цел | флот и склад по составу |
| +5 и выше | цел | и уровни технологий |
Заметность — та же разница с другой стороны: при перевесе +1 цель замечает пролет в 70% случаев, +2 — 40%, +3 — 10%, +4 и выше — 1%. Содержание тревоги тоже растет: от «кто-то пролетал» до «кто, откуда, когда и что успел прочесть». Абсолютов нет ни в одну сторону — джокер в один процент оставляет возможным и провал непобедимого, и удачу обреченного.
Броски делаются в момент пролета, и их итог ложится в снимок навсегда: уничтоженный дрон не может задним числом привезти данные, а выученная позже наука не улучшает старый снимок. В письмо кладется только то, до чего дотянулся зонд, — где положено видеть одно число, приходит число, а не состав.
Отчет разведки. Не текст, а разбор: портрет планеты тем же артом, что на карте, недра шкалой, склад значками ресурсов, флот и оборона плитками с картинками кораблей и установок, инфраструктура по уровням, технологии ярлычками. Ступень, до которой дотянулся зонд, стоит меткой в шапке — по ней видно, почему отчет неполон, без чтения строк. Пустое место всегда объяснено причиной, и причины разные: «зонд не дотянулся» чинится уровнем технологии, «планета не укреплена» приглашает напасть. Тем же компонентом рисуется тревога о чужом зонде над своей колонией.
Бой. Раундовая симуляция: до шести раундов, каждый живой юнит раз в раунд бьет по случайной цели. У юнита три характеристики — атака, щит и корпус. Щит поглощает урон первым и восстанавливается к следующему раунду, корпус не лечится; юнит гибнет, когда корпус уходит в ноль. Отсюда главное правило боя: одиночный слабый залп вязнет в щите, но десять подряд продавливают его в том же раунде. Ионный фрегат снимает щиты с двойным множителем — его роль вскрывать защиту тяжелых кораблей, а не убивать их.
Скорострел решает обратную задачу: после выстрела юнит с шансом (N-1)/N стреляет
снова и снова, пока очередь не оборвется, — так тяжелый корабль успевает выкосить
рой мелочи за один раунд, а не разменивать себя на нее по одной цели. Матрица
односторонняя: крейсер бьет очередью по истребителям (N=10), зондам и транспортам
(N=5), ионный фрегат — по лазерным турелям (N=8) и той же мелочи, истребитель —
по пушечным турелям (N=3). У обороны скорострела нет. Отсюда состав флота начинает
значить больше числа: крейсер уверенно берет равный по стоимости рой истребителей,
но вязнет в лазерных турелях, где тот же бюджет фрегатов проходит насквозь.
Три технологии масштабируют характеристики: оружейная поднимает атаку, щитовая — щиты, бронебойная — корпус, каждая на +10% за уровень, и все три действуют и на флот, и на оборону. «Энергетика» на бой больше не влияет — она осталась экономической веткой и пререквизитом для фрегата и реактора.
Побеждает тот, у кого противник кончился; если после шестого раунда живы обе стороны — ничья, и поле остается за защитником. Разбитая оборона восстанавливается с шансом 70%. Победивший атакующий грабит склад по механике «сейфа»: хранилище прячет пятую часть своей вместимости, а с уязвимого излишка сверх этого порога агрессор забирает 90% пропорционально каждому типу ресурса — руда, полимеры и плазма одинаково. Защита поресурсная: пустой рудный склад не одалживает свою защиту полимерам. Итог все так же режется трюмами уцелевших кораблей.
Порог поднимает «Бункерование» — по два процента за уровень. До половины
вместимости (пятнадцатый уровень) это обычная ветка развития, за ней каждый
уровень обходится втрое дороже обычного роста: на шестнадцатом это уже почти
семь миллионов руды. Абсолютная броня покупаться не должна — полная
неуязвимость убивает грабеж, а с ним и повод держать флот. Отчеты показывают, куда ушел урон с обеих сторон и сколько
ресурсов спрятало хранилище защитника. Бой случаен — цели выбираются броском кубика,
и один и тот же состав дает разброс исходов; случайность вынесена в параметр rng,
поэтому предпросмотр в симуляторе и тесты подставляют зерно и получают повторяемый бой.
src/
index.ts точка входа: Express, Socket.IO, монтирование роутов
config/env.ts загрузка и валидация окружения
config/auth.ts секрет JWT, сроки жизни токенов, провайдеры
config/version.ts номер версии игры, который видят игроки
db/prisma.ts PrismaClient с pg-адаптером
game/rules.ts чистые формулы: добыча, стоимость, энергия, вместимость складов
game/techTree.ts дерево технологий: стоимость, время, требования, бонусы
game/ships.ts классы кораблей: стоимость, время, боевые профили
game/defenses.ts стационарная оборона: стоимость, время, энергия
game/combat.ts бой: раунды, щиты и корпус, скорострел, обломки, грабеж
game/fleets.ts логистика: орбиты, гиперпрыжки, трюмы, топливо
game/fogOfWar.ts видимость планет, снимки разведки и их старение
game/market.ts цена рынка, комиссии, залоги и вместимость складов хаба
game/espionage.ts лестница шпионажа: что видит зонд и что замечает цель
game/expeditions.ts событийный движок экспедиций и лимит слотов
game/score.ts формулы рейтинга: во что вложены ресурсы (чистый модуль)
game/syndicate.ts правила синдиката: ранги, налог, Кіш, модули и их стройка, технологии, налет, пакты
game/baseState.ts состояние игрока и баз в памяти + снимки для клиента
game/gameLoop.ts изолированный тик: таймеры, очереди, догон офлайна
game/bot/decide.ts решения бота как чистая функция «снимок → намерения»
game/bot/director.ts исполнение намерений и сборка снимка для бота
game/bot/personality.ts характеры: доли бюджета, порядок науки, состав эскадры
game/bot/mind.ts две роли языковой модели: стратег и дипломат
game/bot/plan.ts разбор и проверка плана от модели
game/bot/directives.ts разовые поручения модели и их сверка со снимком
game/bot/budget.ts учет обращений к модели и потолки переписки
game/bot/llm.ts вызов провайдера; любая неудача возвращает null
routes/ REST + middleware авторизации и разбор ввода
services/authService.ts пароли, JWT, смена пароля, вход через Google
services/commanderService.ts профиль командира и онбординг
services/achievementService.ts каталог достижений и их выдача
services/mapService.ts сборка карты системы под конкретного игрока
services/marketService.ts биржа: ордера, сведение сделок, бартер (в транзакциях)
services/botService.ts создание, пауза и удаление ботов
services/buildingService.ts карточки построек и проекция уровней вперед
services/technologyService.ts карточки технологий и проекция уровней вперед
services/syndicateService.ts синдикаты: ранги, заявки, казна, Кіш, модули и стройка, налог, кодекс
services/syndicateAccess.ts членство, права и пакты — отдельно, чтобы не замкнуть импорты
services/pactService.ts пакты синдикатов: предложение, ответ, расторжение, письма
services/hubStock.ts склады биржи: склад хаба и общий склад под блокировкой
services/adminService.ts пульт гейм-мастера: чтение и правка состояния
services/mailService.ts центр связи: ящик, доставка, приватность
services/reportMail.ts сборка текста системных отчетов (чистый модуль)
services/simulationService.ts боевой симулятор: расчет без записи в БД
services/fleetTemplateService.ts шаблоны флотов
services/warService.ts дипломатия, отчеты о боях, предупреждение перед атакой
services/scoreService.ts рейтинг командиров и синдикатов с кешем
types/api.ts типы ответов REST
types/socket.ts типизированные интерфейсы Socket.IO
scripts/ генерация галактики, проверка формул, стенд для тестов
public/ клиент: вход, командный центр, карты, биржа, синдикаты
(index.html хранит спрайты иконок и иллюстраций)
prisma/ схема и миграции
tests/ тесты формул на игровых модулях и стресс-тесты по живому API
| Метод | Путь | Назначение |
|---|---|---|
| POST | /api/auth/register | регистрация по email и паролю |
| POST | /api/auth/login | вход, выдача JWT |
| GET | /api/auth/config | публичные настройки входа и версия игры |
| POST | /api/auth/oauth/:provider | вход через Google (id_token) или Telegram (initData) |
| POST | /api/auth/password/reset-request | запрос кода смены пароля |
| POST | /api/auth/password/reset | установка нового пароля |
| GET | /api/auth/me | текущая сессия: аккаунт, командир, аватары |
| POST | /api/auth/commander | создание командира (онбординг) |
| GET | /api/state | состояние игрока: базы, очереди, технологии, флот |
| GET | /api/bases/:id/buildings/:type | проекция постройки на десять уровней вперед |
| GET | /api/bases/:id/technologies/:tech | проекция технологии на десять уровней вперед |
| POST | /api/bases/:id/build | поставить здание в стройку |
| POST | /api/bases/:id/rush | доделать стройку за криптогривну |
| POST | /api/bases/:id/build/cancel | отменить стройку с полным возвратом |
| POST | /api/bases/:id/research | запустить исследование |
| POST | /api/bases/:id/research/cancel | отменить исследование |
| POST | /api/bases/:id/research/join | присоединить лабораторию колонии к идущему исследованию |
| POST | /api/bases/:id/ships | заказать корабли на верфи |
| POST | /api/bases/:id/ships/cancel | отменить заказ верфи |
Экспедиция отправляется тем же POST /api/bases/:id/fleets с миссией EXPEDITION:
без цели — в глубокий космос родной системы, с targetSystemId — в чужую.
Тесты в tests/ делятся надвое. Большая часть считает формулы
напрямую на игровых модулях и сервера не требует: бой, полеты, хранилища,
рейтинг, обломки, шпионаж, решения ботов, учет обращений к модели, проверка
id_token и соответствие растровых файлов типам объектов. Остальные —
стресс-тесты, права в синдикатах, гонки на бирже и доступ к пульту
гейм-мастера — бьют по живому API работающего dev-сервера.
Тесты не проходят проверку типов: tsconfig.json включает только src/**,
поэтому расхождение с изменившейся сигнатурой видно исключительно на запуске.
Меняя экспортируемый тип, наборы надо прогнать, а не понадеяться
на npm run typecheck.
npm run --silent test:stand > stand.json
node tests/auth.mjs
node tests/stress-market-fleet.mjs stand.json
node tests/stress-races.mjs stand.json
node tests/syndicates.mjs stand.json
node tests/economy-build.mjs stand.json
Тесты хранилищ проверяют формулы напрямую на игровых модулях, поэтому им нужен только запущенный сервер для последней секции:
npm run test:storage
npm run test:commander
npm run test:mail
npm run test:admin
npm run test:debris
npm run test:assets
npm run test:combat
npm run test:fleets
npm run test:score
npm run test:speed
npm run test:syndicate
npm run test:oauth
npm run test:bot
npm run test:budget
npm run test:espionage
strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes.
Отдельный прогон npm run audit добавляет noUnusedLocals и noUnusedParameters.public/app.js в tsconfig.json не входит, поэтому у него свой узкий гейт —
npm run audit:client. Он отбирает из checkJs только «имя не найдено»:
полная проверка дает полторы сотни замечаний о типах DOM и как гейт непригодна,
а вот вызов удаленной функции node --check пропускает — и падает уже
в браузере, унося все, что рисуется после.Response<...> (types/api.ts),
события Socket.IO — через типовые контракты (types/socket.ts):
расхождение между сервером и клиентом ловит компилятор.npm run check:rules).UPDATE,
жесткий перезапуск между шагами не задваивает начисления и не теряет корабли.Правила разработки и текущая архитектура — в CLAUDE.md.
| тесты приватности почты и формулировок системных отчетов |
npm run test:admin | тесты доступа к пульту гейм-мастера и синхронизации с тиком |
npm run test:score | формулы рейтинга: постройки по уровням, флот, ресурсы |
npm run test:speed | технологии скорости: ускорение стройки и плата энергией |
npm run test:syndicate | правила синдикатов: ранги, налог, Кіш и его модули, стройка, налет, пакты, журнал казны |
npm run test:oauth | проверка id_token Google: подпись, aud, срок, email_verified |
npm run test:debris | тесты формулы обломков и атомарности их сборки |
npm run test:assets | сверка имен растровых файлов с типами объектов |
npm run test:combat | тесты боевого движка: раунды, щиты, скорострел, технологии, обломки, баланс |
npm run test:fleets | тесты полетов: скорость, топливо, прыжки, типы миссий, правила вылета |
npm run test:bot | решения бота: порядок застройки и науки, состав флота, щит новичка, торговля, коалиция |
npm run test:budget | учет обращений к языковой модели: дневная норма, паузы, потолки переписки |
npm run test:espionage | лестница шпионажа: раскрытие по разнице уровней, заметность, нагрузка письма |
npm run admin:grant | выдать (или снять --revoke) роль администратора аккаунту |
| POST | /api/bases/:id/defenses | заказать стационарную оборону |
| POST | /api/bases/:id/defenses/cancel | отменить заказ обороны |
| GET | /api/galaxy | макро-карта: все системы с координатами |
| GET | /api/map?systemId= | карта системы (по умолчанию — родной) с туманом войны |
| GET | /api/planets/at?x=&y=&position= | планета по координатам: система X:Y и орбита |
| GET | /api/planets/:planetId/joint-attacks | атаки на планету, к которым можно присоединиться |
| GET | /api/leaderboard | рейтинг командиров и синдикатов |
| POST | /api/bases/:id/fleets/preview | предрасчет маршрута: время, топливо, трюмы, Брама |
| POST | /api/bases/:id/fleets | отправить флот: любая миссия, в том числе удержание, рейсы в Кіш, налет и совместная атака |
| POST | /api/fleets/:fleetId/recall | отозвать флот с удержания |
| GET | /api/market | склады биржи, стакан, сводка рынка, бартер, первая страница журнала |
| GET | /api/market/history/:resource | средняя цена по дням за 30 суток |
| GET | /api/market/trades | страница журнала сделок, курсор ?before=, фильтры ?resource= и ?mine=1 |
| POST | /api/market/storage/upgrade | расширить склад биржи за криптогривну |
| POST | /api/market/orders | выставить ордер на покупку или продажу |
| POST | /api/market/orders/:id/fill | исполнить чужой ордер |
| DELETE | /api/market/orders/:id | снять свой ордер |
| POST | /api/market/barter | предложить обмен ресурса на ресурс |
| POST | /api/market/barter/:id/accept | принять обмен целиком |
| DELETE | /api/market/barter/:id | снять свое предложение обмена |
| GET | /api/war | дипломатия, пакты, отчеты о боях и экспедициях |
| POST | /api/war/declare | объявить личную войну |
| POST | /api/war/peace | заключить личный мир |
| POST | /api/war/syndicate/declare | объявить войну синдикату (право дипломатии) |
| POST | /api/war/syndicate/peace | заключить мир между синдикатами (право дипломатии) |
| POST | /api/war/syndicate/pact/propose | предложить пакт другому синдикату |
| POST | /api/war/syndicate/pact/respond | принять или отклонить предложение пакта |
| POST | /api/war/syndicate/pact/cancel | отозвать предложение или расторгнуть пакт |
| GET | /api/syndicates | список синдикатов и свой синдикат целиком: казна, Кіш, модули, стройка, состав |
| POST | /api/syndicates | основать синдикат |
| GET | /api/syndicates/:id/codex | кодекс синдиката для кандидата |
| POST | /api/syndicates/:id/apply | вступить или подать заявку с принятием кодекса |
| POST | /api/syndicates/applications/:id/approve | одобрить заявку |
| POST | /api/syndicates/applications/:id/reject | отклонить заявку |
| POST | /api/syndicates/members/:id/kick | исключить участника |
| POST | /api/syndicates/members/:id/rank | назначить ранг |
| POST | /api/syndicates/members/:id/leader | передать лидерство |
| POST | /api/syndicates/members/:id/payout | выдать гривну из казны |
| POST | /api/syndicates/ranks | создать ранг |
| POST | /api/syndicates/ranks/:id | изменить ранг |
| POST | /api/syndicates/ranks/:id/delete | удалить ранг |
| POST | /api/syndicates/rules | правила набора: режим, взнос, минимальный рейтинг |
| POST | /api/syndicates/description | описание синдиката |
| POST | /api/syndicates/tax | ставка налога с крипто-фермы |
| POST | /api/syndicates/codex | новая редакция кодекса |
| POST | /api/syndicates/kish/upgrade | начать стройку уровня Коша |
| POST | /api/syndicates/treasury/upgrade | начать стройку Скарбниці |
| POST | /api/syndicates/academy/upgrade | начать стройку Академии |
| POST | /api/syndicates/watch/upgrade | начать стройку Дозора |
| POST | /api/syndicates/gates | начать стройку или повышение Брамы в системе |
| POST | /api/syndicates/construction/cancel | отменить стройку в Коше с полным возвратом |
| GET | /api/syndicates/projection/module/:module | уровни вперед у модуля Коша (?systemId= для Брамы) |
| GET | /api/syndicates/projection/tech/:tech | уровни вперед у технологии синдиката |
| POST | /api/syndicates/research | начать изучение технологии синдиката |
| POST | /api/syndicates/kish/defenses | поставить оборону Коша из казны |
| POST | /api/syndicates/kish/move | перенести Кіш к своей Браме |
| POST | /api/syndicates/donate | взнос в казну с личного счета |
| POST | /api/syndicates/broadcast | рассылка по составу |
| POST | /api/syndicates/leave | покинуть синдикат |
| POST | /api/syndicates/disband | распустить синдикат (главарь) |
| POST | /api/commander/simulate | прогон боя без записи в БД |
| GET | /api/commander/espionage | разведанные цели для симулятора |
| GET | /api/commander/fleet-templates | список шаблонов флотов |
| POST | /api/commander/fleet-templates | создать шаблон |
| PUT | /api/commander/fleet-templates/:id | изменить шаблон |
| DELETE | /api/commander/fleet-templates/:id | удалить шаблон |
| GET | /api/mail | ящик командира, фильтр ?type= |
| POST | /api/mail | личное письмо по позывному |
| POST | /api/mail/:id/read | пометить письмо прочитанным |
| POST | /api/mail/read-all | пометить прочитанным весь ящик |
| DELETE | /api/mail/:id | удалить письмо |
| GET | /api/admin/schema | справочник ключей для формы админки (admin) |
| GET | /api/admin/dashboard | сводка по серверу: онлайн, активность, колонии (admin) |
| GET | /api/admin/commanders | список командиров, поиск ?search= (admin) |
| GET | /api/admin/commanders/:id | полный стейт игрока (admin) |
| PATCH | /api/admin/commanders/:id | принудительная правка состояния (admin) |
| POST | /api/admin/commanders/:id/password-reset | выдать игроку код смены пароля (admin) |
| POST | /api/admin/commanders/:id/block | закрыть или вернуть доступ (admin) |
| POST | /api/admin/commanders/:id/message | письмо игроку от гейм-мастера (admin) |
| DELETE | /api/admin/commanders/:id | удалить учетную запись, подтверждение позывным (admin) |
| GET | /api/admin/bots | список ботов и каталог характеров (admin) |
| POST | /api/admin/bots | создать бота: позывной, характер, система (admin) |
| POST | /api/admin/bots/:id/active | пауза и возобновление решений (admin) |
| POST | /api/admin/bots/:id/nudge | заставить бота решать прямо сейчас (admin) |
| DELETE | /api/admin/bots/:id | удалить бота вместе с колониями (admin) |