NRF SDK 0.9

Creating a mod

Generate the package from Kotlin

Turn a Kotlin project into a package containing its identity, models and every image.

What you write and what is generated

Prerequisites: a configured project and at least one signalModel to assemble, or a toolMod with a service or window. The plugin calls createMod to read declarations and generate the catalogue. It does not launch the game or call rules, services or onTick. Keep createMod and initializers free of game access and file writes.

SourcePurpose and destination
mod.jsonIdentity, name, version, module and compatibility. The Hub reads it before compilation. The plugin generates nimby.mod.modInfo: use that identity in signalMod(modInfo) or toolMod(modInfo).
metadata(author, description, name)Author, description and optional display name. name and description accept tr; without name, the name comes from modInfo. The complete version, including its alpha suffix, comes from mod.json.
construction(states, name, kind, catalogueName)Texture catalogue and construction-menu entry. One declaration per signal model.
assets/ · imgs/ · config/Files copied into the package. Declare assets/closed.svg as closed.svg; imgs/signals/closed.svg keeps its path.
mod.txt · nrf-metadata.json · nrf-mod.ini · project.jsonGame catalogue, metadata translations, loader manifest and Hub manifest. These are generated files: do not edit them, as the next build replaces them.
Assemble the mod
import nimby.*
import nimby.mod.modInfo

fun createMod() = signalMod(modInfo) {
    metadata(author = "Your name", description = "My custom signals.")
    signal(firstSignal)
}

Catalogue parameters

Inside the signalModel block
construction(
    states = listOf("closed.svg", "open.svg"),
    name = "Mon signal",
    kind = "path"
)
ParameterExact use
states: List<String>Between 1 and 256 images in their permanent order. The SDK does not guess paths returned by images or appearance. Also declare both images of each blinking animation.
name: StringName in the construction menu. Defaults to the model title. Accepts plain text or tr: generation resolves English, then the JSON fallback language.
kind: String = "path"Native signal kind, written into SignalTemplate. Our signals use path. This field decides neither indication nor speed; another kind requires checking game support.
catalogueName: String = nameTexture-set name in the game’s native selectors; it may differ from the constructible signal name.
size: Int = 0 · left: Boolean = falseInitial construction choices: size from 0 to 4, and left side when left=true. For a size-4 model on the left, declare both values explicitly. These are catalogue defaults, not changes to existing placed signals.
nameKey / catalogueNameKey: String?Game localization keys for an existing catalogue. With tr(...) in name or catalogueName, leave its corresponding key null: the two mechanisms cannot be combined for the same name.

Keep textureSet and the states order of a published catalogue, including old images still referenced by saves. Changing a file at an existing index changes that appearance; reordering indices can assign the wrong image to existing signals.

Build and inspect the result

Project commands
.\gradlew.bat generateDebugGameManifest
.\gradlew.bat windowsTest packageMod

The generated file is in build/gradle/generated/game/debug/mod.txt or release/mod.txt. The assembled package contains mod.txt at its root. Native tests also receive the generated catalogue. A missing file, an out-of-package path, an ambiguous resource, a duplicate catalogue or an old assets/mod.txt blocks the build.

Before activation, open the package and check image paths, the mod name, models and their initial side and size choices. Test every indication, including fallbacks and both phases of blinking. Generated files are rebuilt from Kotlin; do not fix them manually.