Development Tools
New in v1.0.0Hot reload, TypeScript support, and debugging utilities
This guide covers development tools that make building mods faster and easier.
Hot Reload#
During development, you can reload all mods without restarting the game:
// Reload all mods (clears mod state and re-executes mod scripts)
await window.SubwayBuilderAPI.reloadMods();What Gets Cleared#
Hot reload clears:
- All registered callbacks (except
mapReady- map is already loaded) - UI components
- Custom layers, sources, and styles
- Custom train types (keeps built-in heavy-metro and light-metro)
- Custom cities (keeps built-in cities)
The mod loader then re-executes all enabled mod scripts.
Development Workflow#
- Make changes to your mod file
- Open browser console (F12 or Cmd+Option+I)
- Run
await window.SubwayBuilderAPI.reloadMods() - Your changes take effect immediately
<Callout type="tip"> Create a keyboard shortcut in your mod for quick reloading:
document.addEventListener('keydown', (e) => {
if (e.key === 'F5' && e.ctrlKey) {
e.preventDefault();
window.SubwayBuilderAPI.reloadMods();
}
});</Callout>
TypeScript Support#
The API is fully typed. If using TypeScript:
import type { ModdingAPI } from '@/app/game/moddingAPI';
declare global {
interface Window {
SubwayBuilderAPI: ModdingAPI;
}
}
// Now you get autocomplete!
window.SubwayBuilderAPI.registerCity({
// TypeScript will validate this
});Type Definitions#
The game exports all its types, so you can reference them in your mod:
import type { City, Station, Route, Train, TrainType, StationType } from '@/app/game/types';<Callout type="info"> Remember: Mods run via new Function(), so ES6 import statements won't work in the mod script itself. Use TypeScript types only for development-time checking. </Callout>
Debugging#
Console Logging#
The Modding API logs all operations with a [Modding API] prefix:
// Check console for:
// [Modding API] City registered: BERLIN
// [Modding API] Hook registered: onDayChange
// [Modding API] UI component registered: my-stats-panelInspect Registered Content#
// List all registered cities
console.table(window.SubwayBuilderAPI.utils.getCities());
// Get current map instance
const map = window.SubwayBuilderAPI.utils.getMap();
console.log('Map loaded:', !!map);
// Check constants/game rules
const rules = window.SubwayBuilderAPI.utils.getConstants();
console.log('Starting money:', rules.STARTING_MONEY);Inspect Game State#
const api = window.SubwayBuilderAPI;
// Stations
const stations = api.gameState.getStations();
console.log('Stations:', stations.length);
console.table(
stations.map((s) => ({
name: s.name,
id: s.id
}))
);
// Routes
const routes = api.gameState.getRoutes();
console.log('Routes:', routes.length);
routes.forEach((r) => {
console.log(`Route ${r.id}: ${r.stationIds.length} stops`);
});
// Trains
const trains = api.gameState.getTrains();
console.log('Trains:', trains.length);
// Budget
const budget = api.gameState.getBudget();
console.log('Money:', budget.money);
console.log('Bonds:', budget.bonds);Load City Data Files#
If you need to read `demand_data.json` or other city files inside a mod, do not use `fetch()` (Electron file:// paths and gz files won’t work reliably). Use api.utils.loadCityData() instead:
const api = window.SubwayBuilderAPI;
const cityCode = api.utils.getCityCode();
const demandData = await api.utils.loadCityData(`/data/${cityCode}/demand_data.json.gz`);
console.log('Pops:', demandData.pops.length);Common Issues#
Mod Not Loading#
- Check the console for errors
- Verify
manifest.jsonhas correctidandmainfields - Ensure the mod is enabled in the Mods menu
Hook Not Firing#
- Verify the hook name is correct (e.g.,
onDayChangenotondaychange) - Make sure the game state triggers your hook (e.g.,
onStationBuiltneeds a station to be built) - Check if an error in your callback is preventing execution
UI Not Appearing#
- Check that the placement ID is valid (e.g.,
'settings-menu','escape-menu-buttons') - Verify your component doesn't throw errors during render
- Use
console.login your component to verify it's being called
City Not Appearing#
- Ensure
registerCityis called before the city select screen loads - Check that data files exist in the correct location
- Verify coordinates are within valid ranges
Data File Validation#
The modding API exposes Zod schemas for validating city data files:
const api = window.SubwayBuilderAPI;
// Validate demand data
const result = api.utils.schemas.DemandDataSchema.safeParse(myDemandData);
if (result.success) {
console.log('✅ Demand data is valid!');
} else {
console.error('❌ Validation errors:', result.error.errors);
}Available Schemas#
| Schema | Validates |
|---|---|
DemandDataSchema | demand_data.json |
BuildingIndexSchema | buildings_index.json (standard) |
OptimizedBuildingIndexSchema | buildings_index.json (optimized) |
RoadsGeojsonSchema | roads.geojson |
RunwaysTaxiwaysSchema | runways_taxiways.geojson |
Tips#
- Check API availability: Always check
window.SubwayBuilderAPIexists before using - Use hooks for timing: Register content in
onGameInitto ensure proper loading - Test incrementally: Add one feature at a time and verify it works
- Check console: Modding API logs all operations with
[Modding API]prefix - Cities need data: Custom cities require data files in
public/data/{CODE}/