Lire et agir
Observer les trains : requêtes, identités et états
Construire des fiches de trains avec les seuls groupes utiles et conserver le sens des données inconnues.
Choisir le point d’entrée
Vous allez obtenir un lot de trains, joindre leurs informations puis préparer des valeurs d’affichage. Un outil Native doit déjà recevoir un ToolContext actif ; une application JVM doit déjà posséder un Game ouvert. Les résultats sont des copies conservables, mais le ToolContext appartient uniquement au callback qui l’a fourni.
| Besoin | Native | JVM |
|---|---|---|
| Lot de données de trains | context.trains(query) | game.trains.snapshot(query = query) |
| Joindre un train identifié | snapshot[trainId] | snapshot.train(trainId) |
| Plan de ligne d’un train | context.linePlan(trainId) | game.trains.snapshot(selectedTrain = trainId, query = query) |
| Mesure de conduite ciblée | Observations du callback de conduite | game.trains.read(trainId) |
Composer une requête selon l’écran
TrainQuery choisit les données supplémentaires. Déclarez ensemble tous les besoins du calcul puis réutilisez le lot. Les valeurs par défaut incluent le service et les lieux ; désactivez-les pour un écran qui ne les utilise pas. En Native, une requête déjà couverte dans le callback réutilise sa copie ; une demande plus riche peut déclencher une nouvelle lecture.
| Option | Défaut | Ce qu’elle demande |
|---|---|---|
| includeService | true | Service et état opérationnel associés au train. |
| includeLocations | true | Voies et gares pour les jointures de localisation ; ne demande pas la table des quais. |
| includeTimetables | false | Affectation et échéances ; implique le service et les voies/gares même si includeLocations vaut false. |
| includeLines | false | Catalogue des lignes, identités et relations parentales disponibles. |
| includeTags | false | Tags et déclarations des lignes ; implique le catalogue des lignes. |
| includeCharacteristics | false | Caractéristiques des profils configuré et actuel. |
| includeComposition | false | Composition ordonnée et catalogue des modèles de véhicules. |
| includePassengers | false | Nombre observé d’occupants ; indépendant de la capacité. |
La capture spécialisée ne demande pas la carte complète, les signaux, les quais, les occupations, les réservations ou le chemin réservé. Des tables de réseau vides dans ce profil ne prouvent donc pas que le monde est vide. Pour les quais en JVM, demandez game.snapshot() et utilisez Observation.platform. Demandez le réseau uniquement si votre fonctionnalité en a besoin.
Faire les jointures sans perdre les inconnues
TrainId, LineId, StationId, TrackId, TimetableId, TagId et VehicleModelId expriment la nature d’une identité. Leur value est opaque : ne la découpez pas et ne la remplacez pas par un nom. Utilisez les recherches indexées du lot : get et line en Native, train, line et station en JVM. Les véhicules portent directement leur model ; inutile de le rechercher pour chaque fiche.
| Situation | Traitement attendu |
|---|---|
| Groupe non demandé | Ne pas afficher « aucun ». Demander ce groupe si l’écran en a besoin. |
| Objet ou propriété nullable absent | Conserver « inconnu » ou « indisponible » ; la requête ne suffit pas à garantir sa présence. |
| Liste nullable obtenue et vide | Aucun élément dans cette liste observée ; ce constat ne s’étend pas aux tables non demandées. |
| Identité présente, jointure absente | Conserver l’identité et afficher un libellé de remplacement sans fabriquer l’objet joint. |
En Native, Train rassemble les parties disponibles : une demande de localisation peut fournir un TrainService partiel avec présence et lieux, sans état de service. Les demandes du callback se cumulent ; une option false ne retire pas un groupe déjà lu. En JVM, Observation.train(id) retourne un TrainRecord regroupant train, service, details et metadata ; le service y reste absent si son groupe n’a pas été demandé. La présence d’un train ne garantit donc pas toutes ses parties enrichies.
Distinguer mouvement, état et alerte
La vitesse mesurée décrit le mouvement. TrainState décrit un état opérationnel et TrainAlert un signalement ; aucun de ces champs ne constitue une permission de franchir un signal. Interprétez chaque enum dans son domaine, sans attribuer un ordre de gravité à sa position dans entries.
| Valeur | Sens |
|---|---|
| null | Donnée indisponible ou non demandée à cet emplacement. |
| Unknown | Le jeu rapporte explicitement son état inconnu. |
| Other | Une valeur observée ne correspond pas aux catégories nommées du SDK. |
| TrainAlert.None | Aucune alerte rapportée ; ce n’est pas une autorisation de mouvement. |
Construire des fiches prêtes à afficher
package wiki.trainqueries
import nimby.*
data class TrainCard(
val id: TrainId,
val name: String,
val speedKmh: Double?,
val state: TrainState?,
val alert: TrainAlert?,
val positionStation: Station?,
val serviceStation: Station?,
)
// Appeler depuis un callback d'outil ; ne pas conserver son ToolContext.
fun readTrainCards(context: ToolContext): List<TrainCard> {
val snapshot = context.trains(TrainQuery())
return snapshot.trains.map { train ->
TrainCard(
train.trainId, train.name, train.speedKmh,
train.service?.state, train.service?.alert,
train.position?.station, train.service?.locationStation,
)
}
}
// null reste inconnu ; une vitesse nulle connue vaut bien 0.0.
fun measuredSpeedKmh(card: TrainCard): Double? = card.speedKmh
package wiki.jvmtrainqueries
import fr.nimby.sdk.*
import java.nio.file.Path
data class TrainCard(
val id: TrainId,
val name: String,
val speedKmh: Double?,
val state: TrainState?,
val alert: TrainAlert?,
val positionStation: Station?,
val serviceStation: Station?,
)
fun readTrainCards(game: Game): List<TrainCard> {
val snapshot = game.trains.snapshot(query = TrainQuery())
return snapshot.trains.mapNotNull { train ->
val record = snapshot.train(train.trainId) ?: return@mapNotNull null
TrainCard(
train.trainId, train.name, train.speedKmh,
record.service?.state, record.service?.alertState,
record.positionStation, record.locationStation,
)
}
}
// Exemple ponctuel ; une application conserve sa connexion entre deux lectures.
fun readOnce(sdkLibrary: Path, gamePid: Int): List<TrainCard> =
Nimby.connect(sdk = sdkLibrary, processId = gamePid).use(::readTrainCards)
Les fonctions renvoient des TrainCard composées uniquement de valeurs copiées. La gare de position et la gare du service sont deux informations distinctes : un train peut rouler entre gares tout en ayant une destination de service. L’interface peut afficher un tiret pour une valeur inconnue sans modifier le résultat SDK.
Conserver une copie sans la croire actuelle
Une copie reste lisible après le callback ou la lecture, mais elle ne se met pas à jour. En Native, worldId et generation décrivent son contexte ; capturedAtMillis date la capture et ageMillis est son âge au moment de la copie, pas un compteur qui progresse. Effacez les calculs associés lors d’un changement de monde ou de génération.
En JVM, conservez les jointures à l’intérieur d’un même Observation et invalidez votre cache lors d’un changement de partie ou de connexion. processId et gameHash donnent un contexte de processus et de version ; ils ne sont pas une identité durable de sauvegarde. Une opération ultérieure doit vérifier ses propres préconditions actuelles.