Егор Урванов

Архитектура игры «Хроники Королевств»: C4 и критические пути

Схемы C4 до уровня компонентов и четыре критических пути через sequence: начало партии, кадр, приказ и сохранение.

«Хроники Королевств» — стратегия в духе Age of Empires II, которая запускается как обычный сайт: без сервера и сборки, около 80 модулей на JavaScript и холст на всю страницу. Игра переписана из Python-версии построчно, поэтому правила и числа совпадают с оригиналом. Ниже схемы C4 до уровня компонентов и четыре пути, на которых всё держится.

C4: контекст и контейнеры

Уровень 1. Система и её окружение

Игрокмышь, клавиатураХроники Королевствигра в браузереСтатический хостинглюбой, без кодаХранилище браузераIndexedDBКонвейер ассетовtools/, один разВерсия на Pythonэталон для сверкииграетотдаёт файлысохраненияспрайты, звукисверка
Игре нужны только файлы и браузер. Конвейер и Python-версия работают вне игры: первый готовит картинки, вторая служит эталоном для тестов.

Уровень 2. Контейнеры

Статический хостингфайлы как естьЗагрузчикindex.html, boot.jsАссетыassets/ + manifestИграmain.js + src/Средаruntime/API браузераCanvas, Audio, IDBстраницагруппы ассетовimportвызовы pygameчтениерисует, звучит
Загрузчик тянет ассеты и показывает полосу прогресса, затем подключает игру. Игра говорит с браузером только через среду, которая повторяет API pygame.

C4 уровня 3: компоненты

Главный контейнер, «Игра», делится на три части: симуляция (правила), соперник (ИИ) и интерфейс. Симуляция не знает о графике, поэтому гоняется в тестах без экрана. Среда выполнения разобрана отдельно.

Симуляция

