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.

js
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:

js
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#

MemberPage
api.hookshooks: game events, each on* returns an unsubscribe function
api.actionsactions: money, pause and speed, fares, bonds
api.gameStategame-state: stations, routes, tracks, trains, demand, money, statistics
api.uiui: placements, notifications, toolbar and floating panels, theme
api.mapmap: MapLibre sources and layers, tile and routing overrides
api.trains, api.stationstrains, stations: train and station type registries
api.buildbuild: blueprints, construction, routes and trains
api.demand, api.popTimingdemand, pop-timing: demand points, pops and commute timing
api.storagestorage: per-mod persistence
api.registerCity, api.citiescities: custom cities and their data files
api.mods, api.tutorialmods, tutorial
api.utilsutils: React, Lucide icons, UI components, Recharts, i18n, constants, the map
api.schemasschemas: Zod schemas for city data files
api.modifyConstants, api.modifyColors, api.modifyPathfindingRulesconstants
api.registerNewspaperTemplatescontent-templates
api.version, api.reloadModsreference index

The shapes the getters return are on types.

Mods folder#

OSPath
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#

  1. 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.
  2. Each folder needs a manifest.json with id, name and version. 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.
  3. id is a string without /, \ or ... Any other value is listed as invalid.
  4. main names the script and defaults to index.js. A main whose resolved path does not start with the mod folder's path fails with Invalid script path.
  5. Only mods on the enabled list in settings.json run. A new mod starts disabled.
  6. minGameVersion, maxGameVersion and dependencies are read and shown in the Mod Manager. Nothing checks them.
  7. 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#