Architecture of “Chronicles of Kingdoms”: C4 and critical paths
C4 diagrams down to components and four critical paths as sequences: starting a match, a frame, an order and save/load.
“Chronicles of Kingdoms” is a real-time strategy in the spirit of Age of Empires II that runs as an ordinary website: no server and no build step, about 80 JavaScript modules and one canvas filling the page. It was ported line by line from the Python version, so the rules and numbers match the original. Below: C4 diagrams down to components and the four paths everything rests on.
C4: context and containers
Level 1. The system and its surroundings
Level 2. Containers
C4 level 3: components
The main container, “Game”, splits into three parts: the simulation (the rules), the opponent (AI) and the interface. The simulation knows nothing about graphics, so tests run it without a screen. The runtime is described separately.
Simulation
- Units, buildings, techs and the 14 civilizations sit in the
content/*tables: a new unit is a line of data. - Match parameters (resources, ages, treaty, victory) are collected in
match.js. - Maps: six recipes in
mapgen.jsplus relief with heights, cliffs and shallows.
Opponent (AI)
- A profile says how many villagers to keep in each age, when to advance and when to attack; there are six levels, from easiest to extreme.
- War runs in a loop: rally, march, engage, retreat; the decision comes from comparing strengths.
- The navy is plugged in only on maps with water.
Interface
- The loop is simple: events, world update, drawing, sound; the menu, loading and results are states of the same loop.
- Audio listens to the world’s events (a hit, a death, a building) and decides what to play.
- The picture uses ready sprites; if they are missing, a fallback procedural graphic is drawn.
Runtime
py.jsreproduces numbers, sorting andrandombit for bit, so a saved game and the tests give the same results as in Python.storage.jsbehaves like plain files: it reads and writes at once and flushes to IndexedDB in the background.assets.jsloads by manifest in groups; unit sheets arrive one by one when needed.
Four critical paths
1. Starting a match
2. One frame
3. An order and its execution
4. Save and load
The rules everything rests on
- The World is the only source of truth. The screen only reads it and sends orders.
- The player and the AI are equals. Both command units through the same
cmd_*. - Data is separate from code. Units, buildings, techs and civilizations live in tables.
- Simulation without graphics. Tests run whole AI-vs-AI matches without a screen and compare them with CPython.
- What is read synchronously is loaded in advance. The loader puts assets in memory before the game starts; unit sheets stream in one by one.
Weak spots
| Where | What is wrong |
|---|---|
| Big files | world.js is 3.2 k lines, ui.js 2.2 k, hud.js 2 k: there are few boundaries inside them. |
| Mixed-in parts | The game screen is assembled from six mixed-in parts that talk through shared fields. |
| Links by name | Modules find each other through the modules.* registry at run time: the links are invisible in the code. |
| Step depends on the frame | The step length comes from real time. A shared online match would need a fixed step. |
| Weight | The first load is about 60 MB, all unit sheets another ~90 MB; in memory they inflate to gigabytes. |
| No server | Saves live in one browser only, and there is no online play. |