Modding
What mods can do, the API object and what it exposes, the mods folder, how the desktop app loads a mod, and links to mod repositories.
Subway Builder loads mods in the desktop app only. A mod is a folder in the mods folder with a manifest.json and a script. The script runs inside the game with the modding API on window.SubwayBuilderAPI.
What Mods Can Build#
- Custom cities with their own map tiles, demand data, roads and buildings.
- UI: toolbar buttons, panels, settings controls and overlays.
- Game changes: train types, station types, game rules, fares and commute timing.
- Map changes: custom layers, tile sources and styles.
API Version#
window.SubwayBuilderAPI is created when the app starts; the loader waits for it before running any mod script. window.SubwayBuilderAPI.version is '1.0.0', a fixed string that does not track the game version.
const api = window.SubwayBuilderAPI;
console.log(api.version); // "1.0.0"No Imports Needed#
A mod script runs through new Function, so import statements are syntax errors. Everything a mod needs is on the API object, including the libraries the game itself uses:
const api = window.SubwayBuilderAPI;
const { React, icons, components, charts } = api.utils;
const h = React.createElement;
const badge = h(components.Badge, null, `${api.gameState.getStations().length} stations`);
console.log(typeof icons.Train, typeof charts.LineChart, !!badge);The object and its namespaces hooks, actions, build, gameState, storage, map, cities, trains, stations, popTiming, career and ui are frozen. Mod scripts run in strict mode, so assigning to a member of a frozen object throws.
What's Exposed#
| Member | Page |
|---|---|
api.hooks | hooks: game events, each on* returns an unsubscribe function |
api.actions | actions: money, pause and speed, fares, bonds |
api.gameState | game-state: stations, routes, tracks, trains, demand, money, statistics |
api.ui | ui: placements, notifications, toolbar and floating panels, theme |
api.map | map: MapLibre sources and layers, tile and routing overrides |
api.trains, api.stations | trains, stations: train and station type registries |
api.build | build: blueprints, construction, routes and trains |
api.demand, api.popTiming | demand, pop-timing: demand points, pops and commute timing |
api.storage | storage: per-mod persistence |
api.registerCity, api.cities | cities: custom cities and their data files |
api.mods, api.tutorial | mods, tutorial |
api.utils | utils: React, Lucide icons, UI components, Recharts, i18n, constants, the map |
api.schemas | schemas: Zod schemas for city data files |
api.modifyConstants, api.modifyColors, api.modifyPathfindingRules | constants |
api.registerNewspaperTemplates | content-templates |
api.version, api.reloadMods | reference index |
The shapes the getters return are on types.
Mods folder#
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/metro-maker4/mods/ |
| Windows | %APPDATA%\metro-maker4\mods\ |
| Linux | ~/.config/metro-maker4/mods/ |
The game creates mods/ when it is missing. Settings > Advanced > Mod Manager > Open Mods Folder opens it from inside the game.
How a mod loads#
- On launch, in an idle callback (timeout 2 000 ms), the loader scans every folder in
mods/. Folders whose name starts with.are skipped. A symlink to a folder is accepted. - Each folder needs a
manifest.jsonwithid,nameandversion. A folder without one, or with any of the three missing, is listed in the Mod Manager under its folder name with the error (Manifest missing required fields (id, name, version)for a missing field), its toggle disabled, and never runs. idis a string without/,\or... Any other value is listed as invalid.mainnames the script and defaults toindex.js. Amainwhose resolved path does not start with the mod folder's path fails withInvalid script path.- Only mods on the enabled list in
settings.jsonrun. A new mod starts disabled. minGameVersion,maxGameVersionanddependenciesare read and shown in the Mod Manager. Nothing checks them.- Each enabled script runs once, in the order the folders were scanned, wrapped in a strict-mode function. A script that throws is recorded under Issues in the Mod Manager; the remaining mods still load.
Getting started has the manifest table and the enable steps. Debugging has the reload semantics and the log locations.
Mod repositories#
Related Pages#
- Console: try the API in DevTools.
- First mod: a mod folder, manifest and script.
- API reference: one page per namespace.