Флотnaval.jsТаблицыdata.js, content/Рынок, реликвииmarket, relicsПравила матчаmatch.jsМирworld.jsСтатистикаstats, scoringКартыmapgen, terrainПриказыorders.jsОборонаdefense.jsхарактеристикипараметрыгенерацияочередь приказовстены, гарнизонвода, кораблихук tickсобытия
Мир хранит игроков, юниты, здания, снаряды, туман и ищет пути; один шаг мира двигает всё это.
  • Юниты, здания, технологии и 14 цивилизаций лежат в таблицах content/*: новая единица добавляется строкой данных.
  • Параметры матча (ресурсы, эпохи, перемирие, победа) собирает match.js.
  • Карты: шесть рецептов в mapgen.js плюс рельеф с высотами, обрывами и мелями.

Соперник (ИИ)

Экономикаeco_ai.jsМиртот же, что у игрокаФлотnaval_ai.jsПрофили6 уровней, по эпохамПланировщикai.jsЗащитаai_defense.jsАрмияai_army.jsВойнаai_war.jsчислаcmd_*излишкисоставволныколоколкарты с водой
Каждый соперник думает раз в полсекунды и отдаёт те же команды, что и игрок.
  • Профиль задаёт, сколько селян держать в каждой эпохе, когда переходить дальше и когда нападать; уровней шесть, от лёгкого до предельного.
  • Война идёт по кругу: сбор, поход, бой, отход; решение берётся по сравнению сил.
  • Флот подключается только на картах с водой.

Интерфейс

Меню и лоббиmenu, lobby, screensСохраненияsavegame, saves_uiЗвукsound, music, synthМирчитает и шлёт приказыЭкран игрыui.jsОтрисовкаgfx, sprites3dПанелиhud, hud_windowsУправлениеcontrols.js, keymapЯзыкиi18n, 7 локалейupdate(dt)состояниеF7 / F8события миракадринтерфейсвводтексты
Экран игры — один большой класс, к которому подмешаны панели, управление, меню, лобби и экраны вокруг матча.
  • Цикл простой: события, обновление мира, отрисовка, звук; меню, загрузка и итоги — это состояния того же цикла.
  • Звук слушает события мира (удар, смерть, постройка) и сам решает, что проиграть.
  • Картинка берёт готовые спрайты; если их нет, рисуется запасная процедурная графика.

Среда выполнения

Идиомы Pythonpy.jsИграsrc/numpynp.jsСериализацияpickle.jspygamepygame.jsАссетыassets.jsФайлыstorage.jsAPI браузераCanvas, Audio, IDBчисла, randomрисует, вводсинтез звукасохранениякартинки, звукCanvas, AudioIndexedDBфайлы
Игровой код остался в стиле Python; среда притворяется Python, pygame и numpy поверх браузера.
  • py.js повторяет числа, сортировку и random бит в бит, поэтому сохранённая партия и тесты дают те же результаты, что в Python.
  • storage.js ведёт себя как обычные файлы: читает и пишет сразу, а в IndexedDB сбрасывает в фоне.
  • assets.js грузит по манифесту группами; листы юнитов приходят по одному, когда нужны.

Четыре критических пути

1. Начало партии

ИгрокЛоббиЗагрузкаМирИИАссетыStart Gamebegin_loadingnew World(...)карта, ресурсы, стартовые юнитымир готовпо ИИ на соперникалисты стартовых юнитовкамера, ландшафт, панелилисты пришлиматч идёт
Мир строится за один шаг, а картинка ждёт, пока придут спрайты стартовых юнитов (не дольше короткого лимита).

2. Один кадр

ЦиклМирЮнитыЗданияИИКартинка, звукввод: мышь, клавишиupdate(dt)update каждого юнитапуть, добыча, бойочереди и стройкатуман, хуки, победаai.update(dt)приказы cmd_*мир обновлёнdraw + события мира
Время кадра режется на шаги не длиннее 0.034 с; ИИ решает раз в полсекунды, остальные кадры только ждёт.

3. Приказ и его выполнение

ИгрокЭкран игрыПриказыЮнитМирПКМ на картецель: земля, ресурс, враг, зданиеissue(юнит, приказ)cmd_*: состояниеupdate каждый кадрfind_path (A*)путьидёт, добывает, бьётon_idle: из очереди
Игрок и ИИ используют одни и те же команды юнита; очередь по Shift и патрули живут в модуле приказов.

4. Сохранение и загрузка

ИгрокЭкран игрыСохраненияpickle.jsstorage.jsIndexedDBF7 или слот в менюsave_worlddumps: мир, камератекст, адреса таблиц.tmp, затем replaceв фонеF8 или слот в менюload_worldчитает файлзаголовок + телоloadsWorld, randomмир, камера, шапкаattach_world(w, ui)
В файл попадает весь граф объектов целиком; статические таблицы записываются адресами и берутся из текущей версии игры.

Правила, на которых всё держится

  • Мир один знает правду. Экран только читает его и шлёт приказы.
  • Игрок и ИИ равны. Оба командуют юнитами через одни и те же cmd_*.
  • Данные отдельно от кода. Юниты, здания, технологии и цивилизации живут в таблицах.
  • Симуляция без графики. Тесты гоняют целые матчи ИИ против ИИ без экрана и сверяют их с CPython.
  • Нужное синхронно загружено заранее. Загрузчик кладёт ассеты в память до старта игры; листы юнитов подгружаются по одному.

Слабые места

Где Что не так
Большие файлы world.js — 3,2 тысячи строк, ui.js — 2,2 тысячи, hud.js — 2 тысячи: границ внутри них мало.
Подмешанные части Экран игры собран из шести подмешанных частей, которые общаются через общие поля.
Связи по имени Модули находят друг друга через реестр modules.* во время работы: в коде связи не видны.
Шаг зависит от кадра Длина шага берётся из реального времени. Для общей партии по сети понадобится фиксированный шаг.
Вес Первая загрузка около 60 МБ, все листы юнитов — ещё около 90 МБ; в памяти они раздуваются до гигабайтов.
Нет сервера Сохранения живут только в одном браузере, сетевой игры нет.

Об игре

Страница игрыХроники КоролевствСтратегия в реальном времени в духе Age of Empires II — прямо в браузере
© 2026 Егор Урванов