Восстановление игры RJ Games Under Fire: Invasion (RU: «Пекло! Космическая стратегия!», внутр. код SpaceHeat) для запуска в браузере. Оригинал — Android/iOS/Windows на собственном движке Inferno (cocos2d-x). Серверы мертвы, но игра офлайновая — вся логика в клиенте.
Некоммерческий фанатский проект по сохранению игры, не связан с RJ Games. Все права на игру, её графику, звук, тексты и данные принадлежат правообладателям. Оригинальные файлы игры (APK, OBB) в репозиторий не входят; в
data/лежат данные, извлечённые из её конфигурации, — для исследования и сохранения. Чтобы пересобрать их, нужен свой экземпляр оригинала.
mobi.rjg.underfirev1.3.12 (versionCode 62, последняя, 29.05.2016), XAPK из APKPure, 649 МБ: APK + два OBB —main.13иpatch.59.- Подпись APK:
C=RU, L=Moscow, O=RJ Games, OU=Mobile Games, MD5 сертификатаF8:07:8F:CE:59:A8:54:8A:D1:27:94:CD:1E:D8:66:7C— оригинал, без модов. - Движок
libinferno.so(cocos2d-x, 15 МБ) + FMOD, только armeabi (32-бит). 64-битной сборки нет → на Apple Silicon не запускается; поэтому цель — браузерная пересборка. - Код игры остаётся внутри APK (
lib/armeabi/); вraw/извлекаются только ассеты и данные.
raw/ # распакованный оригинал — не в git (см. «Подготовка данных»)
apk/assets/ # из APK: звуки, музыка, шрифты, шейдеры, немного интерфейса
obb_main/ # main.13 OBB: основная графика и иконки, интерфейс, звук, config.xml 2014 г.
obb_patch/ # patch.59 OBB: АВТОРИТЕТНЫЙ config.xml (2015 г., 5.4 МБ) + обновлённая графика
data/ # результат tools/parse_config.py — в git; руками не правим, только пересборкой
config.json # 50 секций, 13 511 сущностей (см. config_sections.json)
config_sections.json # сводка «секция → количество»
server.json # серверная часть: 21 секция, 11 782 сущности (награды квестов — actions)
server_sections.json # сводка по server.json
locale_ru.json / locale_en.json # все строки игры: 6 416 / 6 305, ключ «секция/…» (@ = значение)
assets/ # ассеты для браузера: WebP, атласы, анимации, макеты UI, шрифты — не в git,
# собираются tools/build_assets.py или скачиваются из релиза (tools/fetch_assets.py)
web/ # браузерный клиент: TypeScript + Vite + PixiJS 8
src/engine/ # слой совместимости с cocos2d-x 2.x (геометрия узлов, правила CocosBuilder)
deploy/ # выкладка на свой сервер: deploy.sh и конфиг nginx (см. «Деплой»)
tools/ # парсеры/декодеры
extract_xapk.py # XAPK → raw/ (из APK — только assets/, оба OBB — целиком)
parse_config.py # config.xml, server.xml, локали (Inferno, lxml recover) → data/*.json
build_assets.py # весь конвейер ассетов raw/ → assets/ и архивы для релиза:
decode_textures.py # .pkm.ccz → WebP с альфой из нижней половины
convert_atlases.py # .atlas → спрайт-листы PixiJS + индекс кадров
visuals_to_json.py # config/visuals/*.xml → анимации
ccbi_to_json.py # макеты CocosBuilder → JSON (ccbi_write.py — обратно, для проверки)
fetch_assets.py # готовые ассеты из GitHub Release → assets/ (без XAPK)
Файлы patch перекрывают main, main — APK (1 618 путей есть и в main, и в patch). Без дублей
и .DS_Store — 10 175 файлов из 11 929.
| Тип | Файлов | Что это |
|---|---|---|
.pkm.ccz |
3 503 | текстуры: ETC1 в zlib-контейнере (см. «Форматы ассетов») |
.atlas |
2 685 | нарезка текстур на кадры |
.png |
1 952 | иконки (1 927) и мелкая графика — готовы к использованию |
.xml |
1 655 | config.xml, анимации (config/visuals/), карты, миссии, локали |
.ccbi |
177 | макеты интерфейса CocosBuilder |
.wav / .mp3 |
115 / 63 | звуки, музыка, эмбиенты |
.ttf |
12 | шрифты |
.frag / .vert |
8 / 1 | GLSL-шейдеры (размытие, ч/б и т. п.) |
Склейка 548 XML-документов namespace inferno: без общего корня; lxml в режиме recover
разбирает её без ошибок. Имён секций 53, три из них пустые (currency_groups, galaxies,
stars), поэтому в config.json 50 секций. Patch — надмножество main: все секции main плюс
achievements (69) и spaceport_store (14), 13 511 сущностей против 10 580, поэтому парсер
читает только patch.
Ключевые секции: buildings (665), soldiers (320), units (86), turrets (11),
resources (182), order_prices/offers/packs/spaceport_store (экономика),
chapters (5)/missions (143)/maps (121), star_systems (9)/territories (120, галактика),
quests (2 355), perks (7), abilities (180), behaviors (362), contracts (896),
invasions (26), requirements (5 105). Модель совпадает по духу с Under Control
(другая игра RJ Games).
server.xml — такая же склейка (477 документов) с серверной частью данных: 2 431 действие
(actions — награды всех 2 355 квестов), требования, таймеры, контракты. Сервер игры встроен
в клиент и читает её сам; парсер кладёт её в data/server.json.
Отдельно от config.xml лежат описания анимаций config/visuals/**/*.xml: visual →
направления (атлас, смещение, hitArea) → состояния (idle, work, …) → слои с номерами
кадров. parse_config.py их пока не читает.
.pkm.ccz— 16-байтный заголовокCCZ!(сжатие zlib), внутри PKM v1.0, формат ETC1 RGB. ETC1 не хранит прозрачность: у 3 405 из 3 503 текстур закодированная высота вдвое больше реальной, и в нижней половине лежит альфа-маска (подтверждено декодированием и шейдером вlibinferno.so). Цвет не премультиплицирован. Остальные 98 текстур — без альфы..atlas— текстовый формат Inferno (не plist cocos2d-x). Первая строкаtextures: <имя>.png, хотя фактически рядом лежит<имя>.pkm.ccz. Дальше по строке на кадр через табуляцию: имя,x y w hв текстуре,ox oy— смещение обрезанного кадра от левого верхнего угла исходного,W H— исходный размер. Необязательное 10-е полеr: кадр лежит повёрнутым на 90° по часовой (1 340 из 149 933 кадров)..ccbi— макеты CocosBuilder, формат v5 в варианте Inferno: у колбэков кнопок лишние поля, поэтому штатный ридер ломается на 253 файлах из 306. Подробности — docs/research/ccbi.md.
- XAPK получен, подпись проверена, распакован (APK + 2 OBB).
- Полная инвентаризация ассетов и данных; форматы текстур и атласов определены.
-
parse_config.py: config.xml →data/config.json(lossless по секциям) + локали; полнота сверена с исходником. - Публичный репозиторий; порядок работы — в CONTRIBUTING.md.
- Распаковка XAPK одной командой (
tools/extract_xapk.py), CI с линтером и тестами. - Исследование и выбор стека: пять отчётов в docs/research/, решение — docs/engine-decision.md.
- Конвейер ассетов (
tools/build_assets.py): текстуры → WebP, атласы → спрайт-листы PixiJS, анимации, макеты UI; готовый комплект — в релизеassets-v1.3.12(tools/fetch_assets.py). - Прототип на PixiJS: интерфейс из ccbi, изометрия, размытие — стек подтверждён (docs/prototype.md).
Задачи по этапам — в milestones на GitHub.
- Стек: PixiJS 8 + TypeScript, решено 29.09.2026 — см. docs/engine-decision.md. Сначала браузеры на компьютере, телефоны — позже. Прототип сделан, стек подтверждён — docs/prototype.md.
- Графика: конвейер готов (
tools/build_assets.py: WebP, спрайт-листы, анимации); осталась галерея для визуальной проверки и связка визуалов с сущностями конфига. - Справочники: из
config.jsonиserver.jsonсобрать читаемые каталоги: юниты со статами и уроном, здания по уровням, экономика, награды квестов, дерево миссий и глав. - Тексты: сопоставить ключи конфига (
X::name) со строкамиdata/locale_*.jsonи шаблонами способностей. - Отрисовка: браузерный клиент — UI из макетов ccbi, колония, карта галактики, сцена боя.
- Логика боя: интерпретатор скриптов поведения, способности, волны и условия миссий, ИИ.
Чего не хватит в данных — реверсом
libinferno.so, благо символы в нём с именами. - Колония и прогресс: строительство и производство по таймерам, квесты, сохранения (в оригинале это открытый XML).
Оригинал в репозиторий не входит — нужен свой XAPK mobi.rjg.underfire 1.3.12. Скрипт
распаковывает его в raw/: из APK — только assets/, оба OBB — целиком — и сверяет число
файлов с эталоном версии (160 / 8 884 / 2 932).
uv run tools/extract_xapk.py /путь/к/Under+Fire_+Invasion_1.3.12_APKPure.xapk # --force — перезаписать
uv run tools/build_assets.py # raw/ → assets/ (≈1,5 мин на 8 ядрах)Собранный assets/ выложен архивами в релиз
assets-v1.3.12. Этого
хватает для клиента, графики и интерфейса; raw/ нужен только для пересборки ассетов и реверса
libinferno.so.
python3 tools/fetch_assets.py # всё: JSON и WebP (≈450 МБ)
python3 tools/fetch_assets.py --only json # без текстур: атласы, анимации, макеты UI, шрифтыОбновить релиз после пересборки (нужен raw/):
uv run tools/build_assets.py --pack dist/
gh release upload assets-v1.3.12 dist/* --clobberЗависимости Python описаны в pyproject.toml и закреплены в uv.lock; ставит их
uv вместе с Python 3.12 из .python-version:
uv sync # .venv: lxml, Pillow, texture2ddecoder + pytest и ruff
uv run tools/parse_config.py # пересобрать data/*.json из raw/uv run запускает скрипт в .venv; то же даёт python3 tools/… после source .venv/bin/activate.
Только tools/fetch_assets.py обходится стандартной библиотекой и работает без uv.
Проверки — те же, что в CI (GitHub Actions на каждый PR и push в main). Тесты не требуют
файлов игры: парсер проверяется на синтетических XML, data/ — на согласованность.
uv run ruff check . && uv run pytest -qcd web && npm install
npm run dev # http://localhost:5173; ассеты из ../assets — по /assets/
npm run typecheck && npm test && npm run build # то же, что проверяет CIКлиент — статика: сборка web/dist плюс assets/. Выкладывается на свой сервер (Ubuntu, nginx)
скриптом web/deploy/deploy.sh; адрес сервера и домен — UF_DEPLOY_HOST и UF_DOMAIN в .env
(образец — .env.example, сам .env не коммитится).
web/deploy/deploy.sh --nginx --assets # первый раз: nginx и конфиг сайта, ассеты релиза на сервер (≈450 МБ)
web/deploy/deploy.sh # дальше: собрать web/dist и выложить
web/deploy/deploy.sh --status # что сейчас на сервереНа сервере сборка лежит в /var/www/underfire/dist и заменяется атомарно, ассеты — в
/var/www/underfire/assets и отдаются как /assets/. Их сервер скачивает сам из релиза
assets-v1.3.12 скриптом tools/fetch_assets.py, с компьютера уходит только сборка (~1 МБ).
Конфиг nginx — web/deploy/underfire.nginx.conf: без домена сайт отвечает по IP, с UF_DOMAIN —
по имени; HTTPS после первого деплоя — certbot --nginx -d <домен>.
Если работать прямо на сервере (репозиторий склонирован туда, есть Node.js), в .env ставится
UF_DEPLOY_HOST=local, и те же команды выполняются без ssh.
Код и документация проекта — под лицензией MIT. Она не распространяется на содержимое
игры: данные в data/ извлечены из Under Fire: Invasion, а права на игру, её графику, звук
и тексты принадлежат RJ Games (правообладателям).