Référence de l'API publique (OmsiLaunch.Api)

Documentation de la version v0.1.0-beta.3Voir la source sur GitHub

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.

Pages associées : référence LaunchSpec, codes d'erreur, cycle de vie de la session, transactions et récupération, contrôle runtime, capacités, plan de contrôle local, codes de sortie, état de la validation à l'exécution.

Vocabulaire de stabilité#

NiveauSignification sur cette page
STABLE_BETALe 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.
EXPERIMENTALAppelable et testé, mais le contrat ou les preuves d'exécution peuvent changer avant de devenir stables.
PARTIALPrésent dans le contrat ; seule une partie du comportement est implémentée ou validée (le texte précise laquelle).
INTERNALPublic dans l'assembly pour des raisons techniques (le pont partage le type) mais pas une surface d'intégration ; peut changer sans préavis.
UNAVAILABLEPrésent dans le contrat mais rejeté par le build actuel.

Vue d'ensemble des assemblies#

AssemblyRôle pour les intégrateurs
OmsiLaunch.ApiContrats purs : records, enums, IOmsiLaunch, registre des capacités, catalogue d'erreurs, formats de transmission, helpers D3D. Ne contient aucun IntPtr, nint, handle Win32, adresse native ni objet processus.
OmsiLaunch.CoreOmsiLaunchService (l'implémentation de IOmsiLaunch), OmsiLaunchRuntimePaths, SessionPlanner, LaunchValidation, SessionProfileCompiler.
OmsiLaunch.ProcessIRuntimePlatform et CurrentWindowsX64Platform (le seul adaptateur de plateforme), InstallationLease. Nécessaire pour construire le service.
OmsiLaunch.Configuration, OmsiLaunch.Content, OmsiLaunch.Interop, OmsiLaunch.Plugin, OmsiLaunch.Builds.Omsi23004Assemblies 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ètreValeur valideInvalide / par défaut
platformnew 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.
PluginBuildDirectoryRé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.
NativeBridgePathChemin de OmsiLaunch.Native.x86.dll (dans le paquet : <package>\plugins\OmsiLaunch.Native.x86.dll).Comme ci-dessus.
ReleaseManifestPathrelease-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.

Règles de propriété de la session#

RègleDétail
Un seul propriétaire par installationStartSessionAsync 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 processusSessionHandle 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 sessionsUn 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ésStartSessionAsync 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.

IOmsiLaunch#

public interface IOmsiLaunch
{
    Task<SessionPlan> PlanSessionAsync(LaunchSpec spec, CancellationToken cancellationToken = default);
    Task<SessionHandle> StartSessionAsync(SessionPlan plan, CancellationToken cancellationToken = default);
    Task<SessionStatus> GetStatusAsync(SessionHandle session, CancellationToken cancellationToken = default);
    Task<SessionStatus> WaitForAsync(SessionHandle session, SessionState state, TimeSpan timeout, CancellationToken cancellationToken = default);
    Task StopAsync(SessionHandle session, CancellationToken cancellationToken = default);
    Task CloseAsync(SessionHandle session, CancellationToken cancellationToken = default);
    Task<RuntimeCommandResult> ExecuteRuntimeAsync(SessionHandle session, RuntimeCommand command, TimeSpan timeout, CancellationToken cancellationToken = default);
    Task<IReadOnlyList<Capability>> GetCapabilitiesAsync(InstallationSpec installation, CancellationToken cancellationToken = default);
    Task<IReadOnlyList<ContentIdentity>> DiscoverAsync(InstallationSpec installation, ContentQueryKind query, OptionalValue<string> scope = default, CancellationToken cancellationToken = default);
    Task<RecoveryStatus> RecoverPendingAsync(InstallationSpec installation, bool restore, CancellationToken cancellationToken = default);
}

Points communs à toutes les méthodes :

  • 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é.

Exemple minimal complet#

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
}

PlanSessionAsync#

AspectDétail
SignatureTask<SessionPlan> PlanSessionAsync(LaunchSpec spec, CancellationToken cancellationToken = default)
ObjectifCompiler 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ètresspec : 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.
RetourSessionPlan 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ésultatChaque 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éesOperationCanceledException 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.
AnnulationVérifiée une fois à l'entrée. La planification est ensuite un travail synchrone sur le système de fichiers.
Session Running requiseNon.
Modifie l'état d'OMSINon.
Modifie le système de fichiersNon (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 / restaurationAucune.
LimitationsDemander 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.
Exemplevar 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)));

StartSessionAsync#

