English · Русский
A map of the Sandustry Sandkit modding API — every method a mod can reach, what each one delegates to inside the engine, and the limits that aren't written down anywhere.
Read the map → · Русская версия
Not affiliated with Sandustry or its developers.
A hand-written API reference is true for exactly one build. This one is generated from your own installation, so when the game updates you re-run it and get a current map — including a diff telling you what moved.
The game ships two files, dist/js/external-mod-runtime.js and dist/js/external-mod-worker-runtime.js. Every call a mod makes passes through one of them on its way into the engine. They are the API, more so than any type definition, and they are small enough to parse exactly.
Requires Node 18+ and an installed copy of the game.
npm run mapThat runs three steps, which you can also run separately:
| step | what it does |
|---|---|
npm run extract |
Finds app.asar and pulls out the two runtimes, workshop-mods.js, and bundle.js, into work/. |
npm run analyze |
Parses the runtimes into data/, collects the value catalogs — event ids, hook ids, sound ids, gameConfig defaults, the window.electron bridge, sandkit.enums and sandkit.react — and patch limits, and diffs against a sandkit.d.ts if it finds one. |
npm run site |
Generates docs/index.html and MAP.md from data/. |
The game is found automatically in the usual Steam locations on macOS, Windows and Linux. If yours lives somewhere else:
SANDUSTRY_RESOURCES=/path/to/Sandustry/resources npm run mapThe .d.ts cross-check looks at ~/sandkit.d.ts by default; point SANDKIT_DTS somewhere else to override it. It is optional — skip it and everything else still builds.
sandkit.enums and sandkit.react are read straight out of the shipped code, so
they regenerate with everything else. sandkit.state cannot be: it is the live
game state, built as a world loads, and nothing in the bundle describes its shape.
scripts/state-dumper/ is a small mod that measures one. Copy the folder into
your Sandustry mods/ directory, load a world, press F7, and it downloads
state-shape.json. Move that into work/, re-run npm run analyze, and the
section appears. Then remove the mod — it is a tool, not something to leave
installed.
It records structure only: key names, types, array lengths, object sizes. No values are read, so a dump carries nothing personal and two players' dumps can be compared. Without one the state section simply says so rather than guessing.
work/ is git-ignored, and so are the extracted game files by name. Nothing from the game's own code is redistributed here.
What the repository does carry is the derived description — method names, argument counts, the engine function behind each call, event ids, and the documented limits — plus the generated site. That is the same category of thing as any community API reference.
content/en.js, content/ru.js all prose, one file per language
scripts/lib/parse-runtime.js the parser: minified bridge -> namespace tree
scripts/lib/collect-enums.js sandkit.enums, sandkit.react, sandkit.state
scripts/state-dumper/ a mod that describes sandkit.state in-game
scripts/extract.js locate the install, unpack what is needed
scripts/analyze.js runtimes -> data/, plus the d.ts diff
scripts/build-site.js data/ + content/ -> docs/ and MAP*.md
data/ generated: the API trees, events, patch rules
docs/ generated: the GitHub Pages site (en, ru)
MAP.md, MAP.ru.md generated: the same reference as Markdown
Against Sandustry 0.5.2, apiVersion 1:
- 281 methods across 53 namespaces on the main thread, 88 across 18 in the simulation worker.
- The community
sandkit.d.tsis accurate — every method lines up in both directions, with no phantom entries. Its one omission isconstants(constants.physics→normal,skip,aggressiveSkip). - A plain Workshop mod can already string-patch the game bundle: up to 256 patches against any
.jsunderjs/, withexpectedMatchesmandatory and atomic groups that roll back on a miss. No third-party loader required. dependenciesin the manifest is resolved with a real topological sort with cycle detection, which makes a library mod a supported pattern.- There is no main → worker messaging. Worker → main is an ordinary event; the other direction is a
SharedArrayBufferand nothing else. sandkit.apiis not the whole object a mod is handed. Three more members carry no methods and so appear in no method table:enums(22 enumerations, 323 members, values resolved from the bundle),react(a frozen copy of the game's own React 18.3.1 — mod components render inside the game's tree, so hooks only work from this copy), andstate(the live game state, and the same object assandkit.engine.state). Nothing describesstate's shape statically, so it is measured from a running world.
The generated page carries the rest, including the traps that cost real time — entries being scripts rather than modules, one-way registration, and element type ids drifting between versions.
Corrections to the prose and additions to the trap list are welcome. The generated files (data/, docs/, MAP*.md) are build output — change the generator, not the output.
Translations. All prose lives in content/, one file per language, and content/ru.js mirrors the shape of content/en.js key for key. To add a language, copy en.js, translate the values, set code, label and dir, and add the file to LOCALES in scripts/build-site.js. Method names, event ids and manifest fields are deliberately left untranslated — they are identifiers the reader will grep for.
If the parser breaks on a future build it will say so loudly rather than emit a half-map: it throws when it cannot find the freeze helper, the engine handle, or the API assembly.
MIT.