Creating a mod
Prepare effective network settings
A pure transformation before rules: work-zone range, derived values and tests without a game.
What prepareNetwork may change
Declare prepareNetwork in signalMod when several signals participate in one rule. The callback receives the mod’s observed signals before decisions. It returns effective settings used for this evaluation. It saves no settings, builds no signals and changes no game observations. A work-zone range, for example, can activate an option on following signals only in this copy.
| Preserve | Transform |
|---|---|
| Entry count and order, complete ID, model, nextSignal link, observation and availability. | Effective settings values, copying only signals that change. |
| The distinction between unknown, occupied and clear, and boundaries absent from the mod’s network. | Explicit domain policy: range, priority between sources, and handling of unknown data. |
Work-zone source and integer range
package wiki.prepared
import nimby.*
val work = Checkbox("work", "Work zone", "Apply this model's work-zone rule.")
val workBlocks = NumberSetting("workBlocks", "Following blocks", maximum = 64,
defaultValue = 0, visibleWhen = work.name)
enum class Aspect { Closed, Open }
enum class Reason { Unknown, Clear, Work }
val model = signalModel(
SignalType("example.work", "Work signal", "example_work", checkboxes = listOf(work)),
fallback = Indication(Aspect.Closed, Reason.Unknown)) {
number(workBlocks)
construction(listOf("closed.svg", "open.svg"))
rules {
if (!fresh || !routeKnown || block != Occupancy.Clear || observation.forcedStop || observation.lampFailed)
Indication(Aspect.Closed, Reason.Unknown)
else if (enabled(work)) Indication(Aspect.Closed, Reason.Work)
else Indication(Aspect.Open, Reason.Clear)
}
images { if (it.aspect == Aspect.Open) "open.svg" else "closed.svg" }
driving { if (it.aspect == Aspect.Open) AutomaticDriving.clear() else AutomaticDriving.stop() }
}
// Example policy: propagate along nextSignal, not physical distance.
// Zero means source only. No derived setting is written to persistent storage.
fun effectiveWorkSettings(signals: List<Signal>): List<Signal> {
val byId = signals.associateBy { it.id }
val affected = HashSet<Long>()
for (source in signals) {
if (source.type != model.type.id || source.settingsStatus != SettingsStatus.Present ||
!source.observation.fresh || source.settings[work.name] != true) continue
var current: Signal? = source
val seen = HashSet<Long>()
repeat(workBlocks.read(source.settings) + 1) {
val signal = current ?: return@repeat
if (!seen.add(signal.id) || signal.type != model.type.id ||
signal.settingsStatus != SettingsStatus.Present || !signal.observation.fresh) {
current = null
return@repeat
}
affected.add(signal.id)
current = byId[signal.nextSignal]
}
}
return signals.map { signal ->
if (signal.id in affected && signal.settings[work.name] != true)
signal.copy(settings = signal.settings + (work.name to true))
else signal
}
}
fun createPreparedMod() = signalMod("prepared-example", "Prepared settings") {
signal(model)
prepareNetwork(::effectiveWorkSettings)
}
The example chooses a simple policy: workBlocks is zero for the source alone, then counts nextSignal links. It builds an ID lookup once, visits at most 65 signals per source and stops at cycles, other models or unavailable data. This is neither a distance in metres nor route planning. The original input stays intact; NumberSetting.read and withValue avoid knowledge of numeric storage.
For a loadable package, add closed.svg and open.svg under assets and use this model from your signalMod(modInfo) entry point, with metadata. The snippet’s IDs and paths are examples: choose your own and keep them stable after distribution.
Test derived data
In a test, call mod.prepareObservedNetwork(input) to inspect prepared settings through the SDK guards, or mod.evaluateNetwork(input) to check final decisions. Test a zero range, a limit, a cycle, a missing boundary, a removed source and an unavailable observation. Also compare the input after the call to prove it was not mutated. Do not apply preparation twice to the same derived result.