Contents
In short: In a strategy game, each capital or source spreads outward up to a cost limit, and each place wants to know its nearest source. MKA Engine provides both as basic engine operations; the game writes the cost, the limit and what the result means. In one measurement with 282 sources, close to game scale, the nearest-source call took about 13 ms inside WebAssembly, the sandbox where mods (packages of rules that extend the game) run; a separate spread for each source took about 2.7 seconds.
Why was one call per source not enough?
Imagine dropping a drop of ink on a map from every capital. Each drop spreads up to its own limit, but where two drops meet you want to know which one arrived first. The game's current path makes one spread call per capital; each call returns an answer for the whole map, and the mod combines those answers one by one. That combining step is called folding. On top of that, the game's own correction passes repeat much of the same work.
What the engine added
Bounded multi-source spread. One call takes many sources. Each source spreads up to its own limit, under the edge rules of its own owner, and sources do not take each other's area. The result contains only the places that were reached; each place a source reaches is a hit.
Labelled nearest source. Each place takes the value of the lowest-cost source among those whose label matches its own (the label says which group of sources a place belongs to). The result is an array with one value per place across the map. The mod writes it into a column itself: a table column that holds one value per place.
The first solution returned every hit to the mod's memory. With 282 sources, each producing about 17 thousand hits, the total reached 4.8 million. That exceeded the mod's 16 MiB memory, and splitting the work into 41 calls still ran out of fuel (the instruction budget of a call). That is why a separate path was added that returns only the nearest source for each place.
What the engine keeps, what the game chooses
| Topic | Engine | Game |
|---|---|---|
| How spread works | Spreads by cost and stops at the limit | — |
| Cost | Reads the cost inputs once per call | The cost formula and its source, for example terrain or roads |
| Limit | Takes the limit as a parameter | The value of the limit |
| Label | Matches labels; picks one source per place | Which place belongs to which label |
| Result | Returns an array | What the result means: proximity, market access or supply |
We split it this way on purpose. Proximity, market access and supply can all be built on the same spread operation, but each needs a different rule: some want the nearest source, others want every source's own area. The engine supports both; the game chooses which one is used.
Measurements
We measured in two places: direct engine code, and WebAssembly, the sandbox where mods run. Each measurement used an unattended run of 30,000 places; direct engine code used 280 sources and WebAssembly 282. Each run had seven rounds, and within each round the paths ran one after another. Other builds were running on the machine and the average load was between 14 and 27, so the absolute numbers are noisy; the comparisons rest on ratios measured in the same round. Each table value is the median of the seven rounds: the middle value when the seven are sorted.
| Path | Median (ms) |
|---|---|
| 280 separate spread calls (the current path) | 88.4 |
| One call with 280 sources | 10.1 |
| Path | Country-sized limit (24 units) | Game-scale limit (226 units) |
|---|---|---|
| Nearest source, one call | 8.2 | 13.1 |
| Spread per source, folding in the mod | 83.3 | 2,691.8 |
On direct engine code the difference is about 8.7 times: each of the 280 separate calls re-reads the cost inputs, while one call reads them once. With a single source the difference is small (0.26 to 0.30 ms). In WebAssembly at game scale the difference is about 200 times; at country size, about 10 times. The game-scale limit produces about 17 thousand hits per source, 4.8 million in total. The gap has two sources: the answer stays the size of the map instead of growing with the total number of hits, and the search stops once every place of the label has been reached. The answer covers the whole map: 30,000 cells, about 480 KB. The WebAssembly measurement follows the same pattern as the direct engine measurement taken the same day.
What does it cost?
- Each answer covers the whole map. Even the nearest-source path returns the full array, not only the nearest value of one place.
- Writing the result straight into a table column was considered. The write cost was too small to measure, so that path was not added.
- The measurement ran on one machine under shared load. The full turn time of the game and its real cost formulas were not measured.
Frequently asked questions
What is bounded spread?
It is a reachability calculation that starts at one source and spreads outward up to a cost limit. With many sources, each spreads up to its own limit, and the sources do not take each other's area.
How is the nearest source found?
Each place takes the value of the lowest-cost source among those whose label matches its own. Even with many sources, this returns as one answer for the whole map in a single call.
Does the engine write this calculation as a game rule?
No. The engine provides only the spread operation; the game chooses the cost, the limit and what the result means.
How reliable are the measurements?
They come from one machine under shared load. The absolute numbers are noisy, but the ratios from the same rounds are consistent. The full game turn time and the real cost formulas were not measured.
Sources and records
External sources: WebAssembly security model (opens in a new tab), MDN: WebAssembly concepts (opens in a new tab).
Related diaries: Mods in the engine, Events and multipliers.
If you have questions or suggestions about this diary, write to us through the contact page. For an overview of the engine, see the MKA Engine page; for all diaries, see the Developer Diaries list.
Sources and related docs
You may also like
- How is a strategy game's world map made?
For MKA Engine we built a 27,357-location world map of 1337 from real data: how terrain, rivers, straits, population and location borders were made.
- The road to a game engine
How we started MKA Engine: reading four games, treating determinism as law, moving phase by phase with measurements, and where we are now.