AspectDétail
SignatureTask<SessionHandle> StartSessionAsync(SessionPlan plan, CancellationToken cancellationToken = default)
ObjectifDé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ètresplan : 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.
RetourSessionHandle(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éesInvalidOperationException("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ésultatTout é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).
AnnulationAvant/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 requiseNon.
Modifie l'état d'OMSIOui : 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 fichiersOui, 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 / restaurationOuvre la transaction (Prepared → Applied → RuntimeDeployed → HandoffCreated → ProcessStarted). Chaque chemin de sortie de la session aboutit à une restauration. Voir transactions et récupération.
LimitationsSeuls 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).
Exemplevar 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);

GetStatusAsync#

AspectDétail
SignatureTask<SessionStatus> GetStatusAsync(SessionHandle session, CancellationToken cancellationToken = default)
ObjectifLire 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ètressession : un handle renvoyé par StartSessionAsync et pas encore fermé.
RetourSessionStatus(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éesKeyNotFoundException pour les handles inconnus/fermés. Ne lève jamais d'exception par ailleurs.
AnnulationLe jeton est ignoré (l'appel se termine de façon synchrone).
Session Running requiseNon.
Modifie OMSI / système de fichiers / transactionNon / Non / Aucune.
StabilitéSTABLE_BETA.
Exemplevar status = await launch.GetStatusAsync(session); Console.WriteLine($"{status.State} events={status.RuntimeEvents!.Count}");

WaitForAsync#

AspectDétail
SignatureTask<SessionStatus> WaitForAsync(SessionHandle session, SessionState state, TimeSpan timeout, CancellationToken cancellationToken = default)
ObjectifInterroger (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ètresstate : 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.
RetourL'é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éesKeyNotFoundException ; OperationCanceledException lorsque le jeton de l'appelant est annulé (seule l'annulation par l'appelant se propage ; le timeout interne, non).
AnnulationJeton de l'appelant respecté à chaque intervalle de 100 ms.
Session Running requiseNon.
Modifie OMSI / système de fichiers / transactionNon / Non / Aucune.
StabilitéSTABLE_BETA.
Exemplevar running = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(185)); if (running.State != SessionState.Running) { /* timed out or Failed */ }

StopAsync#

AspectDétail
SignatureTask StopAsync(SessionHandle session, CancellationToken cancellationToken = default)
ObjectifDemander 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ètressession.
RetourTâche terminée ; n'attend ni l'arrêt ni la restauration. Utilisez WaitForAsync(session, SessionState.Completed, ...) pour observer la fin.
Exceptions levéesKeyNotFoundException.
AnnulationJeton ignoré.
Session Running requiseNon. 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'OMSIOui : met fin au processus OMSI (code de sortie 1).
Modifie le système de fichiersIndirectement : déclenche la restauration, la suppression du journal et la suppression des sauvegardes par le superviseur.
Transaction / restaurationDé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.
Exempleawait launch.StopAsync(session); var done = await launch.WaitForAsync(session, SessionState.Completed, TimeSpan.FromMinutes(1));

CloseAsync#

AspectDétail
SignatureTask CloseAsync(SessionHandle session, CancellationToken cancellationToken = default)
ObjectifLibé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ètressession.
RetourSe 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éesKeyNotFoundException ; 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.
AnnulationS'applique uniquement à l'attente ; n'annule jamais la restauration.
Session Running requiseNon.
Modifie l'état d'OMSIOui lorsque la session est encore active (comme StopAsync).
Modifie le système de fichiersIndirectement (restauration par le superviseur).
Transaction / restaurationGarantit 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.
StabilitéSTABLE_BETA.
Exempletry { ... } finally { await launch.CloseAsync(session); }

ExecuteRuntimeAsync#

AspectDétail
SignatureTask<RuntimeCommandResult> ExecuteRuntimeAsync(SessionHandle session, RuntimeCommand command, TimeSpan timeout, CancellationToken cancellationToken = default)
ObjectifExé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ètrescommand.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érifications1. 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.
RetourRuntimeCommandResult(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ésultatOL_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éesKeyNotFoundException ; 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.
AnnulationRespecté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 requiseOui (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'OMSIDé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 fichiersAucune écriture de l'hôte. OMSI peut écrire ses propres fichiers en conséquence (non suivis).
Transaction / restaurationAucune.
LimitationsUne 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.
Exemplevar 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"]}");

GetCapabilitiesAsync#

AspectDétail
SignatureTask<IReadOnlyList<Capability>> GetCapabilitiesAsync(InstallationSpec installation, CancellationToken cancellationToken = default)
ObjectifRenvoyer 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ètresinstallation.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é.
Retour51 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 PublicCapabilityRegistryPublicCapabilityRegistry.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éesOperationCanceledException à l'entrée ; ArgumentException pour un chemin racine vide.
AnnulationVérifiée une fois à l'entrée.
Session Running requiseNon.
Modifie OMSI / système de fichiers / transactionNon / Non / Aucune.
StabilitéContrat d'appel STABLE_BETA ; le contenu de la liste est un inventaire maintenu à la main : PARTIAL.
Exempleforeach (var c in await launch.GetCapabilitiesAsync(new InstallationSpec(root))) Console.WriteLine($"{c.Name} {c.Available} {c.EvidenceState} {c.Reason}");

DiscoverAsync#

AspectDétail
SignatureTask<IReadOnlyList<ContentIdentity>> DiscoverAsync(InstallationSpec installation, ContentQueryKind query, OptionalValue<string> scope = default, CancellationToken cancellationToken = default)
ObjectifÉ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ètresquery et scope selon le tableau ci-dessous. scope est requis pour Entrypoints (identité de carte), Repaints, FleetNumbers, Registrations (identité de véhicule).
RetourListe 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éesOperationCanceledException à 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.
AnnulationVérifiée une fois à l'entrée.
Session Running requiseNon.
Modifie OMSI / système de fichiers / transactionNon / 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).
Exemplevar 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 :

ValeurPortéeIdentityKindDisplayName
Mapsaucunemaps\<dir>\global.cfgmapnom du répertoire de la carte
Situationsaucunesituations\...\<file>.osnsituationidentité de carte référencée par le .osn (peut être null)
VehiclesaucuneVehicles\...\<file>.busvehicle[friendlyname] ou nom de fichier
Repaintsidentité de véhicule (requise ; sans elle : liste vide)<cti path>#item:<ordinal>repaintnom [item]
HofsaucuneVehicles\...\<file>.hofhofnull
FleetNumbersidentité de véhicule (requise ; sans elle : liste vide)chemin source [number] relatif au véhiculefleet-numbernull
Registrationsidentité de véhicule (requise ; sans elle : liste vide)registration_automatic / registration_list / registration_freeregistrationpremière ligne de valeur (null pour free)
AddonsaucuneAddons\<dir>addondirectory-only
Entrypointsidentité de carte (requise ; sans elle : ArgumentException)<map identity>#entrypoint:<SHA-256 of the 12-line record>entrypointlibellé 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).

RecoverPendingAsync#

AspectDétail
SignatureTask<RecoveryStatus> RecoverPendingAsync(InstallationSpec installation, bool restore, CancellationToken cancellationToken = default)
ObjectifSignaler 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ètresinstallation.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.
RetourRecoveryStatus(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éesInvalidOperationException("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.
AnnulationTransmise aux écritures du journal/des sauvegardes ; une annulation en cours de restauration laisse le journal en attente.
Session Running requiseNon (il refuse tant qu'un propriétaire est actif).
Modifie l'état d'OMSINon.
Modifie le système de fichiersUniquement 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 / restaurationAchè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.
Exemplevar r = await launch.RecoverPendingAsync(new InstallationSpec(root), restore: true); Console.WriteLine($"pending={r.Pending} recovered={r.Recovered}");

Types de contrat#

Valeurs optionnelles et primitives sémantiques#

TypeDéfinitionRemarques
OptionalValue<T>readonly record struct OptionalValue<T>(Presence Presence, T? Value) ; IsSet, Unset statique, Set(T) statiqueDistingue « non demandé » de « demandé avec une valeur ». La forme JSON est documentée dans la référence LaunchSpec.
PresenceUnset = 0, Set = 1Enum de type byte.
SemanticDate(int Year, int Month, int Day)Validé uniquement lorsque DateTimeMode.Explicit (mois 1..12, jour 1..31).
SemanticTime(int Hour, int Minute, int Second)Validé uniquement lorsque DateTimeMode.Explicit (0..23, 0..59, 0..59).

Famille LaunchSpec#

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.

TypeObjectifStabilité
LaunchSpecRecord 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
InstallationSpecRootPath, ExpectedExecutableSha256 (transporté, non consommé).STABLE_BETA / PARTIAL
WorldSpec, WorldMode, EntrypointSpec, EntrypointModeSé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).NewMap, SavedSituation : STABLE_BETA ; LastMapState : UNAVAILABLE ; EntrypointMode.Identity : PARTIAL
DateSpec, TimeSpec, YearSpec, DateTimeModeDateTimeMode : Unset, Explicit, System. Tout mode autre que Unset rend le plan non exécutable.PARTIAL (STATICALLY_PARTIAL)
WeatherSpec, WeatherModeWeatherMode : Unset, Preset, Icao, RealCurrent. Tout mode autre que Unset rend le plan non exécutable.PARTIAL
PlayerVehicleSpecModel, Repaint, Hof, FleetNumber, Registration, Enabled. Tout champ défini rend le plan non exécutable.PARTIAL
EnvironmentSpecHuit groupes IReadOnlyDictionary<string, OptionalValue<string>> de paramètres sémantiques de options.cfg.STABLE_BETA
InputSpecKeyboardDocument, ControllerDocument ; toute valeur définie rend le plan non exécutable.PARTIAL
DiagnosticsSpecSix booléens ; transportés, non consommés.PARTIAL
SessionPresentationSpec, SplashModeSplashMode : Unset = 0, Native = 0 (alias), Managed = 1.STABLE_BETA
InternetTexturesSpec, InternetTexturesModeInternetTexturesMode : Native, Disabled, Override.STABLE_BETA (Native), EXPERIMENTAL (Disabled, Override)
SessionProfileMetadataProvenance d'un profil de session compilé (Id, Name, Version, Author, PresetId, PresetIndex, PresetName, PackagePath).STABLE_BETA
LaunchBehaviorSpecRestoreConfiguration (transporté, la restauration a toujours lieu), SuppressStaleClosecheckWarning, StartupTimeoutSeconds (1..600, 180 par défaut), ShutdownTimeoutSeconds (transporté, non consommé).STABLE_BETA / PARTIAL

Types de plan et d'état#

TypeChampsRemarques
SessionPlanSessionId (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.
RuntimePlatformInfoOsFamily, 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).
CapabilityName, 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).
PlannedMutationRelativePath, SemanticKey, RequestedValue, Operation (token-patch, vector-component-patch, exact-file-overlay).Les mutations de présentation utilisent les clés session-presentation.splash, internet-textures.override, internet-textures.cache, internet-textures.target.
LaunchDiagnosticCode, 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.
SessionStatusSessionId, State (SessionState), Diagnostics, RuntimeEvents.Les diagnostics de session n'incluent pas les diagnostics du plan.
RuntimeEventType, 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.
SessionHandleSessionId.Local au processus.
RecoveryStatusPending, Recovered, Diagnostics.Voir RecoverPendingAsync.
ContentIdentityIdentity, Kind, DisplayName.Voir DiscoverAsync.
ContentQueryKindMaps, Situations, Vehicles, Repaints, Hofs, FleetNumbers, Registrations, Addons, Entrypoints.

SessionState#

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.

Types de contrôle runtime#

TypeDéfinitionStabilité
RuntimeCommand(Guid SessionId, ulong RequestId, string Operation, IReadOnlyDictionary<string, string>? Arguments)STABLE_BETA
RuntimeCommandResult(Guid SessionId, ulong RequestId, bool Succeeded, string? ErrorCode, IReadOnlyDictionary<string, string>? Values)STABLE_BETA
RuntimeCommandWireCodec 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
StartupHandoffWireCodec : 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.

Types du registre des capacités#

TypeObjectif
PublicCapabilityRegistryProtocolVersion ("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.
PublicCapabilityDescriptorId, Family, Classification, Kind, RequiresSession, RequiresExactProfile, ApiRoute, CliRoute, RuntimeValidation, Description, HandleTypes.
PublicCapabilityClassificationPublicStableBeta, PublicExperimental, InternalOnly, Unsupported.
PublicCapabilityKindRead, Write, Action, Event.
PublicRuntimeArgumentDescriptorName, Required, Description.
PublicRuntimeArgumentValidationAccepted, ErrorCode, Message.

Catalogue complet : capacités.

Méthodes d'extension D3DRuntimeApi#

Wrappers typés au-dessus de ExecuteRuntimeAsync pour les opérations d3d.* (EXPERIMENTAL, capacité PublicExperimental d3d.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).

MéthodeOpérationArguments et limites
GetD3DStatusAsync(IOmsiLaunch, SessionHandle, TimeSpan timeout, CancellationToken) → D3DDeviceStatusd3d.statusaucun
CreateD3DTextureAsync(..., uint width, uint height, D3DTextureFormat format, uint levels = 1, TimeSpan? timeout, ...) → D3DTextureDescriptiond3d.texture.createlargeur/hauteur 1..4096, niveaux 0..16
DescribeD3DTextureAsync(..., D3DTextureHandle handle, uint level = 0, ...)d3d.texture.describeniveau 0..15
UpdateD3DTextureAsync(..., D3DTextureHandle handle, D3DTextureUpdate update, ...)d3d.texture.updateD3DTextureUpdate(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)
ReleaseD3DTextureAsync(..., D3DTextureHandle handle, ...)d3d.texture.releaseune libération répétée est rejetée avec OL_E_D3D_RESOURCE_RELEASED

Types : D3DDeviceStatus(Available, State, Generation, LiveTextureCount, ResetHookInstalled, ExecutionThreadId, LastResetThreadId, QueryInterfaceHResult, CooperativeLevelHResult, OwnedDeviceReferences) ; D3DDeviceState : NotReady, Ready, Lost, Resetting, Stopping, Stopped ; D3DTextureHandle(Value) avec Value = "d3dtex-<session id N>-<16 hex>" ; D3DTextureDescription(Handle, State, DeviceState, Generation, Width, Height, Format, Levels, Level, LevelWidth, LevelHeight, HResult, ExecutionThreadId) ; D3DTextureResourceState : Live, Released, Stale ; D3DTextureFormat : A8R8G8B8, X8R8G8B8, R5G6B5, X1R5G5B5, A1R5G5B5, A4R4G4B4, A8, L8, A8L8.

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);
}

Types de contrat du processus#

TypeContenu
PublicExitCodeSuccess = 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.
PublicErrorCategoryConstantes 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.
PublicErrorCodesUn 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).
OmsiRuntimeExceptionProprié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.

MembreComportement
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.
IReadOnlyList<string> Segments(string relativePath)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"

Profils de session (OmsiLaunch.Core)#

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.

MembreComportement
SessionProfileCompiler.Load(string installationRoot, string id, int presetIndex) → SessionProfilePackageLit 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.
SessionProfileCompiler.Apply(SessionProfilePackage profile, LaunchSpec baseline, WorldMode selectedWorldMode) → LaunchSpecRenvoie 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).
SessionProfileCompiler.ValidateCompatibility(SessionProfilePackage, string installationRoot, WorldSpec world, WorldMode mode)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.
SessionProfileCompiler.Schema, MaxBytes, SchemaKeys"omsilaunch.session-profile/v1", 262144, et les clés acceptées pour chaque mapping YAML.
SessionProfilePackage(RootPath, Metadata, CompatibleMaps, New, Preset), ProfileNew, ProfilePresetLe paquet chargé ; Preset est uniquement le préréglage sélectionné.
SessionProfileException(string code, string message)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
}

