Traduction de la page originale en anglais d’OmsiLaunch 0.1.0-beta3. La page anglaise fait foi : en cas de divergence, la page anglaise et le code prévalent.
Cette page est la référence normative de l'API publique managée d'OmsiLaunch 0.1.0-beta3 : l'assembly OmsiLaunch.Api (contrats) et le point d'entrée pour les intégrateurs OmsiLaunchService dans OmsiLaunch.Core. Elle ne documente que ce que fait le code actuel. Tout ce qu'un intégrateur peut appeler, recevoir ou observer est listé ici avec son niveau de stabilité ; tout ce qui n'est pas listé n'est pas une surface d'intégration.
L'inventaire de l'API publique généré liste chaque type et membre public de OmsiLaunch.Api, OmsiLaunch.Core et OmsiLaunch.Process avec sa signature et sa stabilité ; un contrôle bloquant de documentation échoue lorsque l'inventaire et les assemblies divergent. Cette page en explique la sémantique.
Le contrat est figé pour la ligne de protocole 0.1 et le chemin est validé à l'exécution dans research/reports/OMSILAUNCH-RUNTIME-VALIDATION-MATRIX.md.
EXPERIMENTAL
Appelable et testé, mais le contrat ou les preuves d'exécution peuvent changer avant de devenir stables.
PARTIAL
Présent dans le contrat ; seule une partie du comportement est implémentée ou validée (le texte précise laquelle).
INTERNAL
Public dans l'assembly pour des raisons techniques (le pont partage le type) mais pas une surface d'intégration ; peut changer sans préavis.
UNAVAILABLE
Présent dans le contrat mais rejeté par le build actuel.
Assemblies d'implémentation. Leurs types publics sont INTERNAL pour les intégrateurs.
Point d'entrée : OmsiLaunchService et OmsiLaunchRuntimePaths#
public sealed record OmsiLaunchRuntimePaths(string PluginBuildDirectory, string NativeBridgePath, string? ReleaseManifestPath = null);
public sealed class OmsiLaunchService : IOmsiLaunch
{
public OmsiLaunchService(IRuntimePlatform platform, OmsiLaunchRuntimePaths runtimePaths);
}
Paramètre
Valeur valide
Invalide / par défaut
platform
new CurrentWindowsX64Platform() (namespace OmsiLaunch.Process). Il détecte la plateforme, crée le processus OMSI avec CreateProcessW, l'attend et y met fin.
Aucune autre implémentation n'est livrée. Un IRuntimePlatform personnalisé est INTERNAL.
PluginBuildDirectory
Répertoire qui contient les fichiers de référence de la closure du plugin permanent : OmsiLaunch.Plugin.opl, OmsiLaunch.PluginNE.dll, OmsiLaunch.Plugin.deps.json, OmsiLaunch.Plugin.runtimeconfig.json et chaque OmsiLaunch.*.dll de la closure managée (doit inclure OmsiLaunch.Plugin.dll). Dans un paquet installé, il s'agit de <package>\plugins.
Répertoire ou fichier manquant : PlanSessionAsync renvoie un plan non exécutable avec OL_E_RUNTIME_ARTIFACT_MISSING.
NativeBridgePath
Chemin de OmsiLaunch.Native.x86.dll (dans le paquet : <package>\plugins\OmsiLaunch.Native.x86.dll).
Comme ci-dessus.
ReleaseManifestPath
release-manifest.json à côté de OmsiLaunch.exe lorsqu'il est présent. Fournit le SHA-256 attendu de chaque fichier de plugins/ (plugin.integrity.reference = manifest).
null (disposition de développement) : les fichiers installés sont seulement vérifiés quant à leur présence et à leur cohérence avec la closure de référence (plugin.integrity.reference = self). Manifeste mal formé : OL_E_RELEASE_MANIFEST_INVALID.
Le service lit ces chemins à chaque PlanSessionAsync et StartSessionAsync ; il ne copie, ne prépare ni ne supprime jamais de fichiers du plugin (voir plugin permanent). La CLI construit le service exactement ainsi (tools/OmsiLaunch.Cli/Program.cs) :
using OmsiLaunch.Api;
using OmsiLaunch.Core;
using OmsiLaunch.Process;
var package = AppContext.BaseDirectory; // directory that contains OmsiLaunch.exe
var plugins = Path.Combine(package, "plugins");
var manifest = Path.Combine(package, "release-manifest.json");
IOmsiLaunch launch = new OmsiLaunchService(
new CurrentWindowsX64Platform(),
new OmsiLaunchRuntimePaths(plugins, Path.Combine(plugins, "OmsiLaunch.Native.x86.dll"), File.Exists(manifest) ? manifest : null));
Créez un seul service par processus et partagez-le. Stabilité : STABLE_BETA.
StartSessionAsync acquiert le bail d'installation, un sémaphore nommé Local\OmsiLaunch.Installation.<SHA-256 of the upper-cased full root path>, et le conserve jusqu'à ce que le superviseur ait restauré l'installation. Un second démarrage sur la même racine depuis n'importe quel processus de la même session de connexion Windows (logon session) échoue avec OL_E_INSTALLATION_BUSY (signalé comme une session Failed, voir StartSessionAsync). Le bail vaut par session de connexion, pas entre sessions de connexion différentes, et n'est pas libéré tant qu'un autre processus détient un handle sur lui (risque accepté).
Les handles sont locaux au processus
SessionHandle encapsule le Guid de la session. Il n'a de sens que pour l'instance de OmsiLaunchService qui l'a renvoyé. Un handle construit à partir d'un Guid connu dans un autre processus (ou une autre instance du service) produit KeyNotFoundException. Le contrôle inter-processus passe par le plan de contrôle local, pas par les handles.
Appelez toujours CloseAsync
À partir de StartSessionAsync, le processus possède une transaction durable. CloseAsync demande l'arrêt canonique si nécessaire, attend le superviseur (sortie du processus, restauration exacte, libération du bail) et oublie la session. Il doit être appelé sur chaque chemin de sortie, y compris après un état Failed. Sans lui, l'entrée de session reste en mémoire ; la restauration elle-même est effectuée par le superviseur dans tous les cas.
Les sessions en échec restent des sessions
Un démarrage qui échoue après le retour de StartSessionAsync signale SessionState.Failed ; le handle reste valide pour GetStatusAsync/WaitForAsync jusqu'à CloseAsync.
Les plans sont revérifiés
StartSessionAsync recalcule le hash de Omsi.exe et replanifie la spécification ; un plan qui n'est plus exécutable est rejeté avec OL_E_PLAN_NOT_RUNNABLE.
Les handles inconnus ou déjà fermés lèvent KeyNotFoundException (« Unknown OmsiLaunch session. »).
Aucune méthode n'exige une session Running, sauf ExecuteRuntimeAsync.
Les exceptions qui portent un code OmsiLaunch placent le code au début de Exception.Message ("OL_E_PLAN_NOT_RUNNABLE: ..."). La CLI extrait les codes des messages de la même manière (CliProgram.Classify).
Build pris en charge : uniquement Omsi23004_692EBFBF (plus le hash de la liste d'autorisation Steam LAA, accepté ; le gameplay n'est pas validé, il nécessite une véritable installation Steam). Voir compatibilité.
var none = new Dictionary<string, OptionalValue<string>>();
var spec = new LaunchSpec(
Installation: new InstallationSpec(@"C:\OMSI 2"),
World: new WorldSpec(WorldMode.NewMap, OptionalValue<string>.Set(@"maps\Grundorf\global.cfg"), OptionalValue<string>.Unset, OptionalValue<int>.Set(1)),
Date: new DateSpec(DateTimeMode.Unset, OptionalValue<SemanticDate>.Unset),
Time: new TimeSpec(DateTimeMode.Unset, OptionalValue<SemanticTime>.Unset),
PlayerVehicle: OptionalValue<PlayerVehicleSpec>.Unset,
Environment: new EnvironmentSpec(none, none, none, none, none, none, none, none),
Behavior: new LaunchBehaviorSpec());
var plan = await launch.PlanSessionAsync(spec);
if (!plan.IsRunnable) { foreach (var d in plan.Diagnostics) Console.WriteLine($"{d.Code}: {d.Message}"); return; }
var session = await launch.StartSessionAsync(plan);
try
{
var status = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(plan.Spec.Behavior.StartupTimeoutSeconds + 5));
if (status.State == SessionState.Running)
{
var time = await launch.ExecuteRuntimeAsync(session, new RuntimeCommand(session.SessionId, 1, "time.read"), TimeSpan.FromSeconds(5));
Console.WriteLine(time.Succeeded ? $"{time.Values!["hour"]}:{time.Values["minute"]}" : time.ErrorCode);
await launch.StopAsync(session);
}
var final = await launch.WaitForAsync(session, SessionState.Completed, Timeout.InfiniteTimeSpan);
Console.WriteLine(final.State); // Completed, or Failed with diagnostics
}
finally
{
await launch.CloseAsync(session); // always
}
Compiler un LaunchSpec en SessionPlan sans démarrer OMSI : valider la spécification, détecter la plateforme, calculer l'empreinte de Omsi.exe, résoudre les identités de contenu, calculer les mutations de fichiers planifiées, lister les capacités requises et non prises en charge, et décider de IsRunnable. Capacité publique session.plan.
Paramètres
spec : un LaunchSpec entièrement renseigné (voir référence LaunchSpec). Installation, World, Date, Time, Environment (les huit dictionnaires) et Behavior doivent être non null ; les membres optionnels peuvent être null. RootPath doit être un répertoire absolu ; une racine vide est consignée comme OL_E_INSTALLATION_NOT_FOUND, mais la sonde de plateforme sur un chemin vide lève ArgumentException avant que le plan ne soit renvoyé ; ne passez donc jamais une racine vide.
Retour
SessionPlan avec un nouveau SessionId, BuildProfileId = "Omsi23004_692EBFBF" (toujours cette constante, même lorsque l'exécutable ne correspond pas), le Spec d'entrée, Platform, ResolvedContent, TouchedFiles, RuntimeArtifacts (chemins de destination plugins\OmsiLaunch.* plus "OmsiLaunch startup handoff v4"), RequiredCapabilities, UnsupportedRequestedFeatures, PlannedMutations, Diagnostics, IsRunnable. IsRunnable vaut true exactement lorsqu'aucun code de diagnostic ne commence par OL_E_. Les diagnostics informatifs (plugin.integrity.reference avec le message self ou manifest, session_profile.selected) ne rendent jamais un plan non exécutable.
Erreurs portées par le résultat
Chaque erreur de planification est un diagnostic, pas une exception : OL_E_INSTALLATION_NOT_FOUND, OL_E_INSTALLATION_NOT_WRITABLE, OL_E_UNSUPPORTED_OPERATING_SYSTEM, OL_E_UNSUPPORTED_BUILD, OL_E_MAP_NOT_FOUND, OL_E_ENTRYPOINT_NOT_FOUND, OL_E_ENTRYPOINT_REQUIRED, OL_E_SITUATION_NOT_FOUND, OL_E_SITUATION_MAP_NOT_FOUND, OL_E_VEHICLE_NOT_FOUND, OL_E_REPAINT_NOT_FOUND, OL_E_HOF_NOT_FOUND, OL_E_DATE_TIME_APPLY_FAILED, OL_E_INVALID_ARGUMENT, OL_E_CAPABILITY_UNAVAILABLE, OL_E_UNKNOWN_SETTING, OL_E_SETTING_NOT_WRITABLE, OL_E_SESSION_PRESENTATION_INVALID (le message porte le code de l'écran de démarrage/ITX), OL_E_PERMANENT_PLUGIN_MISSING, OL_E_PERMANENT_PLUGIN_HASH_MISMATCH, OL_E_PERMANENT_PLUGIN_MANIFEST_INCOMPLETE (la closure du plugin installée dans plugins\ est vérifiée par rapport au manifeste de version au moment de la planification), OL_E_RUNTIME_ARTIFACT_MISSING (le message peut porter OL_E_RELEASE_MANIFEST_INVALID). Conditions complètes : règles de validation de LaunchSpec.
Exceptions levées
OperationCanceledException si le jeton est déjà annulé à l'entrée (le seul point de contrôle) ; ArgumentException/NotSupportedException pour des chemins racine syntaxiquement invalides ; NullReferenceException/ArgumentNullException pour des membres requis null ; System.Text.Json.JsonException pour un manifeste de version syntaxiquement invalide.
Annulation
Vérifiée une fois à l'entrée. La planification est ensuite un travail synchrone sur le système de fichiers.
Session Running requise
Non.
Modifie l'état d'OMSI
Non.
Modifie le système de fichiers
Non (lit Omsi.exe, les fichiers de contenu, la closure du plugin, le manifeste). Les valeurs des paramètres ne sont pas validées ici (seulement l'existence et l'accessibilité en écriture de la clé) ; une valeur invalide échoue au démarrage avec OL_E_INVALID_SETTING_VALUE.
Transaction / restauration
Aucune.
Limitations
Demander un mode Date/Time/Year autre que Unset, un mode Weather autre que Unset, un champ quelconque de PlayerVehicle, des documents Input, EntrypointIdentity ou WorldMode.LastMapState produit OL_E_CAPABILITY_UNAVAILABLE et un plan non exécutable sur ce build (entrées STATICALLY_PARTIAL / UNSUPPORTED_FOR_CURRENT_PROFILE dans UnsupportedRequestedFeatures).
Stabilité
STABLE_BETA.
Exemple
var plan = await launch.PlanSessionAsync(spec); Console.WriteLine(plan.IsRunnable ? "READY" : string.Join(", ", plan.Diagnostics.Where(d => d.Code.StartsWith("OL_E_")).Select(d => d.Code)));
Démarrer une session OMSI gérée et transactionnelle à partir d'un plan exécutable : acquérir le bail d'installation, récupérer un journal obsolète, valider la closure du plugin permanent, prendre un instantané des fichiers de session et appliquer leurs overlays, créer le handoff de démarrage, le slot de télémétrie et la boîte aux lettres runtime, démarrer Omsi.exe, consigner le processus dans le journal et confier la session à un superviseur en arrière-plan. Capacité publique session.start.
Paramètres
plan : un SessionPlan avec IsRunnable == true. La spécification contenue dans le plan est replanifiée ; seul plan.SessionId est conservé du plan de l'appelant. plan.Spec.Behavior.StartupTimeoutSeconds doit être compris entre 1..600.
Retour
SessionHandle(plan.SessionId) dès que Omsi.exe a été créé et consigné (état WaitingForPlugin), ou dès que le chemin de démarrage a échoué (état Failed). N'attend pas le gameplay ; utilisez WaitForAsync(session, SessionState.Running, ...).
Exceptions levées
InvalidOperationException("OL_E_PLAN_NOT_RUNNABLE") lorsque plan.IsRunnable est faux ; InvalidOperationException("OL_E_PLAN_NOT_RUNNABLE: <codes>") lorsque la nouvelle planification n'est pas exécutable (par exemple Omsi.exe modifié, contenu supprimé, closure du plugin manquante) ; InvalidOperationException("Duplicate session id.") lorsqu'une session avec le même identifiant est encore enregistrée (appelez d'abord CloseAsync) ; ArgumentOutOfRangeException lorsque StartupTimeoutSeconds est en dehors de 1..600 ; OperationCanceledException en cas d'annulation avant ou pendant la nouvelle planification ; plus tout ce que lève PlanSessionAsync. Dans tous les cas où une exception est levée, aucune session n'est enregistrée.
Erreurs portées par le résultat
Tout échec postérieur à la nouvelle planification est intercepté dans le chemin de démarrage : la session est enregistrée, son état est Failed, et ses diagnostics contiennent OL_E_START_SESSION dont le message est le message interne (commençant par le code interne lorsqu'il y en a un) : OL_E_INSTALLATION_BUSY (bail détenu ou processus OMSI journalisé encore vivant), OL_E_PERMANENT_PLUGIN_MISSING, OL_E_PERMANENT_PLUGIN_MANIFEST_INCOMPLETE, OL_E_PERMANENT_PLUGIN_HASH_MISMATCH, OL_E_RELEASE_MANIFEST_INVALID, OL_E_SPLASH_ASSET_MISSING, OL_E_SPLASH_ASSET_DIRECTORY_MISSING, OL_E_SPLASH_FORMAT_UNSUPPORTED, OL_E_ITX_PROFILE_REQUIRED, OL_E_ITX_PROFILE_MISSING, OL_E_ITX_PROFILE_INVALID, OL_E_ITX_TARGET_OUTSIDE_TEXTURE_PATH, OL_E_UNKNOWN_SETTING, OL_E_SETTING_NOT_WRITABLE, OL_E_INVALID_SETTING_VALUE, OL_E_CLOSECHECK_REMOVE_FAILED, OL_E_RECOVERY_BACKUP_CORRUPT, OL_E_RECOVERY_ABSENT_OWNERSHIP_MISMATCH, OL_E_RECOVERY_ABSENT_OWNERSHIP_UNVERIFIED (uniquement lorsque la nouvelle tentative différée avec les overlays de cette session ne parvient toujours pas à prouver la propriété), OL_E_RECOVERY_JOURNAL_REMOVE_FAILED, OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED. Le nettoyage peut ajouter OL_E_PROCESS_CLEANUP_FAILED, OL_E_RESTORE_DEFERRED (sortie d'OMSI non confirmée ; journal conservé) ou OL_E_RESTORE_FAILED. Les échecs ultérieurs sont signalés par le superviseur (voir cycle de vie de la session).
Annulation
Avant/pendant la nouvelle planification : levée d'exception. Ensuite, le jeton est transmis à la transaction et à la création du processus ; une annulation à ce stade est traitée comme tout échec de démarrage (Failed + OL_E_START_SESSION: The operation was canceled.), le processus (s'il a été créé) est arrêté et l'installation restaurée.
Session Running requise
Non.
Modifie l'état d'OMSI
Oui : crée le processus OMSI avec les variables d'environnement OMSILAUNCH_SESSION_ID, OMSILAUNCH_HANDOFF_NAME, OMSILAUNCH_TELEMETRY_NAME, OMSILAUNCH_RUNTIME_CHANNEL, OMSILAUNCH_INTERNET_TEXTURES_MODE.
Modifie le système de fichiers
Oui, dans la racine de l'installation : .omsilaunch\diagnostics\<sessionId>-host.log (rétention : les 50 sessions les plus récentes), .omsilaunch\journal.json, .omsilaunch\backup\<sessionId>\*.bin, .omsilaunch\assets\splash\*.bmp (copié une fois pour l'écran de démarrage géré), overlays de session (correctifs de options.cfg, GUI\NewSplashscreen_*.bmp, Texture\standard.itx), suppressions de session (cibles ITX, Texture\standard.ipr, closecheck), et suppression définitive d'un closecheck obsolète préexistant lorsque SuppressStaleClosecheckWarning vaut true (diagnostic closecheck.stale-removed).
Transaction / restauration
Ouvre la transaction (Prepared → Applied → RuntimeDeployed → HandoffCreated → ProcessStarted). Chaque chemin de sortie de la session aboutit à une restauration. Voir transactions et récupération.
Limitations
Seuls WorldMode.NewMap avec PresentedEntrypointIndex et WorldMode.SavedSituation atteignent le gameplay. WorldMode.LastMapState est UNAVAILABLE. Les demandes de date/heure/météo/véhicule du joueur/entrée n'atteignent jamais cette méthode, car elles sont non exécutables dès la planification.
Stabilité
STABLE_BETA (les cycles de vie NEW_MAP et SAVED_SITUATION sont validés à l'exécution).
Exemple
var session = await launch.StartSessionAsync(plan); var s = await launch.GetStatusAsync(session); if (s.State == SessionState.Failed) Console.WriteLine(s.Diagnostics.Last(d => d.Code.StartsWith("OL_E_")).Message);
Lire l'état sémantique du cycle de vie, les diagnostics collectés jusqu'ici et la liste bornée des événements runtime. Capacité publique session.status.
Paramètres
session : un handle renvoyé par StartSessionAsync et pas encore fermé.
Retour
SessionStatus(SessionId, State, Diagnostics, RuntimeEvents) : un instantané immuable (les tableaux sont copiés sous le verrou de la session). RuntimeEvents n'est jamais null pour une session active.
Exceptions levées
KeyNotFoundException pour les handles inconnus/fermés. Ne lève jamais d'exception par ailleurs.
Annulation
Le jeton est ignoré (l'appel se termine de façon synchrone).
Session Running requise
Non.
Modifie OMSI / système de fichiers / transaction
Non / Non / Aucune.
Stabilité
STABLE_BETA.
Exemple
var status = await launch.GetStatusAsync(session); Console.WriteLine($"{status.State} events={status.RuntimeEvents!.Count}");
Interroger (toutes les 100 ms) jusqu'à ce que la session soit dans l'état state, ou dans un état terminal (Completed, Failed), ou que le timeout expire ; puis renvoyer l'état courant.
Paramètres
state : n'importe quel SessionState. Attendre un état transitoire déjà dépassé (ou jamais défini, voir cycle de vie de la session) revient à attendre un état terminal ou le timeout. timeout : n'importe quel TimeSpan non négatif ou Timeout.InfiniteTimeSpan.
Retour
L'état au moment où l'attente s'est terminée. En cas de timeout, l'état est renvoyé, pas une exception : vérifiez vous-même State. Une attente de Running qui se termine en Failed revient immédiatement avec les diagnostics d'échec.
Exceptions levées
KeyNotFoundException ; OperationCanceledException lorsque le jeton de l'appelant est annulé (seule l'annulation par l'appelant se propage ; le timeout interne, non).
Annulation
Jeton de l'appelant respecté à chaque intervalle de 100 ms.
Session Running requise
Non.
Modifie OMSI / système de fichiers / transaction
Non / Non / Aucune.
Stabilité
STABLE_BETA.
Exemple
var running = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(185)); if (running.State != SessionState.Running) { /* timed out or Failed */ }
Demander l'arrêt canonique. Positionne l'indicateur d'arrêt et revient immédiatement ; le superviseur observe l'indicateur dans sa boucle de 100 ms, appelle TerminateProcess sur Omsi.exe, attend la sortie, marque le journal ProcessExited, restaure chaque fichier appartenant à la session et libère le bail. Il s'agit d'un arrêt forcé : la routine d'arrêt propre à OMSI ne s'exécute pas et OMSI ne réécrit pas options.cfg à la sortie (délibéré, protège la transaction). L'arrêt coopératif par WM_CLOSE n'est pas implémenté (décision produit ; lors de la clôture d'exécution, OMSI ne s'est pas fermé dans les 30 s suivant WM_CLOSE, L05b). Capacité publique session.stop.
Paramètres
session.
Retour
Tâche terminée ; n'attend ni l'arrêt ni la restauration. Utilisez WaitForAsync(session, SessionState.Completed, ...) pour observer la fin.
Exceptions levées
KeyNotFoundException.
Annulation
Jeton ignoré.
Session Running requise
Non. Idempotent ; un arrêt demandé avant le démarrage du superviseur est honoré dès que celui-ci démarre ; un arrêt sur une session terminale est sans effet.
Modifie l'état d'OMSI
Oui : met fin au processus OMSI (code de sortie 1).
Modifie le système de fichiers
Indirectement : déclenche la restauration, la suppression du journal et la suppression des sauvegardes par le superviseur.
Transaction / restauration
Déclenche ProcessExited → Restoring → Restored. Les modifications côté runtime effectuées via ExecuteRuntimeAsync (écritures de l'horloge, véhicules créés en cours de session, variables de script, textures D3D) ne sont pas restaurées ; elles disparaissent avec le processus.
Stabilité
STABLE_BETA.
Exemple
await launch.StopAsync(session); var done = await launch.WaitForAsync(session, SessionState.Completed, TimeSpan.FromMinutes(1));
Libérer le handle du consommateur sans laisser la transaction en suspens : si la session n'est pas terminale, demander l'arrêt canonique ; puis attendre la tâche de cycle de vie du superviseur (sortie du processus, restauration, libération du bail) ; puis oublier la session.
Paramètres
session.
Retour
Se termine lorsque la session est terminale et supprimée. Après le retour, le handle est inconnu (KeyNotFoundException à tout appel ultérieur, y compris un second CloseAsync).
Exceptions levées
KeyNotFoundException ; OperationCanceledException si l'appelant annule pendant l'attente du superviseur. Dans ce cas, la session n'est pas supprimée et le superviseur continue de s'exécuter ; appelez à nouveau CloseAsync.
Annulation
S'applique uniquement à l'attente ; n'annule jamais la restauration.
Session Running requise
Non.
Modifie l'état d'OMSI
Oui lorsque la session est encore active (comme StopAsync).
Modifie le système de fichiers
Indirectement (restauration par le superviseur).
Transaction / restauration
Garantit que la transaction est menée à son terme avant la libération du handle (lorsque le superviseur a été démarré). Pour une session qui a échoué avant le démarrage du superviseur, le chemin de démarrage a déjà restauré ou signalé OL_E_RESTORE_DEFERRED.
Exécuter une opération runtime publique dans le processus OMSI en cours d'exécution via la boîte aux lettres de session à requête unique (mappée en mémoire, 64 KiB, requête liée à l'identifiant de session et à l'identifiant de requête). Le plugin exécute l'opération sur le thread d'interface d'OMSI. Catalogue des opérations : contrôle runtime et capacités.
Paramètres
command.SessionId doit être égal à session.SessionId. command.RequestId : ulong choisi par l'appelant ; utilisez un compteur strictement croissant par processus (les helpers D3D commencent à 30 000, le propriétaire CLI à 10 001/50 000). command.Operation : un identifiant d'opération publique issu de PublicCapabilityRegistry.PublicRuntimeOperationIds (par exemple time.read, road-vehicle.read, d3d.texture.create). command.Arguments : valeurs de type chaîne indexées par des noms ordinaux ; les noms requis pour chaque opération proviennent de PublicCapabilityRegistry.GetRuntimeArguments. timeout : mesuré à partir du moment où la requête est déposée dans la boîte aux lettres (l'attente derrière une autre commande en cours n'est pas comptée). La CLI utilise 5 s (15 s pour road-vehicles.spawn) en tant que propriétaire et 8 s / 30 s en tant que client.
Ordre des vérifications
1. Validation par le registre (avant la recherche de la session) : opération inconnue ou internal.* → résultat Succeeded=false, ErrorCode=OL_E_RUNTIME_OPERATION_UNKNOWN ; argument requis manquant (absent ou composé d'espaces) → OL_E_RUNTIME_ARGUMENT_REQUIRED. 2. Recherche de la session → KeyNotFoundException. 3. command.SessionId != session.SessionId → InvalidOperationException("OL_E_RUNTIME_SESSION_MISMATCH"). 4. État différent de Running → InvalidOperationException("OL_E_SESSION_NOT_RUNNING"). 5. Requête dans la boîte aux lettres. 6. Les valeurs de résultat dont la clé commence par internal_ ou se termine par _address, _pointer, _vmt sont retirées.
Retour
RuntimeCommandResult(SessionId, RequestId, Succeeded, ErrorCode, Values). En cas de succès, Values contient les chaînes sémantiques de l'opération (documentées pour chaque opération dans contrôle runtime).
Erreurs portées par le résultat
OL_E_RUNTIME_OPERATION_UNKNOWN, OL_E_RUNTIME_ARGUMENT_REQUIRED (registre) ; OL_E_RUNTIME_RESPONSE_TOO_LARGE (le résultat du plugin a dépassé la boîte aux lettres ; les résultats de liste bornée sont raccourcis avec truncated=true à la place) ; OL_E_RUNTIME_SETTING_NOT_PERSISTENT (weather.set, toujours) ; chaque code OL_E_D3D_* (avec Values["detail"] et Values["native_status"]) ; et OL_E_RUNTIME_OPERATION_FAILED pour tout autre échec côté plugin. Dans ce dernier cas, le code spécifique ne figure pas dans ErrorCode : c'est le premier jeton de Values["detail"] (par exemple detail = "OL_E_RUNTIME_OBJECT_HANDLE_STALE", exception = "InvalidOperationException"). Codes qui arrivent de cette manière : OL_E_RUNTIME_OPERATION_UNAVAILABLE, OL_E_RUNTIME_ARGUMENT_REQUIRED (vérifications côté plugin), OL_E_RUNTIME_VALUE_OUT_OF_RANGE, OL_E_RUNTIME_VALUE_INVALID, OL_E_RUNTIME_OBJECT_HANDLE_REQUIRED, OL_E_RUNTIME_OBJECT_HANDLE_STALE, OL_E_RUNTIME_SCRIPT_OBJECT_UNAVAILABLE, OL_E_RUNTIME_VARIABLE_NOT_FOUND, OL_E_RUNTIME_VARIABLE_UNAVAILABLE, OL_E_RUNTIME_STRING_VARIABLE_NOT_FOUND, OL_E_RUNTIME_CONSTANT_NOT_FOUND, OL_E_RUNTIME_CONSTANTS_UNAVAILABLE, OL_E_RUNTIME_CURVE_NOT_FOUND, OL_E_RUNTIME_CURVE_EMPTY, OL_E_RUNTIME_CURVE_DEGENERATE, OL_E_RUNTIME_CURVE_INVALID, OL_E_RUNTIME_HOF_UNAVAILABLE, OL_E_RUNTIME_PLAYER_VEHICLE_UNAVAILABLE, OL_E_CAMERA_PRESET_FAMILY_UNSUPPORTED, OL_E_TIME_APPLY_FAILED, OL_E_RUNTIME_BUS_IDENTITY_INVALID, OL_E_MAKEVEHICLE_BUS_NOT_FOUND, OL_E_MAKEVEHICLE_DELTA_ZERO, OL_E_MAKEVEHICLE_DELTA_MULTIPLE, OL_E_MAKEVEHICLE_NATIVE_FAILED, OL_E_RUNTIME_CREATED_OBJECT_NOT_IN_COLLECTION, OL_E_RUNTIME_CREATED_OBJECT_INVALID, OL_E_PLACE_RANDOM_BUS_FAILED, OL_E_RUNTIME_SETTING_UNAVAILABLE. Voir codes d'erreur.
Exceptions levées
KeyNotFoundException ; InvalidOperationException avec OL_E_RUNTIME_SESSION_MISMATCH, OL_E_SESSION_NOT_RUNNING, OL_E_RUNTIME_CHANNEL_CLOSED (boîte aux lettres déjà libérée par le superviseur), OL_E_RUNTIME_CHANNEL_BUSY (le slot contient encore une requête abandonnée), OL_E_RUNTIME_REQUEST_ID_REUSED (une réponse obsolète pour le même identifiant de requête se trouve encore dans le slot) ; TimeoutException("OL_E_RUNTIME_REQUEST_TIMEOUT") ; InvalidDataException("OL_E_RUNTIME_RESPONSE_INVALID") (réponse corrompue, étrangère ou non concordante) ; ArgumentOutOfRangeException lorsque la requête sérialisée dépasse la boîte aux lettres ; OperationCanceledException.
Annulation
Respectée pendant l'attente de la barrière par session et toutes les 20 ms pendant l'interrogation de la réponse. Une annulation en cours de traitement ne réinitialise pas le slot : l'appel suivant sur cette session peut échouer avec OL_E_RUNTIME_CHANNEL_BUSY jusqu'à ce que le plugin publie sa réponse (qui est alors écartée comme obsolète). Préférez le timeout ; un timeout réinitialise le slot et une réponse tardive est détectée et écartée.
Session Running requise
Oui (SessionState.Running) ; sinon OL_E_SESSION_NOT_RUNNING est levée. La boîte aux lettres existe jusqu'à ce que le superviseur la libère pendant la restauration.
Modifie l'état d'OMSI
Dépend de l'opération : les opérations Read, non ; les opérations Write/Action (time.set, camera.set, camera.lock, camera.unlock, road-vehicles.spawn, road-vehicles.place-random, vehicle.variable.set, d3d.texture.*) modifient un état interne au processus qui n'est pas restauré.
Modifie le système de fichiers
Aucune écriture de l'hôte. OMSI peut écrire ses propres fichiers en conséquence (non suivis).
Transaction / restauration
Aucune.
Limitations
Une seule commande en cours par session (les appels sur une même session sont sérialisés). La requête et la réponse sont chacune limitées à 64 KiB moins 8 octets ; les charges utiles de pixels D3D à 48 KiB. internal.road-vehicles.make-basic est INTERNAL et inaccessible. weather.set est UNAVAILABLE. timetable.logs.read n'est pas borné et peut renvoyer OL_E_RUNTIME_RESPONSE_TOO_LARGE sur de grands horaires. camera.lock est EXPERIMENTAL ; il nécessite un véhicule du joueur et est validé à l'exécution (CAM01), bien que la chaîne RuntimeValidation du registre indique encore STATICALLY_VALIDATED. Les handles (rv-NNNNNN, hb-NNNNNN, d3dtex-<session>-<hex>) ont une portée limitée à la session.
Stabilité
Transport et contrat STABLE_BETA ; la stabilité de chaque opération suit PublicCapabilityRegistry (PublicStableBeta → STABLE_BETA, PublicExperimental → EXPERIMENTAL), avec les exceptions ci-dessus.
Exemple
var r = await launch.ExecuteRuntimeAsync(session, new RuntimeCommand(session.SessionId, 42, "road-vehicle.read", new Dictionary<string, string> { ["handle"] = "rv-000001" }), TimeSpan.FromSeconds(5)); if (!r.Succeeded) Console.WriteLine($"{r.ErrorCode} {r.Values?["detail"]}");
Renvoyer l'inventaire des preuves du produit pour une installation : une liste fixe d'entrées Capability(Name, Available, EvidenceState, Reason) maintenue dans OmsiLaunchService. Seule runtime.current-windows-x64 est calculée (à partir de la détection de la plateforme) ; toutes les autres entrées sont constantes.
Paramètres
installation.RootPath : répertoire utilisé pour la sonde de plateforme (l'accessibilité en écriture exige que le répertoire existe, ne soit pas en lecture seule et contienne plugins\). ExpectedExecutableSha256 est ignoré.
Retour
51 entrées, par exemple runtime.time.read (RUNTIME_VALIDATED), runtime.weather.write (false, RUNTIME_PARTIAL), world.last-map-state (false, UNSUPPORTED_FOR_CURRENT_PROFILE), world.date.explicit (false, STATICALLY_PARTIAL), content.maps (STATICALLY_VALIDATED), runtime.d3d.lifecycle.reset (IMPLEMENTED_NOT_RUNTIME_VALIDATED).
Différence avec PublicCapabilityRegistry
PublicCapabilityRegistry.All est le catalogue de la surface de contrôle défini à la compilation (36 descripteurs avec classification, type, routes API et CLI, arguments requis) que l'API et la CLI appliquent ; il ne dépend pas de l'installation. GetCapabilitiesAsync est un rapport de preuves d'exécution (état de validation et raisons). Utilisez le registre pour décider de ce que vous pouvez appeler ; utilisez cette liste pour savoir ce qui a été prouvé. Aucune des deux listes n'est dérivée de l'autre.
Exceptions levées
OperationCanceledException à l'entrée ; ArgumentException pour un chemin racine vide.
Annulation
Vérifiée une fois à l'entrée.
Session Running requise
Non.
Modifie OMSI / système de fichiers / transaction
Non / Non / Aucune.
Stabilité
Contrat d'appel STABLE_BETA ; le contenu de la liste est un inventaire maintenu à la main : PARTIAL.
Exemple
foreach (var c in await launch.GetCapabilitiesAsync(new InstallationSpec(root))) Console.WriteLine($"{c.Name} {c.Available} {c.EvidenceState} {c.Reason}");
Énumérer le contenu installé et renvoyer des identités canoniques utilisables dans un LaunchSpec. La découverte ignore les points d'analyse (reparse points) (les cycles de jonctions ne peuvent pas la bloquer), lit les fichiers OMSI en Windows-1252 (UTF-8/UTF-16 marqués par un BOM respectés) et ne suit jamais les liens symboliques.
Paramètres
query et scope selon le tableau ci-dessous. scope est requis pour Entrypoints (identité de carte), Repaints, FleetNumbers, Registrations (identité de véhicule).
Retour
Liste triée de ContentIdentity(Identity, Kind, DisplayName). Les identités sont des chemins relatifs à l'installation avec des barres obliques inverses ; les comparaisons sont insensibles à la casse.
Exceptions levées
OperationCanceledException à l'entrée ; ArgumentException lorsque Entrypoints est interrogé sans portée ou que la racine est vide ; FileNotFoundException (pas de code OL_E_ ; la CLI la convertit en OL_E_NOT_FOUND) lorsque la carte ou le véhicule de la portée n'est pas installé. Une racine ou un répertoire de contenu manquant produit une liste vide, pas une erreur.
Annulation
Vérifiée une fois à l'entrée.
Session Running requise
Non.
Modifie OMSI / système de fichiers / transaction
Non / Non / Aucune.
Stabilité
Maps, Situations, Vehicles : STABLE_BETA (chaque plan validé à l'exécution est résolu par leur intermédiaire). Entrypoints, Repaints, Hofs, FleetNumbers, Registrations, Addons : EXPERIMENTAL (preuves statiques uniquement).
Exemple
var maps = await launch.DiscoverAsync(new InstallationSpec(root), ContentQueryKind.Maps); var entries = await launch.DiscoverAsync(new InstallationSpec(root), ContentQueryKind.Entrypoints, OptionalValue<string>.Set(maps[0].Identity));
Valeurs et résultats de ContentQueryKind :
Valeur
Portée
Identity
Kind
DisplayName
Maps
aucune
maps\<dir>\global.cfg
map
nom du répertoire de la carte
Situations
aucune
situations\...\<file>.osn
situation
identité de carte référencée par le .osn (peut être null)
Vehicles
aucune
Vehicles\...\<file>.bus
vehicle
[friendlyname] ou nom de fichier
Repaints
identité de véhicule (requise ; sans elle : liste vide)
<cti path>#item:<ordinal>
repaint
nom [item]
Hofs
aucune
Vehicles\...\<file>.hof
hof
null
FleetNumbers
identité de véhicule (requise ; sans elle : liste vide)
chemin source [number] relatif au véhicule
fleet-number
null
Registrations
identité de véhicule (requise ; sans elle : liste vide)
identité de carte (requise ; sans elle : ArgumentException)
<map identity>#entrypoint:<SHA-256 of the 12-line record>
entrypoint
libellé du point d'entrée
Les identités de point d'entrée servent uniquement à la découverte : le chemin de lancement utilise PresentedEntrypointIndex ; passer un EntrypointIdentity rend le plan non exécutable sur ce build (world.entrypoint-identity, RUNTIME_PARTIAL).
Signaler ou achever une transaction durable obsolète (<root>\.omsilaunch\journal.json) laissée par un propriétaire qui a planté. Prend le bail d'installation pendant toute la durée de l'appel, de sorte qu'il ne restaure jamais sous une session en cours de démarrage. Capacité publique session.recover ; CLI /recovery-status et /recover.
Paramètres
installation.RootPath : la racine de l'installation (normalisée avec Path.GetFullPath). restore : false = rapport uniquement ; true = restaurer, vérifier, supprimer le journal et les sauvegardes.
Retour
RecoveryStatus(Pending, Recovered, Diagnostics) : Pending = un journal existait au début de l'appel ; Recovered = une restauration a été demandée, s'est exécutée, et aucun journal ne subsiste ; Diagnostics = notes de restauration (restore.session-artifact-removed avec Data["sha256"], OL_W_RESTORE_FOREIGN_FILE_RETAINED), vide lorsque rien n'a été restauré.
Exceptions levées
InvalidOperationException("OL_E_INSTALLATION_BUSY: another OmsiLaunch owner holds this installation.") lorsque le bail est détenu ; IOException("OL_E_INSTALLATION_BUSY: a journaled OMSI process is still alive.") lorsque le PID + l'heure de création + le chemin de l'exécutable du journal correspondent encore à un processus vivant, ou (journal au-delà de HandoffCreated sans PID) lorsqu'un Omsi.exe quelconque de cette racine est en cours d'exécution ; IOException avec OL_E_RECOVERY_BACKUP_CORRUPT, OL_E_RECOVERY_ABSENT_OWNERSHIP_MISMATCH, OL_E_RECOVERY_ABSENT_OWNERSHIP_UNVERIFIED, OL_E_RECOVERY_JOURNAL_REMOVE_FAILED, ou un message de vérification (« Restore hash mismatch: ... », « Restore presence mismatch: ... ») ; InvalidDataException("Invalid OmsiLaunch journal.") / JsonException pour un journal corrompu ; ArgumentException pour une racine vide ; OperationCanceledException. Chaque fois qu'une exception est levée après le début d'une restauration, le journal est conservé et l'appel suivant rejoue de manière idempotente.
Annulation
Transmise aux écritures du journal/des sauvegardes ; une annulation en cours de restauration laisse le journal en attente.
Session Running requise
Non (il refuse tant qu'un propriétaire est actif).
Modifie l'état d'OMSI
Non.
Modifie le système de fichiers
Uniquement avec restore == true : réécrit les originaux à partir des sauvegardes vérifiées (octets, heure de dernière écriture, heure de création, attributs ; originaux en lecture seule pris en charge ; écriture directe (write-through) + vidage ; aucun *.omsilaunch.tmp laissé), supprime les artefacts de session, supprime journal.json et backup\<sessionId>.
Transaction / restauration
Achève la transaction en attente (Restoring → Restored → journal supprimé).
Stabilité
STABLE_BETA : chemin de rapport et récupération après sortie prématurée (matrice RV-008), récupération après une restauration échouée (clôture d'exécution F01), refus en présence d'un propriétaire actif et dans la fenêtre précédant le PID (S04) et récupération différée antérieure à l'empreinte au démarrage (S05) ; voir état de la validation à l'exécution.
Exemple
var r = await launch.RecoverPendingAsync(new InstallationSpec(root), restore: true); Console.WriteLine($"pending={r.Pending} recovered={r.Recovered}");
Tous les records ci-dessous sont documentés propriété par propriété dans la référence LaunchSpec ; ce tableau fixe l'inventaire des types.
Type
Objectif
Stabilité
LaunchSpec
Record racine de la requête, avec les accesseurs EffectiveYear, EffectiveWeather, EffectiveInput, EffectiveDiagnostics, EffectivePresentation, EffectiveInternetTextures qui substituent des valeurs par défaut aux membres optionnels null.
STABLE_BETA
InstallationSpec
RootPath, ExpectedExecutableSha256 (transporté, non consommé).
Sélection du monde. WorldMode : NewMap = 0, SavedSituation = 1, LastMapState = 2, LastSituation = 2 (alias obsolète de LastMapState ; jamais « le .osn le plus récent »). EntrypointMode : Unset, PresentedIndex, Identity (calculé à partir de WorldSpec.Entrypoint).
Provenance d'un profil de session compilé (Id, Name, Version, Author, PresetId, PresetIndex, PresetName, PackagePath).
STABLE_BETA
LaunchBehaviorSpec
RestoreConfiguration (transporté, la restauration a toujours lieu), SuppressStaleClosecheckWarning, StartupTimeoutSeconds (1..600, 180 par défaut), ShutdownTimeoutSeconds (transporté, non consommé).
SessionId (nouveau Guid par plan), BuildProfileId ("Omsi23004_692EBFBF"), Spec, Platform (RuntimePlatformInfo), ResolvedContent (liste de ContentIdentity : map, vehicle, repaint, hof, situation, situation-map), TouchedFiles (chemins relatifs distincts de PlannedMutations), RuntimeArtifacts, RequiredCapabilities (Capability avec STATICALLY_VALIDATED ou UNAVAILABLE), UnsupportedRequestedFeatures (entrées Capability pour les fonctionnalités demandées mais non prises en charge), PlannedMutations, Diagnostics, IsRunnable.
Un record public : il peut être modifié ou devenir obsolète, c'est pourquoi StartSessionAsync replanifie.
RuntimePlatformInfo
OsFamily, OsVersion, OsArchitecture, HostArchitecture, OmsiArchitecture (X86), PluginArchitecture (X86), CurrentPlatformSupported (Windows 10+, OS x64 et hôte x64), LegacyPlatform (toujours false), Wow64Available, InstallationWritable, ProcessLaunchSupported, PluginRuntimeSupported, NativeInteropSupported, SharedMemorySupported, ExactRestoreSupported (tous égaux à CurrentPlatformSupported).
Capability
Name, Available, EvidenceState, Reason.
Les chaînes de preuve sont du texte libre (RUNTIME_VALIDATED, STATICALLY_VALIDATED, STATICALLY_PARTIAL, RUNTIME_PARTIAL, UNAVAILABLE, UNSUPPORTED_FOR_CURRENT_PROFILE, IMPLEMENTED_NOT_RUNTIME_VALIDATED, RELEASE_IF_CLOSED).
Les mutations de présentation utilisent les clés session-presentation.splash, internet-textures.override, internet-textures.cache, internet-textures.target.
LaunchDiagnostic
Code, Message, Data (dictionnaire de chaînes optionnel).
Les codes commençant par OL_E_ sont des erreurs, OL_W_ des avertissements, tout le reste est informatif.
SessionStatus
SessionId, State (SessionState), Diagnostics, RuntimeEvents.
Les diagnostics de session n'incluent pas les diagnostics du plan.
RuntimeEvent
Type, TimestampUtc (heure de réception par l'hôte), Sequence (à partir de 1, par session), Data.
Limité aux 256 événements les plus récents (les plus anciens sont abandonnés). Le slot de télémétrie est un slot à dernière valeur : les événements émis plus vite que l'interrogation de l'hôte toutes les 100 ms peuvent être manqués. Ce n'est pas un journal sans perte.
Enum de type byte, dans l'ordre de déclaration : Created, ValidatingPlatform, Planning, AcquiringInstallationLock, RecoveringPreviousTransaction, Snapshotting, ApplyingConfiguration, DeployingRuntime, CreatingStartupHandoff, StartingProcess, WaitingForPlugin, PluginBootstrap, StartingWorld, EnteringGameplay, Running, ProcessExited, Restoring, CleaningRuntime, Completed, Failed. ValidatingPlatform, Planning et EnteringGameplay ne sont jamais définis par le service actuel ; Snapshotting est transitoire et pratiquement inobservable. États terminaux : Completed, Failed. Sémantique complète : cycle de vie de la session.
Codec statique utilisé par l'hôte et le plugin pour l'enveloppe de la boîte aux lettres : magic 0x4F4C5243 (« OLRC »), version 1, en-tête little-endian de 72 octets (magic, version, kind 1 = requête / 2 = réponse, longueur totale, Guid de session, identifiant de requête, longueur de la charge utile, SHA-256 de la charge utile) suivi d'une charge utile JSON en UTF-8. SerializeRequest, SerializeResponse, TryDeserializeRequest, TryDeserializeResponse, TryReadRequestId.
INTERNAL : public parce que les deux extrémités du pont le partagent ; pas une surface d'intégration ; le format peut changer avec la version du protocole.
StartupHandoff
(Guid SessionId, string BuildProfileId, WorldMode WorldMode, string MapIdentity, int PresentedEntrypointIndex, bool HeadlessStart, bool PlayerVehicleEnabled, DateTimeMode DateMode, DateTimeMode TimeMode, string EntrypointIdentity, string SituationIdentity) — ce que l'hôte publie à destination du plugin dans le fichier mappé en mémoire OmsiLaunch.Handoff.<sessionId>.
INTERNAL
StartupHandoffWire
Codec : magic 0x4F4C5348, version 4 (lit 3 et 4), en-tête de 64 octets avec intégrité de la charge utile par SHA-256.
INTERNAL
Le plugin rejette un handoff (plugin.request.unsupported → OL_E_CAPABILITY_UNAVAILABLE) sauf si WorldMode vaut NewMap ou SavedSituation, HeadlessStart vaut true, PlayerVehicleEnabled vaut false, les deux modes de date/heure valent Unset, et qu'une situation enregistrée a une identité non vide. Le planificateur applique les mêmes contraintes plus tôt, de sorte qu'un plan exécutable ne déclenche jamais ce rejet.
ProtocolVersion ("0.1"), All (36 entrées PublicCapabilityDescriptor), PublicRuntimeOperationIds (les 48 identifiants d'opération concrets qu'un frontend peut transmettre), IsPublicRuntimeOperation, GetRuntimeArguments, ValidateRuntimeArguments (renvoie PublicRuntimeArgumentValidation), IsInternalResultKey. Appliqué par ExecuteRuntimeAsync, la CLI et le plan de contrôle local.
Wrappers typés au-dessus de ExecuteRuntimeAsync pour les opérations d3d.* (EXPERIMENTAL, capacité PublicExperimentald3d.texture). Ils allouent les identifiants de requête à partir d'un compteur global au processus commençant à 30 000 et utilisent un timeout par défaut de 5 s (sauf GetD3DStatusAsync, qui en exige un).
D3DTextureUpdate(Level, X, Y, Width, Height, Pixels) : x/y 0..4095, largeur/hauteur 1..4096, pixels ≤ 48 KiB (encodés en Base64 lors de la transmission)
Erreurs : un résultat en échec est relevé sous la forme OmsiRuntimeException(Code, detail), où Code est le ErrorCode du résultat (ou OL_E_RUNTIME_OPERATION_FAILED s'il est absent) et le message est "<code>: <Values["detail"]>" ; un résultat réussi sans valeurs, ou une chaîne d'état de périphérique inconnue, lève OmsiRuntimeException("OL_E_RUNTIME_PROTOCOL_MISMATCH", ...). Tout ce que lève ExecuteRuntimeAsync se propage sans modification. La gestion de la réinitialisation du périphérique est validée à l'exécution : une réinitialisation fait passer le périphérique par Resetting avant de revenir à Ready et invalide chaque texture active (OL_E_D3D_STALE_RESOURCE_HANDLE, clôture d'exécution D01) ; GetCapabilitiesAsync signale encore runtime.d3d.lifecycle.reset comme IMPLEMENTED_NOT_RUNTIME_VALIDATED (retard de l'auto-déclaration). La transition Lost ne peut pas être produite depuis l'extérieur du produit et n'est couverte que hors ligne.
var status = await launch.GetD3DStatusAsync(session, TimeSpan.FromSeconds(5));
if (status.State == D3DDeviceState.Ready)
{
var texture = await launch.CreateD3DTextureAsync(session, 8, 8, D3DTextureFormat.A8R8G8B8);
var pixels = new byte[8 * 8 * 4];
await launch.UpdateD3DTextureAsync(session, texture.Handle, new D3DTextureUpdate(0, 0, 0, 8, 8, pixels));
await launch.ReleaseD3DTextureAsync(session, texture.Handle);
}
Success = 0, SessionFailed = 1, InvalidArguments = 2, UnsupportedProfile = 3, NoActiveSession = 4, RuntimeUnavailable = 5, NotFound = 6, OperationRejected = 7, TransactionRecoveryFailed = 8, InternalError = 10. Utilisé uniquement par la CLI (codes de sortie) ; l'API ne termine jamais le processus.
PublicErrorCategory
Constantes de chaîne utilisées dans les enveloppes d'erreur de la CLI et du contrôle : invalid_argument, unsupported_profile, session, runtime, not_found, transaction, internal.
PublicErrorCodes
Un const string par code (143 : 142 erreurs OL_E_ et 1 avertissement OL_W_) et All, le catalogue PublicErrorDescriptor(Code, Category). Catégories : Cli, Compatibility, Content, Installation, InvalidArgument, LaunchSpec, LocalControl, Other, Presentation, Process, Runtime, RuntimeD3D, Session, SessionProfile, Transaction, Warning. Référence : codes d'erreur.
PublicErrorDescriptor
(string Code, string Category).
OmsiRuntimeException
Propriété Code plus message ; levée uniquement par D3DRuntimeApi.
InstallationPaths (identité de l'installation et confinement des chemins)#
Stabilité : STABLE_BETA (fonctions pures, aucune E/S, aucun état OMSI, aucune modification du système de fichiers, aucune participation à une transaction, aucune session Running requise). C'est la définition unique utilisée par le bail d'installation, le nom du pipe de contrôle local, le confinement des ressources des profils de session, la validation des cibles des textures Internet et le chemin du modèle d'apparition runtime.
Membre
Comportement
string NormalizeRoot(string root)
Path.GetFullPath(root) sans séparateurs finaux, sauf pour une racine de lecteur (C:\), qui est conservée. Résout les segments . et .., traite / et \ de la même manière et fusionne les séparateurs répétés. Ne résout pas les jonctions ni les liens symboliques. Lève ArgumentException pour une racine null ou vide.
string IdentityKey(string root)
NormalizeRoot(root) en majuscules. Les écritures lexicalement équivalentes d'une même racine (C:\OMSI, C:\OMSI\, C:\OMSI\., C:\foo\..\OMSI, c:\omsi) partagent une même clé ; des racines différentes (C:\OMSI-A, C:\OMSI-B) jamais.
bool TryGetContainedRelativePath(string root, string candidate, out string relativePath)
Résout candidate (relatif à root, ou absolu) et renvoie true uniquement lorsqu'il se trouve strictement sous root ; relativePath est l'écriture canonique avec \. Utilise les segments de Path.GetRelativePath, de sorte qu'un voisin tel que C:\OMSI-A\x n'est jamais à l'intérieur de C:\OMSI ; la racine elle-même, les autres volumes et les échappements par .. renvoient false.
Découpe sur / et \, en supprimant les segments vides.
var same = InstallationPaths.IdentityKey(@"C:\OMSI") == InstallationPaths.IdentityKey(@"c:\foo\..\OMSI\"); // true
InstallationPaths.TryGetContainedRelativePath(@"C:\OMSI", @"Sceneryobjects\\x\texture\a.tga", out var relative); // true, "Sceneryobjects\x\texture\a.tga"
Stabilité : EXPERIMENTAL. Ces types compilent un profil de session YAML (<root>\.omsilaunch\session-profiles\<id>\profile.yaml, schéma omsilaunch.session-profile/v1) en LaunchSpec. L'option CLI /predefined-profile:<id> /predefined-profile-index:<n> utilise exactement ces appels ; un intégrateur peut les utiliser pour démarrer un profil via l'API.
Membre
Comportement
SessionProfileCompiler.Load(string installationRoot, string id, int presetIndex) → SessionProfilePackage
Lit et valide le paquet. id doit être un simple nom de répertoire (OL_E_SESSION_PROFILE_PATH_ESCAPE sinon) ; presetIndex est compris entre 1..5 (OL_E_SESSION_PROFILE_PRESET_NOT_FOUND). Fichier manquant : OL_E_SESSION_PROFILE_NOT_FOUND ; taille supérieure à MaxBytes (256 KiB), YAML invalide, ancres, clés inconnues ou id différent du nom du répertoire : OL_E_SESSION_PROFILE_INVALID ; autre schema : OL_E_SESSION_PROFILE_SCHEMA_UNSUPPORTED. Les chemins des ressources sont confinés au paquet (OL_E_SESSION_PROFILE_PATH_ESCAPE, OL_E_SESSION_PROFILE_ASSET_MISSING). Chaque échec est une SessionProfileException.
Renvoie baseline avec le profil appliqué : pour NewMap, le bloc new du profil (carte, point d'entrée, et toute date/heure/année/météo, qui rendent le plan non exécutable sur ce build) ; les settings du préréglage fusionnés par-dessus Environment.General ; les Presentation, InternetTextures et Behavior du préréglage lorsqu'ils sont présents ; et SessionProfile = profile.Metadata. Pour NewMap, il vérifie aussi la carte par rapport à la liste compatibility du profil (OL_E_SESSION_PROFILE_MAP_MISMATCH).
La vérification de compatibilité pour les autres modes (pour SavedSituation, la carte est lue dans le .osn). La CLI l'appelle une fois le WorldSpec final construit.
IOException avec Code (l'un des codes OL_E_SESSION_PROFILE_*) ; le message est "<code>: <message>".
La CLI rejette en outre les options de ligne de commande qui entrent en conflit avec le profil (OL_E_SESSION_PROFILE_OVERRIDE_CONFLICT) ; cette vérification ne fait pas partie du compilateur. Voir profils de session pour l'ordre de fusion complet.
static async Task<SessionPlan> PlanProfileAsync(IOmsiLaunch launch, LaunchSpec baseline, string installationRoot, string profileId)
{
// baseline: a LaunchSpec for installationRoot with World.Mode = WorldMode.NewMap (see the complete example).
var profile = SessionProfileCompiler.Load(installationRoot, profileId, presetIndex: 1);
var spec = SessionProfileCompiler.Apply(profile, baseline, WorldMode.NewMap);
return await launch.PlanSessionAsync(spec); // plan.Spec.SessionProfile carries the provenance
}
STABLE_BETA en tant que type du paramètre de constructeur de OmsiLaunchService
Détecte la plateforme, vérifie l'accessibilité en écriture, démarre, observe, arrête et attend Omsi.exe. Passez new CurrentWindowsX64Platform() ; l'implémenter vous-même n'est pas pris en charge.
CurrentWindowsX64Platform
STABLE_BETA
La seule implémentation : hôte Windows x64, CreateProcessW pour Omsi.exe, TerminateProcess pour l'arrêt canonique. Ses méthodes sont appelées par le service ; les intégrateurs se contentent de le construire. Membres (partagés avec IRuntimePlatform) : Detect(root) renvoie le RuntimePlatformInfo du plan ; ValidateCurrent(info) lève OL_E_UNSUPPORTED_OPERATING_SYSTEM / OL_E_UNSUPPORTED_OS_ARCHITECTURE / OL_E_PLATFORM_CAPABILITY_MISSING lorsque l'hôte ne peut pas exécuter de session ; IsInstallationWritable(root) sous-tend OL_E_INSTALLATION_NOT_WRITABLE ; StartAsync(request, sha256) crée Omsi.exe et consigne l'identité du processus (PID, heure de création, chemin et hash calculé par le service ; OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED) ; HasExited, WaitForExitAsync et Terminate l'observent et y mettent fin.
Publics dans l'assembly parce que le service et les tests les partagent. Pas une surface d'intégration ; LaunchedProcess encapsule en interne les handles de processus et de thread d'OMSI (ce ne sont pas des membres publics) et n'est jamais renvoyé par IOmsiLaunch.
OmsiLaunchService prend en charge les appels concurrents sur des sessions différentes : les sessions résident dans un ConcurrentDictionary et chaque modification propre à une session se produit sous le verrou privé de cette session.
Les appels concurrents sur une même session sont sûrs mais sérialisés là où c'est nécessaire : ExecuteRuntimeAsync prend une barrière par session, de sorte qu'une seconde commande attend la première (son timeout commence lorsqu'elle est déposée).
Le superviseur s'exécute sur une tâche du pool de threads (Task.Run) depuis le retour de StartSessionAsync jusqu'à ce que la session soit terminale ; il interroge la télémétrie et le processus toutes les 100 ms. Les appelants n'exécutent jamais de code du superviseur.
StopAsync et GetStatusAsync se terminent de façon synchrone et peuvent être appelés depuis n'importe quel thread, y compris dans un gestionnaire ProcessExit (la CLI le fait avec un budget de 4 s).
Aucun appel de l'API n'est lié à un thread ; aucun n'exige de contexte de synchronisation.
Aucun IntPtr, nint, handle Win32, adresse native, pointeur de VMT ni objet processus. Les valeurs de résultat dont les clés commencent par internal_ ou se terminent par _address, _pointer, _vmt sont supprimées avant qu'un résultat ne quitte ExecuteRuntimeAsync.
Aucune opération runtime internal.* : internal.road-vehicles.make-basic est InternalOnly dans le registre et renvoie OL_E_RUNTIME_OPERATION_UNKNOWN depuis l'API et la CLI.
Aucune lecture ni écriture brute de la mémoire d'OMSI, aucun accès au niveau fichier à l'installation au-delà de ce que déclare un LaunchSpec.
Aucun handle inter-processus : le plan de contrôle local est la seule voie inter-processus, et il n'accepte que session.status, session.events, session.stop et runtime.execute.
Pas d'arrêt coopératif d'OMSI, pas de LAST_MAP_STATE, pas d'application de date/heure/météo/véhicule du joueur, pas d'overlays de documents clavier/manette sur ce build.
Contenu de la liste de GetCapabilitiesAsync, membres de spécification date/heure/météo/véhicule du joueur/entrée, DiagnosticsSpec, ExpectedExecutableSha256, RestoreConfiguration, ShutdownTimeoutSeconds
PARTIAL
RuntimeCommandWire, StartupHandoff, StartupHandoffWire, implémentations de IRuntimePlatform, toutes les assemblies d'implémentation