Maintenance
Write a responsive, independent mod
Bound useful work, handle recovery and measure without confusing loading, computation and display.
What the SDK handles
The SDK supervises compatible native mods. It can stop a mod that crashes or whose callback stops responding while other mods keep running. The mod-author contract stays simple: use public functions, bound each calculation and return. After a fault stops a mod, fix the cause and start a new test session; do not assume that the callback will be replayed automatically.
A callback does bounded work, then returns
| When | Useful structure |
|---|---|
| Mod declaration | Prepare models, settings, image descriptions and fixed rules once. Keep identifiers stable. |
| Signalling observation | Use the supplied network and observations. Build useful indexes once per batch; avoid scanning all signals for every signal. |
| service / onTick | Tool callbacks are serialized. Calculate on request; reread nothing while idle. During a preview, renew copied positions. During a command, poll its ticket. |
| onStop / worldId / generation | Release mod data on stop; invalidate plans, indexes and events from another session. Retain no ToolContext beyond its callback. |
An observation interval is a target cadence, not a frequency guarantee. If a calculation exceeds that interval, do not launch a catch-up burst. At high speed, simulated time advances faster than callbacks and visible frames: an animation cannot promise to display every phase.
Request the data you need
| Need | In a native tool | In a JVM application |
|---|---|---|
| Clock only | ToolContext.clock() | Game.clock.read() |
| Train data families | ToolContext.trains(TrainQuery(...)) | Game.trains.snapshot(query = TrainQuery(...)) |
| Network geometry | ToolContext.network() | Game.snapshot() |
Request passengers, timetables, tags, characteristics or compositions only when your feature uses them. A full map is unnecessary for a date form. Conversely, do not replace a required fresh read with a cache based only on elapsed time: a world, track or source may have changed. Missing data remains unknown, not zero or an invented empty list.
Copied results are useful for calculation and display. Within a native callback, already-covered train requests can reuse that callback’s read; network() and a time change invalidate this reuse. In a JVM application, each capture request makes a fresh read. Explicitly open and close your connection and discard caches when it changes.
Verify behaviour before claiming a gain
Outside the game, test a slow callback, an exception, unknown data and repeated temporary refusals, followed by recovery. Check that other features continue, queues stay bounded and a placement operation is submitted only once. Measure read time, mod computation and actual observed cadence separately. An average alone hides spikes.
For an in-game test, retain package versions and hashes, the reference save, before/after phases and logs from the correct launch. Wait for loading to finish before recording the baseline. Startup counters often include that phase: compare differences between known timestamps. Keep speed and rendering settings consistent between runs. A responding click or isolated snapshot does not measure every frame’s duration.
| Measurement | What it establishes |
|---|---|
| Targeted read | Success rate and duration of useful requests; compare median, 95th percentile and maximum. |
| Simulation clock | Ratio of game time to elapsed real time. The selected UI speed does not prove the achieved speed. |
| Driving and appearance | Test train passages at signals, produced decisions and actually visible frames separately. A fast read does not prove instant display. |
| Recovery | After the fault, check that other features progress and delays return to their usual range without replaying an uncertain mutation. |