Types de plateforme (OmsiLaunch.Process)#

TypeStabilitéUtilisation
IRuntimePlatformSTABLE_BETA en tant que type du paramètre de constructeur de OmsiLaunchServiceDé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.
CurrentWindowsX64PlatformSTABLE_BETALa 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.
InstallationLease, LaunchedProcess, ProcessIdentity, ReleaseManifest, RuntimeArtifact, RuntimeArtifactSet, StartupProcessRequest, CurrentRuntimeCommandStore, IOmsiProcessController, OmsiProcessStateINTERNALPublics 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.

Sécurité des threads#

  • 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.

Ce qui ne fait pas partie de l'API#

  • 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.

Résumé de la stabilité#

SurfaceStabilité
Constructeur de OmsiLaunchService, OmsiLaunchRuntimePathsSTABLE_BETA
PlanSessionAsync, StartSessionAsync (NEW_MAP, SAVED_SITUATION), GetStatusAsync, WaitForAsync, StopAsync, CloseAsyncSTABLE_BETA
Transport de ExecuteRuntimeAsync ; opérations PublicStableBetaSTABLE_BETA
Opérations PublicExperimental, D3DRuntimeApi, camera.lockEXPERIMENTAL
Contenu de la liste de GetCapabilitiesAsync, membres de spécification date/heure/météo/véhicule du joueur/entrée, DiagnosticsSpec, ExpectedExecutableSha256, RestoreConfiguration, ShutdownTimeoutSecondsPARTIAL
RuntimeCommandWire, StartupHandoff, StartupHandoffWire, implémentations de IRuntimePlatform, toutes les assemblies d'implémentationINTERNAL
WorldMode.LastMapState / LastSituation, weather.set, opérations internal.*UNAVAILABLE