Créer un mod
Options du mod et raccourcis
Déclarer les préférences du joueur, lire leurs valeurs typées et laisser le SDK gérer les raccourcis des fenêtres.
Ajouter des options à un mod
Cette API Kotlin/Native appartient à l’édition 0.9 en développement. Un mod enregistre ses objets d’option avec options(...) dans toolMod ou signalMod, y compris la déclaration de plusieurs modèles. Le SDK les présente dans Options → NRF Hub. Un outil qui déclare seulement des fenêtres n’a pas besoin d’ajouter options(...) : ses raccourcis apparaissent automatiquement. Un projet doit utiliser le kit et le SDK d’exécution qui prennent en charge cette API.
| Rubrique dans Options → NRF Hub | Contenu |
|---|---|
| Interface | Préférences déclarées avec options(...) : cases à cocher, valeurs numériques et listes de choix. |
| Raccourcis | Raccourcis des fenêtres déclarées avec window(...), à personnaliser ou à désactiver. |
Seules les rubriques correspondant aux réglages des mods chargés apparaissent. S’il n’y a que des raccourcis, ils s’affichent directement sous le titre Raccourcis ; s’il n’y a que des préférences, elles s’affichent directement sous Interface. Les boutons de navigation apparaissent uniquement lorsque les deux rubriques sont utiles. Chaque rubrique regroupe les réglages par mod et utilise ses libellés ; aucune interface de réglages supplémentaire à créer dans le mod.
Dans un projet Kotlin/Native créé avec le kit, remplacez Entry.kt par cet exemple et utilisez le manifeste ci-dessous. Le plugin génère modInfo depuis mod.json : toolMod(modInfo) garde l’identité Kotlin et celle du paquet cohérentes. Le titre de groupe sera Clock tools dans cet exemple ; metadata(name = tr("mod.name")) fournit séparément le nom traduit dans la liste des mods du jeu.
{
"id": "clock-history",
"name": "Clock tools",
"modId": "ClockHistory",
"version": "0.1.0-alpha.1",
"module": "ClockHistoryMod",
"language": "kotlin-native",
"sdkMin": "0.9.0-alpha.1",
"sdkMaxExclusive": "0.10.0",
"gameSha256": [
"fff49ac21720abfc824c2b4f68b862727630eb0db71cfe1f9ea8f685d0db10ae"
]
}package nimby.mod
import nimby.*
private val showDate = BooleanOption("showDate", tr("options.showDate"), true)
private val historySize = IntegerOption(
"historySize", tr("options.historySize"), defaultValue = 5, minimum = 1, maximum = 20,
)
private val order = ChoiceOption("order", tr("options.order"), listOf(
OptionChoice("newest", tr("options.newest")),
OptionChoice("oldest", tr("options.oldest")),
), "newest")
private val samples = mutableListOf<ToolClock>()
private var session: Pair<String, Long>? = null
fun createMod() = toolMod(modInfo) {
metadata(author = "Your name", name = tr("mod.name"), description = tr("mod.description"))
options(showDate, historySize, order)
window("history", tr("window.history"), shortcut = "F9") { event ->
val currentSession = worldId to generation
if (session != currentSession) {
samples.clear()
session = currentSession
}
when (event.action) {
"open", "sample" -> samples.add(clock())
"refresh" -> Unit
else -> return@window
}
val limit = historySize.value
val newestFirst = order.value == "newest"
val displayDate = showDate.value
while (samples.size > limit) samples.removeAt(0)
val rows = if (newestFirst) samples.asReversed() else samples
val message = rows.joinToString("\n") { sample ->
if (displayDate) sample.dateTime().toString()
else "${sample.elapsedMillis} ms"
}
showWindow(event, message, listOf(
ToolButton("sample", tr("history.sample")),
ToolButton("refresh", tr("history.refresh")),
))
}
onStop {
samples.clear()
session = null
}
}
L’ouverture et le bouton Ajouter une lecture ajoutent une observation de l’horloge. Actualiser l’affichage relit les préférences sans ajouter de lecture. Le joueur choisit le nombre de lignes conservées, leur ordre et le texte affiché. Le mod lit .value pendant le callback, puis limite son historique à vingt lectures maximum. L’historique est effacé lors d’un changement de partie ou de l’arrêt du mod ; les préférences restent enregistrées par le SDK.
{
"fallback": "en",
"languages": {
"en": {
"mod.name": "Clock tools",
"mod.description": "Keep a short history of clock readings.",
"window.history": "Reading history",
"options.showDate": "Show dates",
"options.historySize": "Number of readings",
"options.order": "Reading order",
"options.newest": "Newest first",
"options.oldest": "Oldest first",
"history.sample": "Add a reading",
"history.refresh": "Refresh display"
},
"fr": {
"mod.name": "Outils d’horloge",
"mod.description": "Conserver un court historique des lectures de l’horloge.",
"window.history": "Historique des lectures",
"options.showDate": "Afficher les dates",
"options.historySize": "Nombre de lectures",
"options.order": "Ordre des lectures",
"options.newest": "Plus récentes en premier",
"options.oldest": "Plus anciennes en premier",
"history.sample": "Ajouter une lecture",
"history.refresh": "Actualiser l’affichage"
}
}
}Nommer le groupe, la fenêtre et les options
Le nom du mod et le titre de sa fenêtre ont deux rôles distincts. Dans Raccourcis, le SDK affiche d’abord le groupe du mod, puis le titre de chaque fenêtre et sa combinaison de touches. Voir BB Timechange puis Date et heure décrit donc un groupe et une action, pas deux installations du mod.
| Élément affiché | Source dans le mod | Exemple |
|---|---|---|
| Groupe du mod | toolMod(modInfo) → modInfo.title → mod.json.name | Clock tools |
| Nom dans la liste des mods du jeu | metadata(name = tr("mod.name")) | Outils d’horloge |
| Fenêtre et son raccourci | window(..., title = tr("window.history")) | Historique des lectures |
| Préférence | BooleanOption.label / IntegerOption.label / ChoiceOption.label | Afficher les dates — tr("options.showDate") |
| Valeur d’une liste de choix | OptionChoice.label | Plus récentes en premier — tr("options.newest") |
Le groupe utilise GameMod.title : avec toolMod(modInfo) ou signalMod(modInfo), il vient du champ name de mod.json. mod.name et window.history sont ici des clés de traduction choisies par l’auteur pour les autres libellés. Le SDK n’ajoute pas automatiquement le nom du mod au titre de la fenêtre. Donnez au groupe le nom du produit et à chaque fenêtre un titre court qui décrit sa fonction. Si la clé window.history contient déjà le nom du mod, ce nom sera affiché une seconde fois dans sa rubrique.
metadata(name = ...) sert aux informations du mod destinées à la liste des mods du jeu ; ce paramètre ne remplace pas le title utilisé pour le groupe dans Options → NRF Hub. Utilisez tr(...) et assets/translations.json pour traduire les noms visibles, en gardant les identifiants inchangés.
Choisir le bon type
| Déclaration | Valeur et validation |
|---|---|
| BooleanOption | value est un Boolean. La valeur par défaut est false si elle est omise. |
| IntegerOption | value est un Int compris entre minimum et maximum inclus. La valeur par défaut doit respecter ces bornes. |
| ChoiceOption | Deux à seize OptionChoice distincts. value renvoie l’identifiant stable du choix, jamais son libellé traduit. defaultValue doit désigner un choix existant. |
| window(..., shortcut = ...) | Chaque fenêtre possède automatiquement son raccourci configurable. Une chaîne vide le désactive ; le SDK ouvre la fenêtre et appelle son handler. |
L’id d’une option ou d’un OptionChoice doit commencer par une lettre ASCII, puis utiliser uniquement des lettres ASCII, chiffres, _, . ou -, sur 128 caractères maximum. Les identifiants d’options sont uniques dans le mod ; les identifiants de choix sont uniques dans leur liste. Le préfixe window. est réservé au SDK pour les options de raccourci. Les libellés acceptent tr et sont limités à 256 octets UTF-8 ; description est facultative, accepte tr et permet 1024 octets.
Conservez les mêmes objets d’option que ceux enregistrés avec options(...). .value est en lecture seule, sans lecture du réseau ni du disque. Le SDK actualise les valeurs entre les callbacks ; une modification invalide ne remplace pas les valeurs déjà acceptées. Lire .value dans une règle de signal est possible sans ToolContext.
À la création de l’objet, .value vaut defaultValue. Les préférences enregistrées sont ensuite appliquées par le SDK. Lisez .value au moment de traiter une action ou de calculer une règle : une copie faite à l’initialisation du fichier Kotlin conserverait seulement cette ancienne valeur. Déclarer deux objets avec le même id ne les lie pas entre eux et leur enregistrement en double est refusé.
Confier les raccourcis au SDK
Chaque window(id, title, shortcut, handler) ajoute automatiquement une option de raccourci ; aucune option supplémentaire à déclarer. shortcut est sa valeur par défaut ; le joueur peut la modifier ou la désactiver dans Options → NRF Hub, rubrique Raccourcis, sous le nom du mod. Un identifiant de fenêtre stable conserve la préférence lorsque vous réorganisez les déclarations. Cette version propose les raccourcis d’ouverture des fenêtres, sans callback de touche général.
window("history", tr("window.history"), shortcut = "F9") { event ->
showWindow(event, clock().dateTime().toString(), emptyList())
}L’identifiant history reste indépendant du titre traduit. Pour cet identifiant, le SDK crée l’option réservée window.history ; vous ne la déclarez pas et n’écrivez pas son fichier de préférences. Gardez l’id passé à window stable pour conserver l’affectation, même si le titre ou l’ordre des fenêtres change. Un mod peut déclarer huit fenêtres au maximum ; leurs identifiants sont uniques, de 1 à 128 caractères ASCII parmi lettres, chiffres, _, . et -. Deux fenêtres du même mod ne peuvent pas déclarer le même raccourci initial non vide ; plusieurs raccourcis vides sont permis.
Format : Ctrl+, Alt+, Shift+ dans cet ordre, chacun facultatif, puis une touche. Touches disponibles : A–Z, 0–9, F1–F24, Backspace, Tab, Enter, Escape, Space, Left, Right, Up, Down, Home, End, PageUp, PageDown, Insert et Delete. Exemples : F8, Ctrl+T, Alt+F12, Ctrl+Alt+Shift+F24. Une chaîne vide désactive le raccourci.
Le SDK vérifie les raccourcis du jeu et ceux des mods chargés. Une affectation en conflit est refusée et l’ancienne valeur est conservée. Un conflit déjà présent désactive le déclenchement concerné jusqu’à sa résolution. Si les raccourcis du jeu ne peuvent pas être vérifiés, leur utilisation reste suspendue. Les raccourcis ne doivent pas ouvrir d’outil pendant la saisie de texte, la capture d’un raccourci ou lorsque le jeu est en arrière-plan.
Une combinaison valide n’est pas nécessairement libre : F9 est une valeur d’exemple, pas une touche réservée à votre mod. Les vérifications tiennent compte des affectations actuelles du joueur. Certaines actions natives réservent aussi les variantes avec Ctrl, Alt ou Maj ; ajouter un modificateur ne garantit donc pas l’absence de conflit. Le message Déjà utilisé par indique l’action concernée.
Pour modifier une affectation, le joueur clique sur le raccourci actuel ou Attribuer un raccourci, puis appuie sur la combinaison souhaitée. Échap annule la capture ; Retour arrière ou Effacer le raccourci désactive l’affectation. Ces deux touches servent au dialogue de capture et ne s’y attribuent pas comme un raccourci ordinaire. La fermeture du panneau ou la sortie de Raccourcis annule la capture. Un appui maintenu ne répète pas l’ouverture de la fenêtre.
Conserver les choix du joueur
Les préférences sont propres à l’utilisateur et au mod, communes à ses parties. Le SDK prend en charge leur enregistrement et leur chargement. L’identité du mod est GameMod.id, donc modInfo.id issu du champ id de mod.json quand vous utilisez toolMod(modInfo) ou signalMod(modInfo). Le champ modId du manifeste est une autre identité, destinée à la distribution. Gardez stables id, les identifiants d’options et ceux des choix ; traduire un libellé ne change pas l’identité. defaultValue s’applique lorsqu’aucune valeur compatible n’est enregistrée et lors d’une réinitialisation.
Changer defaultValue ou shortcut dans une nouvelle version ne remplace pas un choix enregistré encore valide. Si une valeur sort des nouvelles bornes numériques ou si son identifiant de choix n’existe plus, le SDK utilise la valeur par défaut de cette option. Rétablir les valeurs par défaut de ce mod concerne toutes ses préférences et tous ses raccourcis, y compris ceux de l’autre rubrique. Si un raccourci par défaut entre en conflit, la réinitialisation est refusée sans appliquer partiellement les autres valeurs.
Avant de distribuer le mod, vérifiez les valeurs initiales, leur conservation après redémarrage, les bornes numériques, le changement de langue, un raccourci désactivé et un conflit volontaire. Vérifiez aussi qu’un changement de partie ne rejoue pas une demande d’ouverture ancienne.