Referenz der öffentlichen API (OmsiLaunch.Api)

Dokumentation für Version v0.1.0-beta.3Quelle auf GitHub ansehen

Übersetzung der englischen Originalseite für OmsiLaunch 0.1.0-beta3. Maßgeblich ist die englische Seite: Bei Abweichungen gelten die englische Seite und der Code.

Diese Seite ist die normative Referenz für die verwaltete öffentliche API von OmsiLaunch 0.1.0-beta3: die Assembly OmsiLaunch.Api (Verträge) und der Einstiegspunkt für Integratoren OmsiLaunchService in OmsiLaunch.Core. Sie dokumentiert ausschließlich, was der aktuelle Code tut. Alles, was ein Integrator aufrufen, empfangen oder beobachten kann, ist hier mit seiner Stabilitätsstufe aufgeführt; alles, was hier nicht aufgeführt ist, ist keine Integrationsschnittstelle.

Das generierte Inventar der öffentlichen API listet jeden öffentlichen Typ und jedes öffentliche Mitglied von OmsiLaunch.Api, OmsiLaunch.Core und OmsiLaunch.Process mit Signatur und Stabilität auf; ein Dokumentations-Gate schlägt fehl, wenn Inventar und Assemblies voneinander abweichen. Diese Seite erläutert die Semantik.

Verwandte Seiten: LaunchSpec-Referenz, Fehlercodes, Sitzungslebenszyklus, Transaktionen und Recovery, Runtime-Steuerung, Capabilities, lokale Steuerungsebene, Exitcodes, Status der Runtime-Validierung.

Stabilitätsvokabular#

StufeBedeutung auf dieser Seite
STABLE_BETADer Vertrag ist für die Protokolllinie 0.1 eingefroren, und der Pfad ist in research/reports/OMSILAUNCH-RUNTIME-VALIDATION-MATRIX.md zur Laufzeit validiert.
EXPERIMENTALAufrufbar und getestet, aber der Vertrag oder der Runtime-Nachweis kann sich ändern, bevor er stabil wird.
PARTIALIm Vertrag vorhanden; nur ein Teil des Verhaltens ist implementiert oder validiert (der Text gibt an, welcher Teil).
INTERNALAus technischen Gründen in der Assembly öffentlich (die Bridge verwendet den Typ gemeinsam), aber keine Integrationsschnittstelle; kann sich ohne Ankündigung ändern.
UNAVAILABLEIm Vertrag vorhanden, wird aber vom aktuellen Build abgelehnt.

Überblick über die Assemblies#

AssemblyRolle für Integratoren
OmsiLaunch.ApiReine Verträge: Records, Enums, IOmsiLaunch, Capability-Registry, Fehlerkatalog, Wire-Formate, D3D-Hilfsfunktionen. Enthält kein IntPtr, kein nint, kein Win32-Handle, keine native Adresse und kein Prozessobjekt.
OmsiLaunch.CoreOmsiLaunchService (die Implementierung von IOmsiLaunch), OmsiLaunchRuntimePaths, SessionPlanner, LaunchValidation, SessionProfileCompiler.
OmsiLaunch.ProcessIRuntimePlatform und CurrentWindowsX64Platform (der einzige Plattformadapter), InstallationLease. Wird zum Erzeugen des Service benötigt.
OmsiLaunch.Configuration, OmsiLaunch.Content, OmsiLaunch.Interop, OmsiLaunch.Plugin, OmsiLaunch.Builds.Omsi23004Implementierungs-Assemblies. Ihre öffentlichen Typen sind für Integratoren INTERNAL.

Einstiegspunkt: OmsiLaunchService und OmsiLaunchRuntimePaths#

public sealed record OmsiLaunchRuntimePaths(string PluginBuildDirectory, string NativeBridgePath, string? ReleaseManifestPath = null);
public sealed class OmsiLaunchService : IOmsiLaunch
{
    public OmsiLaunchService(IRuntimePlatform platform, OmsiLaunchRuntimePaths runtimePaths);
}
ParameterGültiger WertUngültig / Standard
platformnew CurrentWindowsX64Platform() (Namespace OmsiLaunch.Process). Erkennt die Plattform, erzeugt den OMSI-Prozess mit CreateProcessW, wartet auf ihn und beendet ihn.Es wird keine andere Implementierung ausgeliefert. Eine eigene IRuntimePlatform ist INTERNAL.
PluginBuildDirectoryVerzeichnis, das die Referenzdateien der permanenten Plugin-Gesamtheit (Closure) enthält: OmsiLaunch.Plugin.opl, OmsiLaunch.PluginNE.dll, OmsiLaunch.Plugin.deps.json, OmsiLaunch.Plugin.runtimeconfig.json und jede OmsiLaunch.*.dll der verwalteten Closure (muss OmsiLaunch.Plugin.dll enthalten). In einem installierten Paket ist dies <package>\plugins.Fehlendes Verzeichnis oder fehlende Datei: PlanSessionAsync liefert einen nicht ausführbaren Plan mit OL_E_RUNTIME_ARTIFACT_MISSING.
NativeBridgePathPfad von OmsiLaunch.Native.x86.dll (im Paket: <package>\plugins\OmsiLaunch.Native.x86.dll).Wie oben.
ReleaseManifestPathrelease-manifest.json neben OmsiLaunch.exe, sofern vorhanden. Liefert den erwarteten SHA-256 jeder Datei in plugins/ (plugin.integrity.reference = manifest).null (Entwicklungslayout): Die installierten Dateien werden nur auf Vorhandensein und Selbstkonsistenz gegenüber der Referenz-Closure geprüft (plugin.integrity.reference = self). Fehlerhaftes Manifest: OL_E_RELEASE_MANIFEST_INVALID.

Der Service liest diese Pfade bei jedem PlanSessionAsync und StartSessionAsync; er kopiert, stellt bereit oder entfernt niemals Plugin-Dateien (siehe permanentes Plugin). Die CLI erzeugt den Service genau so (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));

Erzeugen Sie einen Service pro Prozess und verwenden Sie ihn gemeinsam. Stabilität: STABLE_BETA.

Regeln zur Sitzungseigentümerschaft#

RegelDetail
Ein Eigentümer pro InstallationStartSessionAsync erwirbt die Installations-Lease, einen benannten Semaphor Local\OmsiLaunch.Installation.<SHA-256 of the upper-cased full root path>, und hält sie, bis der Supervisor die Installation wiederhergestellt hat. Ein zweiter Start auf demselben Stammverzeichnis aus einem beliebigen Prozess derselben Anmeldesitzung schlägt mit OL_E_INSTALLATION_BUSY fehl (gemeldet als Failed-Sitzung, siehe StartSessionAsync). Die Lease gilt pro Anmeldesitzung, nicht anmeldeübergreifend, und wird nicht freigegeben, solange ein anderer Prozess ein Handle darauf hält (akzeptiertes Risiko).
Handles sind prozesslokalSessionHandle kapselt die Guid der Sitzung. Es ist nur für die OmsiLaunchService-Instanz aussagekräftig, die es zurückgegeben hat. Ein Handle, das in einem anderen Prozess (oder einer anderen Service-Instanz) aus einer bekannten Guid gebildet wird, führt zu KeyNotFoundException. Prozessübergreifende Steuerung läuft über die lokale Steuerungsebene, nicht über Handles.
Rufen Sie immer CloseAsync aufAb StartSessionAsync besitzt der Prozess eine dauerhafte Transaktion. CloseAsync fordert bei Bedarf den kanonischen Stopp an, wartet auf den Supervisor (Prozessende, exakte Wiederherstellung, Freigabe der Lease) und vergisst die Sitzung. Es muss auf jedem Ausstiegspfad aufgerufen werden, auch nach einem Failed-Zustand. Ohne diesen Aufruf bleibt der Sitzungseintrag im Speicher; die Wiederherstellung selbst führt der Supervisor unabhängig davon durch.
Fehlgeschlagene Sitzungen sind trotzdem SitzungenEin Start, der fehlschlägt, nachdem StartSessionAsync zurückgekehrt ist, meldet SessionState.Failed; das Handle bleibt für GetStatusAsync/WaitForAsync gültig, bis CloseAsync aufgerufen wird.
Pläne werden erneut geprüftStartSessionAsync berechnet den Hash von Omsi.exe neu und plant die Spezifikation erneut; ein Plan, der nicht mehr ausführbar ist, wird mit OL_E_PLAN_NOT_RUNNABLE abgelehnt.

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

Gemeinsame Fakten für jede Methode:

  • Unbekannte oder bereits geschlossene Handles lösen KeyNotFoundException aus („Unknown OmsiLaunch session.“).
  • Keine Methode außer ExecuteRuntimeAsync setzt eine laufende Sitzung (Running) voraus.
  • Ausnahmen, die einen OmsiLaunch-Code tragen, stellen den Code an den Anfang von Exception.Message ("OL_E_PLAN_NOT_RUNNABLE: ..."). Die CLI extrahiert Codes auf dieselbe Weise aus Meldungen (CliProgram.Classify).
  • Unterstützter Build: nur Omsi23004_692EBFBF (zusätzlich der Hash der Steam-LAA-Positivliste, akzeptiert; Gameplay nicht validiert, dafür ist eine echte Steam-Installation erforderlich). Siehe Kompatibilität.

Vollständiges Minimalbeispiel#

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#

AspektDetail
SignaturTask<SessionPlan> PlanSessionAsync(LaunchSpec spec, CancellationToken cancellationToken = default)
ZweckKompiliert eine LaunchSpec zu einem SessionPlan, ohne OMSI zu starten: validiert die Spezifikation, erkennt die Plattform, erstellt den Fingerabdruck von Omsi.exe, löst Inhaltsidentitäten auf, berechnet geplante Dateiänderungen, listet benötigte und nicht unterstützte Capabilities auf und entscheidet über IsRunnable. Öffentliche Capability session.plan.
Parameterspec: eine vollständig befüllte LaunchSpec (siehe LaunchSpec-Referenz). Installation, World, Date, Time, Environment (alle acht Dictionaries) und Behavior dürfen nicht null sein; die optionalen Mitglieder dürfen null sein. RootPath sollte ein absolutes Verzeichnis sein; ein leeres Stammverzeichnis wird als OL_E_INSTALLATION_NOT_FOUND erfasst, die Plattformprüfung auf einem leeren Pfad löst jedoch ArgumentException aus, bevor der Plan zurückgegeben wird – übergeben Sie daher niemals ein leeres Stammverzeichnis.
RückgabeSessionPlan mit einer neuen SessionId, BuildProfileId = "Omsi23004_692EBFBF" (immer diese Konstante, auch wenn die ausführbare Datei nicht übereinstimmt), der Eingabe-Spec, Platform, ResolvedContent, TouchedFiles, RuntimeArtifacts (Zielpfade plugins\OmsiLaunch.* sowie "OmsiLaunch startup handoff v4"), RequiredCapabilities, UnsupportedRequestedFeatures, PlannedMutations, Diagnostics, IsRunnable. IsRunnable ist genau dann true, wenn kein Diagnosecode mit OL_E_ beginnt. Informative Diagnosen (plugin.integrity.reference mit der Meldung self oder manifest, session_profile.selected) machen einen Plan niemals nicht ausführbar.
Im Ergebnis übermittelte FehlerJeder Planungsfehler ist eine Diagnose, keine Ausnahme: 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 (die Meldung trägt den Startbild-/ITX-Code), OL_E_PERMANENT_PLUGIN_MISSING, OL_E_PERMANENT_PLUGIN_HASH_MISMATCH, OL_E_PERMANENT_PLUGIN_MANIFEST_INCOMPLETE (die installierte Plugin-Closure in plugins\ wird zum Planungszeitpunkt gegen das Release-Manifest geprüft), OL_E_RUNTIME_ARTIFACT_MISSING (die Meldung kann OL_E_RELEASE_MANIFEST_INVALID enthalten). Vollständige Bedingungen: LaunchSpec-Validierungsregeln.
AusgelöstOperationCanceledException, wenn das Token beim Eintritt bereits abgebrochen ist (der einzige Prüfpunkt); ArgumentException/NotSupportedException bei syntaktisch ungültigen Stammpfaden; NullReferenceException/ArgumentNullException bei null in erforderlichen Mitgliedern; System.Text.Json.JsonException bei einem syntaktisch ungültigen Release-Manifest.
AbbruchWird einmal beim Eintritt geprüft. Danach ist die Planung synchrone Dateisystemarbeit.
Laufende Sitzung erforderlichNein.
Verändert den OMSI-ZustandNein.
Verändert das DateisystemNein (liest Omsi.exe, Inhaltsdateien, Plugin-Closure, Manifest). Einstellungswerte werden hier nicht validiert (nur Existenz und Beschreibbarkeit des Schlüssels); ein ungültiger Wert schlägt beim Start mit OL_E_INVALID_SETTING_VALUE fehl.
Transaktion / WiederherstellungKeine.
EinschränkungenDie Anforderung eines anderen Date/Time/Year-Modus als Unset, eines anderen Weather-Modus als Unset, eines beliebigen PlayerVehicle-Felds, von Input-Dokumenten, von EntrypointIdentity oder von WorldMode.LastMapState erzeugt auf diesem Build OL_E_CAPABILITY_UNAVAILABLE und einen nicht ausführbaren Plan (Einträge STATICALLY_PARTIAL / UNSUPPORTED_FOR_CURRENT_PROFILE in UnsupportedRequestedFeatures).
StabilitätSTABLE_BETA.
Beispielvar 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#

AspektDetail
SignaturTask<SessionHandle> StartSessionAsync(SessionPlan plan, CancellationToken cancellationToken = default)
ZweckStartet aus einem ausführbaren Plan eine transaktionale, verwaltete OMSI-Sitzung: erwirbt die Installations-Lease, führt die Recovery eines veralteten Journals durch, validiert die permanente Plugin-Closure, erstellt Snapshots und Overlays der Sitzungsdateien, erzeugt die Start-Übergabe (Handoff), den Telemetrie-Slot und die Runtime-Mailbox, startet Omsi.exe, erfasst den Prozess im Journal und übergibt die Sitzung an einen Supervisor im Hintergrund. Öffentliche Capability session.start.
Parameterplan: ein SessionPlan mit IsRunnable == true. Die Spezifikation im Plan wird erneut geplant; aus dem Plan des Aufrufers wird nur plan.SessionId übernommen. plan.Spec.Behavior.StartupTimeoutSeconds muss im Bereich 1..600 liegen.
RückgabeSessionHandle(plan.SessionId), sobald Omsi.exe erzeugt und erfasst wurde (Zustand WaitingForPlugin) oder sobald der Startpfad fehlgeschlagen ist (Zustand Failed). Es wird nicht auf das Gameplay gewartet; verwenden Sie WaitForAsync(session, SessionState.Running, ...).
AusgelöstInvalidOperationException("OL_E_PLAN_NOT_RUNNABLE"), wenn plan.IsRunnable false ist; InvalidOperationException("OL_E_PLAN_NOT_RUNNABLE: <codes>"), wenn die erneute Planung nicht ausführbar ist (z. B. Omsi.exe geändert, Inhalt entfernt, Plugin-Closure fehlt); InvalidOperationException("Duplicate session id."), wenn eine Sitzung mit derselben ID noch registriert ist (rufen Sie zuerst CloseAsync auf); ArgumentOutOfRangeException, wenn StartupTimeoutSeconds außerhalb von 1..600 liegt; OperationCanceledException bei Abbruch vor oder während der erneuten Planung; zusätzlich alles, was PlanSessionAsync auslöst. In allen Fällen mit ausgelöster Ausnahme wird keine Sitzung registriert.
Im Ergebnis übermittelte FehlerJeder Fehler nach der erneuten Planung wird innerhalb des Startpfads abgefangen: Die Sitzung wird registriert, ihr Zustand ist Failed, und ihre Diagnosen enthalten OL_E_START_SESSION, dessen Meldung die innere Meldung ist (beginnend mit dem inneren Code, sofern einer vorhanden ist): OL_E_INSTALLATION_BUSY (Lease gehalten oder ein im Journal erfasster OMSI-Prozess noch aktiv), 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 (nur wenn der zurückgestellte erneute Versuch mit den Overlays dieser Sitzung die Eigentümerschaft weiterhin nicht nachweisen kann), OL_E_RECOVERY_JOURNAL_REMOVE_FAILED, OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED. Die Bereinigung kann OL_E_PROCESS_CLEANUP_FAILED, OL_E_RESTORE_DEFERRED (OMSI-Ende nicht bestätigt; Journal bleibt erhalten) oder OL_E_RESTORE_FAILED hinzufügen. Spätere Fehler meldet der Supervisor (siehe Sitzungslebenszyklus).
AbbruchVor/während der erneuten Planung: löst eine Ausnahme aus. Danach wird das Token an die Transaktion und die Prozesserzeugung weitergegeben; ein Abbruch dort wird wie jeder andere Startfehler behandelt (Failed + OL_E_START_SESSION: The operation was canceled.), der Prozess (falls erzeugt) wird beendet und die Installation wiederhergestellt.
Laufende Sitzung erforderlichNein.
Verändert den OMSI-ZustandJa: erzeugt den OMSI-Prozess mit den Umgebungsvariablen OMSILAUNCH_SESSION_ID, OMSILAUNCH_HANDOFF_NAME, OMSILAUNCH_TELEMETRY_NAME, OMSILAUNCH_RUNTIME_CHANNEL, OMSILAUNCH_INTERNET_TEXTURES_MODE.
Verändert das DateisystemJa, innerhalb des Installationsstammverzeichnisses: .omsilaunch\diagnostics\<sessionId>-host.log (Aufbewahrung: die 50 neuesten Sitzungen), .omsilaunch\journal.json, .omsilaunch\backup\<sessionId>\*.bin, .omsilaunch\assets\splash\*.bmp (einmalig für das verwaltete Startbild kopiert), Sitzungs-Overlays (options.cfg-Patches, GUI\NewSplashscreen_*.bmp, Texture\standard.itx), Sitzungslöschungen (ITX-Ziele, Texture\standard.ipr, closecheck) sowie das dauerhafte Entfernen einer bereits vorhandenen veralteten closecheck-Datei, wenn SuppressStaleClosecheckWarning true ist (Diagnose closecheck.stale-removed).
Transaktion / WiederherstellungÖffnet die Transaktion (Prepared → Applied → RuntimeDeployed → HandoffCreated → ProcessStarted). Jeder Pfad aus der Sitzung heraus endet in der Wiederherstellung. Siehe Transaktionen und Recovery.
EinschränkungenNur WorldMode.NewMap mit PresentedEntrypointIndex und WorldMode.SavedSituation erreichen das Gameplay. WorldMode.LastMapState ist UNAVAILABLE. Datums-/Zeit-/Wetter-/Spielerfahrzeug-/Eingabeanforderungen erreichen diese Methode nie, weil sie bereits zum Planungszeitpunkt nicht ausführbar sind.
StabilitätSTABLE_BETA (die Lebenszyklen NEW_MAP und SAVED_SITUATION sind zur Laufzeit validiert).
Beispielvar 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#

AspektDetail
SignaturTask<SessionStatus> GetStatusAsync(SessionHandle session, CancellationToken cancellationToken = default)
ZweckLiest den semantischen Lebenszykluszustand, die bisher gesammelten Diagnosen und die begrenzte Liste der Runtime-Ereignisse. Öffentliche Capability session.status.
Parametersession: ein von StartSessionAsync zurückgegebenes, noch nicht geschlossenes Handle.
RückgabeSessionStatus(SessionId, State, Diagnostics, RuntimeEvents): ein unveränderlicher Snapshot (Arrays werden unter der Sitzungssperre kopiert). RuntimeEvents ist für eine aktive Sitzung nie null.
AusgelöstKeyNotFoundException bei unbekannten/geschlossenen Handles. Löst sonst nie eine Ausnahme aus.
AbbruchDas Token wird ignoriert (der Aufruf wird synchron abgeschlossen).
Laufende Sitzung erforderlichNein.
Verändert OMSI / Dateisystem / TransaktionNein / Nein / Keine.
StabilitätSTABLE_BETA.
Beispielvar status = await launch.GetStatusAsync(session); Console.WriteLine($"{status.State} events={status.RuntimeEvents!.Count}");

WaitForAsync#

AspektDetail
SignaturTask<SessionStatus> WaitForAsync(SessionHandle session, SessionState state, TimeSpan timeout, CancellationToken cancellationToken = default)
ZweckFragt (alle 100 ms) ab, bis sich die Sitzung in state oder in einem Endzustand (Completed, Failed) befindet oder das Timeout abläuft; gibt dann den aktuellen Status zurück.
Parameterstate: ein beliebiger SessionState. Das Warten auf einen Übergangszustand, der bereits durchlaufen wurde (oder nie gesetzt wird, siehe Sitzungslebenszyklus), wartet bis zu einem Endzustand oder bis zum Timeout. timeout: ein beliebiger nicht negativer TimeSpan oder Timeout.InfiniteTimeSpan.
RückgabeDer Status zum Zeitpunkt, an dem das Warten endete. Bei einem Timeout wird der Status zurückgegeben, keine Ausnahme: Prüfen Sie State selbst. Ein Warten auf Running, das in Failed endet, kehrt sofort mit den Fehlerdiagnosen zurück.
AusgelöstKeyNotFoundException; OperationCanceledException, wenn das Token des Aufrufers abgebrochen wird (nur der Abbruch durch den Aufrufer wird weitergegeben, das interne Timeout nicht).
AbbruchDas Token des Aufrufers wird bei jedem 100-ms-Takt berücksichtigt.
Laufende Sitzung erforderlichNein.
Verändert OMSI / Dateisystem / TransaktionNein / Nein / Keine.
StabilitätSTABLE_BETA.
Beispielvar running = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(185)); if (running.State != SessionState.Running) { /* timed out or Failed */ }

StopAsync#

AspektDetail
SignaturTask StopAsync(SessionHandle session, CancellationToken cancellationToken = default)
ZweckFordert den kanonischen Stopp an. Die Methode setzt das Stopp-Flag und kehrt sofort zurück; der Supervisor bemerkt das Flag innerhalb seiner 100-ms-Schleife, ruft TerminateProcess für Omsi.exe auf, wartet auf das Prozessende, markiert das Journal mit ProcessExited, stellt jede der Sitzung gehörende Datei wieder her und gibt die Lease frei. Dies ist eine erzwungene Beendigung: Die eigene Beenden-Routine von OMSI läuft nicht, und OMSI schreibt options.cfg beim Beenden nicht neu (beabsichtigt, schützt die Transaktion). Ein kooperatives Herunterfahren über WM_CLOSE ist nicht implementiert (Produktentscheidung; im Runtime-Abschluss wurde OMSI innerhalb von 30 s nach WM_CLOSE nicht geschlossen, L05b). Öffentliche Capability session.stop.
Parametersession.
RückgabeAbgeschlossener Task; wartet weder auf die Beendigung noch auf die Wiederherstellung. Verwenden Sie WaitForAsync(session, SessionState.Completed, ...), um den Abschluss zu beobachten.
AusgelöstKeyNotFoundException.
AbbruchToken wird ignoriert.
Laufende Sitzung erforderlichNein. Idempotent; ein Stopp, der angefordert wird, bevor der Supervisor startet, wird berücksichtigt, sobald er startet; ein Stopp für eine Sitzung im Endzustand hat keine Wirkung.
Verändert den OMSI-ZustandJa: beendet den OMSI-Prozess (Exitcode 1).
Verändert das DateisystemIndirekt: löst Wiederherstellung, Löschen des Journals und Entfernen der Backups durch den Supervisor aus.
Transaktion / WiederherstellungLöst ProcessExited → Restoring → Restored aus. Runtime-seitige Änderungen, die über ExecuteRuntimeAsync vorgenommen wurden (Uhrzeitänderungen, erzeugte Fahrzeuge, Skriptvariablen, D3D-Texturen), werden nicht wiederhergestellt; sie verschwinden mit dem Prozess.
StabilitätSTABLE_BETA.
Beispielawait launch.StopAsync(session); var done = await launch.WaitForAsync(session, SessionState.Completed, TimeSpan.FromMinutes(1));

CloseAsync#

AspektDetail
SignaturTask CloseAsync(SessionHandle session, CancellationToken cancellationToken = default)
ZweckGibt das Handle des Verbrauchers frei, ohne die Transaktion zurückzulassen: Befindet sich die Sitzung nicht im Endzustand, wird der kanonische Stopp angefordert; anschließend wird auf den Lebenszyklus-Task des Supervisors gewartet (Prozessende, Wiederherstellung, Freigabe der Lease); danach wird die Sitzung vergessen.
Parametersession.
RückgabeWird abgeschlossen, wenn sich die Sitzung im Endzustand befindet und entfernt wurde. Nach der Rückkehr ist das Handle unbekannt (KeyNotFoundException bei jedem weiteren Aufruf, auch bei einem zweiten CloseAsync).
AusgelöstKeyNotFoundException; OperationCanceledException, wenn der Aufrufer während des Wartens auf den Supervisor abbricht. In diesem Fall wird die Sitzung nicht entfernt und der Supervisor läuft weiter; rufen Sie CloseAsync erneut auf.
AbbruchGilt nur für das Warten; die Wiederherstellung wird niemals abgebrochen.
Laufende Sitzung erforderlichNein.
Verändert den OMSI-ZustandJa, wenn die Sitzung noch aktiv ist (wie bei StopAsync).
Verändert das DateisystemIndirekt (Wiederherstellung durch den Supervisor).
Transaktion / WiederherstellungGarantiert, dass die Transaktion zu Ende geführt wird, bevor das Handle freigegeben wird (sofern der Supervisor gestartet wurde). Bei einer Sitzung, die fehlgeschlagen ist, bevor der Supervisor gestartet wurde, hat der Startpfad bereits wiederhergestellt oder OL_E_RESTORE_DEFERRED gemeldet.
StabilitätSTABLE_BETA.
Beispieltry { ... } finally { await launch.CloseAsync(session); }

ExecuteRuntimeAsync#

AspektDetail
SignaturTask<RuntimeCommandResult> ExecuteRuntimeAsync(SessionHandle session, RuntimeCommand command, TimeSpan timeout, CancellationToken cancellationToken = default)
ZweckFührt eine öffentliche Runtime-Operation innerhalb des laufenden OMSI-Prozesses über die Single-Flight-Mailbox der Sitzung aus (speicherabgebildet, 64 KiB, Anforderung an Sitzungs-ID und Anforderungs-ID gebunden). Das Plugin führt die Operation auf dem UI-Thread von OMSI aus. Operationskatalog: Runtime-Steuerung und Capabilities.
Parametercommand.SessionId muss gleich session.SessionId sein. command.RequestId: vom Aufrufer gewählter ulong; verwenden Sie einen streng monoton steigenden Zähler pro Prozess (die D3D-Hilfsfunktionen beginnen bei 30 000, der CLI-Eigentümer bei 10 001/50 000). command.Operation: eine öffentliche Operations-ID aus PublicCapabilityRegistry.PublicRuntimeOperationIds (z. B. time.read, road-vehicle.read, d3d.texture.create). command.Arguments: Zeichenfolgenwerte mit ordinal verglichenen Namen als Schlüssel; die pro Operation erforderlichen Namen liefert PublicCapabilityRegistry.GetRuntimeArguments. timeout: gemessen ab dem Zeitpunkt, an dem die Anforderung in der Mailbox bereitgestellt wird (Warten hinter einem anderen laufenden Befehl zählt nicht). Die CLI verwendet als Eigentümer 5 s (15 s für road-vehicles.spawn) und als Client 8 s / 30 s.
Reihenfolge der Prüfungen1. Registry-Validierung (vor der Sitzungssuche): unbekannte oder internal.*-Operation → Ergebnis Succeeded=false, ErrorCode=OL_E_RUNTIME_OPERATION_UNKNOWN; fehlendes erforderliches Argument (nicht vorhanden oder nur Leerraum) → OL_E_RUNTIME_ARGUMENT_REQUIRED. 2. Sitzungssuche → KeyNotFoundException. 3. command.SessionId != session.SessionId → InvalidOperationException("OL_E_RUNTIME_SESSION_MISMATCH"). 4. Zustand nicht Running → InvalidOperationException("OL_E_SESSION_NOT_RUNNING"). 5. Mailbox-Anforderung. 6. Ergebniswerte, deren Schlüssel mit internal_ beginnt oder mit _address, _pointer, _vmt endet, werden entfernt.
RückgabeRuntimeCommandResult(SessionId, RequestId, Succeeded, ErrorCode, Values). Bei Erfolg enthält Values die semantischen Zeichenfolgen der Operation (pro Operation in Runtime-Steuerung dokumentiert).
Im Ergebnis übermittelte FehlerOL_E_RUNTIME_OPERATION_UNKNOWN, OL_E_RUNTIME_ARGUMENT_REQUIRED (Registry); OL_E_RUNTIME_RESPONSE_TOO_LARGE (das Plugin-Ergebnis hat die Mailbox überschritten; Ergebnisse begrenzter Listen werden stattdessen mit truncated=true gekürzt); OL_E_RUNTIME_SETTING_NOT_PERSISTENT (weather.set, immer); jeder OL_E_D3D_*-Code (mit Values["detail"] und Values["native_status"]); und OL_E_RUNTIME_OPERATION_FAILED für jeden anderen Plugin-seitigen Fehler. Im letzten Fall steht der spezifische Code nicht in ErrorCode: Er ist das erste Token von Values["detail"] (z. B. detail = "OL_E_RUNTIME_OBJECT_HANDLE_STALE", exception = "InvalidOperationException"). Codes, die auf diesem Weg ankommen: OL_E_RUNTIME_OPERATION_UNAVAILABLE, OL_E_RUNTIME_ARGUMENT_REQUIRED (Plugin-seitige Prüfungen), 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. Siehe Fehlercodes.
AusgelöstKeyNotFoundException; InvalidOperationException mit OL_E_RUNTIME_SESSION_MISMATCH, OL_E_SESSION_NOT_RUNNING, OL_E_RUNTIME_CHANNEL_CLOSED (Mailbox bereits vom Supervisor freigegeben), OL_E_RUNTIME_CHANNEL_BUSY (Slot enthält noch eine aufgegebene Anforderung), OL_E_RUNTIME_REQUEST_ID_REUSED (eine veraltete Antwort für dieselbe Anforderungs-ID liegt noch im Slot); TimeoutException("OL_E_RUNTIME_REQUEST_TIMEOUT"); InvalidDataException("OL_E_RUNTIME_RESPONSE_INVALID") (beschädigte, fremde oder nicht passende Antwort); ArgumentOutOfRangeException, wenn die serialisierte Anforderung die Mailbox überschreitet; OperationCanceledException.
AbbruchWird beim Warten auf das Gate pro Sitzung und alle 20 ms beim Abfragen der Antwort berücksichtigt. Ein Abbruch während einer laufenden Anforderung setzt den Slot nicht zurück: Der nächste Aufruf für diese Sitzung kann mit OL_E_RUNTIME_CHANNEL_BUSY fehlschlagen, bis das Plugin seine Antwort veröffentlicht (die dann als veraltet verworfen wird). Verwenden Sie bevorzugt das Timeout; ein Timeout setzt den Slot zurück, und eine verspätete Antwort wird erkannt und verworfen.
Laufende Sitzung erforderlichJa (SessionState.Running); andernfalls wird OL_E_SESSION_NOT_RUNNING ausgelöst. Die Mailbox existiert, bis der Supervisor sie während der Wiederherstellung freigibt.
Verändert den OMSI-ZustandAbhängig von der Operation: Read-Operationen nicht; Write-/Action-Operationen (time.set, camera.set, camera.lock, camera.unlock, road-vehicles.spawn, road-vehicles.place-random, vehicle.variable.set, d3d.texture.*) verändern prozessinternen Zustand, der nicht wiederhergestellt wird.
Verändert das DateisystemKeine Schreibvorgänge des Hosts. OMSI kann infolgedessen eigene Dateien schreiben (nicht nachverfolgt).
Transaktion / WiederherstellungKeine.
EinschränkungenEin laufender Befehl pro Sitzung (Aufrufe für dieselbe Sitzung werden serialisiert). Anforderung und Antwort sind jeweils auf 64 KiB minus 8 Byte begrenzt; D3D-Pixelnutzdaten auf 48 KiB. internal.road-vehicles.make-basic ist INTERNAL und nicht erreichbar. weather.set ist UNAVAILABLE. timetable.logs.read ist nicht begrenzt und kann bei großen Fahrplänen OL_E_RUNTIME_RESPONSE_TOO_LARGE zurückgeben. camera.lock ist EXPERIMENTAL; es benötigt ein Spielerfahrzeug und ist zur Laufzeit validiert (CAM01), obwohl der RuntimeValidation-String der Registry weiterhin STATICALLY_VALIDATED lautet. Handles (rv-NNNNNN, hb-NNNNNN, d3dtex-<session>-<hex>) gelten nur innerhalb der Sitzung.
StabilitätTransport und Vertrag STABLE_BETA; die Stabilität pro Operation folgt PublicCapabilityRegistry (PublicStableBeta → STABLE_BETA, PublicExperimental → EXPERIMENTAL) mit den oben genannten Ausnahmen.
Beispielvar 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#

AspektDetail
SignaturTask<IReadOnlyList<Capability>> GetCapabilitiesAsync(InstallationSpec installation, CancellationToken cancellationToken = default)
ZweckGibt das Nachweisinventar des Produkts für eine Installation zurück: eine feste Liste von Capability(Name, Available, EvidenceState, Reason)-Einträgen, die in OmsiLaunchService gepflegt wird. Nur runtime.current-windows-x64 wird berechnet (aus der Plattformerkennung); jeder andere Eintrag ist konstant.
Parameterinstallation.RootPath: Verzeichnis für die Plattformprüfung (Beschreibbarkeit setzt voraus, dass das Verzeichnis existiert, nicht schreibgeschützt ist und plugins\ enthält). ExpectedExecutableSha256 wird ignoriert.
Rückgabe51 Einträge, z. B. 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).
Unterschied zu PublicCapabilityRegistryPublicCapabilityRegistry.All ist der zur Kompilierzeit festgelegte Katalog der Steuerungsschnittstelle (36 Deskriptoren mit Klassifizierung, Art, API- und CLI-Routen, erforderlichen Argumenten), den API und CLI durchsetzen; er hängt nicht von der Installation ab. GetCapabilitiesAsync ist ein Runtime-Nachweisbericht (Validierungszustand und Begründungen). Verwenden Sie die Registry, um zu entscheiden, was Sie aufrufen dürfen; verwenden Sie diese Liste, um zu entscheiden, was nachgewiesen ist. Keine der beiden Listen wird aus der anderen abgeleitet.
AusgelöstOperationCanceledException beim Eintritt; ArgumentException bei einem leeren Stammpfad.
AbbruchWird einmal beim Eintritt geprüft.
Laufende Sitzung erforderlichNein.
Verändert OMSI / Dateisystem / TransaktionNein / Nein / Keine.
StabilitätAufrufvertrag STABLE_BETA; der Listeninhalt ist ein von Hand gepflegtes Inventar: PARTIAL.
Beispielforeach (var c in await launch.GetCapabilitiesAsync(new InstallationSpec(root))) Console.WriteLine($"{c.Name} {c.Available} {c.EvidenceState} {c.Reason}");

DiscoverAsync#

AspektDetail
SignaturTask<IReadOnlyList<ContentIdentity>> DiscoverAsync(InstallationSpec installation, ContentQueryKind query, OptionalValue<string> scope = default, CancellationToken cancellationToken = default)
ZweckZählt installierte Inhalte auf und gibt kanonische Identitäten zurück, die in einer LaunchSpec verwendbar sind. Die Erkennung überspringt Analysepunkte (Reparse Points; Junction-Zyklen können sie nicht aufhängen), liest OMSI-Dateien als Windows-1252 (UTF-8/UTF-16 mit BOM wird berücksichtigt) und folgt niemals symbolischen Links.
Parameterquery und scope gemäß der folgenden Tabelle. scope ist erforderlich für Entrypoints (Kartenidentität), Repaints, FleetNumbers, Registrations (Fahrzeugidentität).
RückgabeSortierte Liste von ContentIdentity(Identity, Kind, DisplayName). Identitäten sind installationsrelative Pfade mit umgekehrten Schrägstrichen; Vergleiche unterscheiden nicht zwischen Groß- und Kleinschreibung.
AusgelöstOperationCanceledException beim Eintritt; ArgumentException, wenn Entrypoints ohne Scope abgefragt wird oder das Stammverzeichnis leer ist; FileNotFoundException (kein OL_E_-Code; die CLI bildet sie auf OL_E_NOT_FOUND ab), wenn die Karte oder das Fahrzeug des Scopes nicht installiert ist. Ein fehlendes Stamm- oder Inhaltsverzeichnis ergibt eine leere Liste, keinen Fehler.
AbbruchWird einmal beim Eintritt geprüft.
Laufende Sitzung erforderlichNein.
Verändert OMSI / Dateisystem / TransaktionNein / Nein / Keine.
StabilitätMaps, Situations, Vehicles: STABLE_BETA (jeder zur Laufzeit validierte Plan wird über sie aufgelöst). Entrypoints, Repaints, Hofs, FleetNumbers, Registrations, Addons: EXPERIMENTAL (nur statische Nachweise).
Beispielvar 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));

Werte von ContentQueryKind und Ergebnisse:

WertScopeIdentityKindDisplayName
Mapskeinermaps\<dir>\global.cfgmapName des Kartenverzeichnisses
Situationskeinersituations\...\<file>.osnsituationvon der .osn referenzierte Kartenidentität (kann null sein)
VehicleskeinerVehicles\...\<file>.busvehicle[friendlyname] oder Dateiname
RepaintsFahrzeugidentität (erforderlich; ohne sie: leere Liste)<cti path>#item:<ordinal>repaintName aus [item]
HofskeinerVehicles\...\<file>.hofhofnull
FleetNumbersFahrzeugidentität (erforderlich; ohne sie: leere Liste)fahrzeugrelativer Quellpfad von [number]fleet-numbernull
RegistrationsFahrzeugidentität (erforderlich; ohne sie: leere Liste)registration_automatic / registration_list / registration_freeregistrationerste Wertzeile (null bei free)
AddonskeinerAddons\<dir>addondirectory-only
EntrypointsKartenidentität (erforderlich; ohne sie: ArgumentException)<map identity>#entrypoint:<SHA-256 of the 12-line record>entrypointBezeichnung des Einstiegspunkts

Identitäten von Einstiegspunkten dienen nur der Erkennung: Der Startpfad verwendet PresentedEntrypointIndex; die Übergabe einer EntrypointIdentity macht den Plan auf diesem Build nicht ausführbar (world.entrypoint-identity, RUNTIME_PARTIAL).

RecoverPendingAsync#

AspektDetail
SignaturTask<RecoveryStatus> RecoverPendingAsync(InstallationSpec installation, bool restore, CancellationToken cancellationToken = default)
ZweckMeldet oder vollendet eine veraltete dauerhafte Transaktion (<root>\.omsilaunch\journal.json), die ein abgestürzter Eigentümer hinterlassen hat. Hält die Installations-Lease für die Dauer des Aufrufs, sodass niemals unter einer startenden Sitzung wiederhergestellt wird. Öffentliche Capability session.recover; CLI /recovery-status und /recover.
Parameterinstallation.RootPath: das Installationsstammverzeichnis (normalisiert mit Path.GetFullPath). restore: false = nur melden; true = wiederherstellen, verifizieren, Journal und Backups löschen.
RückgabeRecoveryStatus(Pending, Recovered, Diagnostics): Pending = beim Start des Aufrufs existierte ein Journal; Recovered = eine Wiederherstellung wurde angefordert, ist gelaufen, und es ist kein Journal mehr vorhanden; Diagnostics = Hinweise zur Wiederherstellung (restore.session-artifact-removed mit Data["sha256"], OL_W_RESTORE_FOREIGN_FILE_RETAINED), leer, wenn nichts wiederhergestellt wurde.
AusgelöstInvalidOperationException("OL_E_INSTALLATION_BUSY: another OmsiLaunch owner holds this installation."), wenn die Lease gehalten wird; IOException("OL_E_INSTALLATION_BUSY: a journaled OMSI process is still alive."), wenn PID + Erstellungszeit + Pfad der ausführbaren Datei aus dem Journal noch zu einem aktiven Prozess passen oder (Journal nach HandoffCreated ohne PID) irgendeine Omsi.exe aus diesem Stammverzeichnis läuft; IOException mit OL_E_RECOVERY_BACKUP_CORRUPT, OL_E_RECOVERY_ABSENT_OWNERSHIP_MISMATCH, OL_E_RECOVERY_ABSENT_OWNERSHIP_UNVERIFIED, OL_E_RECOVERY_JOURNAL_REMOVE_FAILED oder einer Verifizierungsmeldung („Restore hash mismatch: ...“, „Restore presence mismatch: ...“); InvalidDataException("Invalid OmsiLaunch journal.") / JsonException bei einem beschädigten Journal; ArgumentException bei einem leeren Stammverzeichnis; OperationCanceledException. Immer wenn nach Beginn einer Wiederherstellung eine Ausnahme ausgelöst wird, bleibt das Journal erhalten, und der nächste Aufruf wiederholt den Vorgang idempotent.
AbbruchWird an die Schreibvorgänge für Journal/Backups weitergegeben; ein Abbruch während der Wiederherstellung lässt das Journal ausstehend.
Laufende Sitzung erforderlichNein (die Methode verweigert den Vorgang, solange ein Eigentümer aktiv ist).
Verändert den OMSI-ZustandNein.
Verändert das DateisystemNur mit restore == true: schreibt Originale aus verifizierten Backups zurück (Bytes, Zeitpunkt des letzten Schreibzugriffs, Erstellungszeit, Attribute; schreibgeschützte Originale werden behandelt; Write-Through + Flush; keine *.omsilaunch.tmp bleibt zurück), entfernt Sitzungsartefakte, löscht journal.json und backup\<sessionId>.
Transaktion / WiederherstellungVollendet die ausstehende Transaktion (Restoring → Restored → Journal entfernt).
StabilitätSTABLE_BETA: Meldepfad und Recovery nach vorzeitigem Beenden (Matrix RV-008), Recovery nach einer fehlgeschlagenen Wiederherstellung (Runtime-Abschluss F01), Verweigerung bei aktivem Eigentümer und im Fenster vor der PID (S04) sowie zurückgestellte Recovery vor dem Fingerabdruck beim Start (S05); siehe Status der Runtime-Validierung.
Beispielvar r = await launch.RecoverPendingAsync(new InstallationSpec(root), restore: true); Console.WriteLine($"pending={r.Pending} recovered={r.Recovered}");

Vertragstypen#

Optionale Werte und semantische Grundtypen#

TypDefinitionHinweise
OptionalValue<T>readonly record struct OptionalValue<T>(Presence Presence, T? Value); IsSet, statisch Unset, statisch Set(T)Unterscheidet „nicht angefordert“ von „mit einem Wert angefordert“. Die JSON-Form ist in der LaunchSpec-Referenz dokumentiert.
PresenceUnset = 0, Set = 1Byte-Enum.
SemanticDate(int Year, int Month, int Day)Wird nur bei DateTimeMode.Explicit validiert (Monat 1..12, Tag 1..31).
SemanticTime(int Hour, int Minute, int Second)Wird nur bei DateTimeMode.Explicit validiert (0..23, 0..59, 0..59).

LaunchSpec-Familie#

Alle folgenden Records sind in der LaunchSpec-Referenz Eigenschaft für Eigenschaft dokumentiert; diese Tabelle legt das Typinventar fest.

TypZweckStabilität
LaunchSpecStamm-Record der Anforderung mit den Accessoren EffectiveYear, EffectiveWeather, EffectiveInput, EffectiveDiagnostics, EffectivePresentation, EffectiveInternetTextures, die für optionale Mitglieder mit null Standardwerte einsetzen.STABLE_BETA
InstallationSpecRootPath, ExpectedExecutableSha256 (mitgeführt, nicht ausgewertet).STABLE_BETA / PARTIAL
WorldSpec, WorldMode, EntrypointSpec, EntrypointModeWeltauswahl. WorldMode: NewMap = 0, SavedSituation = 1, LastMapState = 2, LastSituation = 2 (veralteter Alias von LastMapState; niemals „die neueste .osn“). EntrypointMode: Unset, PresentedIndex, Identity (berechnet aus WorldSpec.Entrypoint).NewMap, SavedSituation: STABLE_BETA; LastMapState: UNAVAILABLE; EntrypointMode.Identity: PARTIAL
DateSpec, TimeSpec, YearSpec, DateTimeModeDateTimeMode: Unset, Explicit, System. Jeder andere Modus als Unset macht den Plan nicht ausführbar.PARTIAL (STATICALLY_PARTIAL)
WeatherSpec, WeatherModeWeatherMode: Unset, Preset, Icao, RealCurrent. Jeder andere Modus als Unset macht den Plan nicht ausführbar.PARTIAL
PlayerVehicleSpecModel, Repaint, Hof, FleetNumber, Registration, Enabled. Jedes gesetzte Feld macht den Plan nicht ausführbar.PARTIAL
EnvironmentSpecAcht IReadOnlyDictionary<string, OptionalValue<string>>-Gruppen semantischer options.cfg-Einstellungen.STABLE_BETA
InputSpecKeyboardDocument, ControllerDocument; jeder gesetzte Wert macht den Plan nicht ausführbar.PARTIAL
DiagnosticsSpecSechs boolesche Werte; mitgeführt, nicht ausgewertet.PARTIAL
SessionPresentationSpec, SplashModeSplashMode: Unset = 0, Native = 0 (Alias), Managed = 1.STABLE_BETA
InternetTexturesSpec, InternetTexturesModeInternetTexturesMode: Native, Disabled, Override.STABLE_BETA (Native), EXPERIMENTAL (Disabled, Override)
SessionProfileMetadataHerkunft eines kompilierten Sitzungsprofils (Id, Name, Version, Author, PresetId, PresetIndex, PresetName, PackagePath).STABLE_BETA
LaunchBehaviorSpecRestoreConfiguration (mitgeführt, die Wiederherstellung erfolgt immer), SuppressStaleClosecheckWarning, StartupTimeoutSeconds (1..600, Standard 180), ShutdownTimeoutSeconds (mitgeführt, nicht ausgewertet).STABLE_BETA / PARTIAL

Plan- und Statustypen#

TypFelderHinweise
SessionPlanSessionId (neue Guid pro Plan), BuildProfileId ("Omsi23004_692EBFBF"), Spec, Platform (RuntimePlatformInfo), ResolvedContent (ContentIdentity-Liste: map, vehicle, repaint, hof, situation, situation-map), TouchedFiles (eindeutige relative Pfade aus PlannedMutations), RuntimeArtifacts, RequiredCapabilities (Capability mit STATICALLY_VALIDATED oder UNAVAILABLE), UnsupportedRequestedFeatures (Capability-Einträge für angeforderte, aber nicht unterstützte Funktionen), PlannedMutations, Diagnostics, IsRunnable.Ein öffentlicher Record: Er kann bearbeitet werden oder veralten, weshalb StartSessionAsync erneut plant.
RuntimePlatformInfoOsFamily, OsVersion, OsArchitecture, HostArchitecture, OmsiArchitecture (X86), PluginArchitecture (X86), CurrentPlatformSupported (Windows 10+, x64-Betriebssystem und x64-Host), LegacyPlatform (immer false), Wow64Available, InstallationWritable, ProcessLaunchSupported, PluginRuntimeSupported, NativeInteropSupported, SharedMemorySupported, ExactRestoreSupported (alle gleich CurrentPlatformSupported).
CapabilityName, Available, EvidenceState, Reason.Nachweis-Strings sind Freitext (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).Darstellungsänderungen verwenden die Schlüssel session-presentation.splash, internet-textures.override, internet-textures.cache, internet-textures.target.
LaunchDiagnosticCode, Message, Data (optionale String-Map).Codes, die mit OL_E_ beginnen, sind Fehler, OL_W_ Warnungen, alles andere ist informativ.
SessionStatusSessionId, State (SessionState), Diagnostics, RuntimeEvents.Sitzungsdiagnosen enthalten keine Plandiagnosen.
RuntimeEventType, TimestampUtc (Empfangszeit beim Host), Sequence (ab 1, pro Sitzung), Data.Auf die 256 neuesten Ereignisse begrenzt (die ältesten werden verworfen). Der Telemetrie-Slot enthält nur den jeweils letzten Wert: Ereignisse, die schneller als die 100-ms-Abfrage des Hosts ausgegeben werden, können verpasst werden. Kein verlustfreies Protokoll.
SessionHandleSessionId.Prozesslokal.
RecoveryStatusPending, Recovered, Diagnostics.Siehe RecoverPendingAsync.
ContentIdentityIdentity, Kind, DisplayName.Siehe DiscoverAsync.
ContentQueryKindMaps, Situations, Vehicles, Repaints, Hofs, FleetNumbers, Registrations, Addons, Entrypoints.

SessionState#

Byte-Enum in Deklarationsreihenfolge: Created, ValidatingPlatform, Planning, AcquiringInstallationLock, RecoveringPreviousTransaction, Snapshotting, ApplyingConfiguration, DeployingRuntime, CreatingStartupHandoff, StartingProcess, WaitingForPlugin, PluginBootstrap, StartingWorld, EnteringGameplay, Running, ProcessExited, Restoring, CleaningRuntime, Completed, Failed. ValidatingPlatform, Planning und EnteringGameplay werden vom aktuellen Service nie gesetzt; Snapshotting ist ein Übergangszustand und praktisch nicht beobachtbar. Endzustände: Completed, Failed. Vollständige Semantik: Sitzungslebenszyklus.

Typen der Runtime-Steuerung#

TypDefinitionStabilität
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
RuntimeCommandWireStatischer Codec, den Host und Plugin für das Mailbox-Envelope verwenden: Magic 0x4F4C5243 („OLRC“), Version 1, 72-Byte-Header in Little-Endian (Magic, Version, Art 1 = Anforderung / 2 = Antwort, Gesamtlänge, Sitzungs-Guid, Anforderungs-ID, Nutzdatenlänge, SHA-256 der Nutzdaten), gefolgt von UTF-8-JSON-Nutzdaten. SerializeRequest, SerializeResponse, TryDeserializeRequest, TryDeserializeResponse, TryReadRequestId.INTERNAL: öffentlich, weil beide Enden der Bridge ihn gemeinsam verwenden; keine Integrationsschnittstelle; das Format kann sich mit der Protokollversion ändern.
StartupHandoff(Guid SessionId, string BuildProfileId, WorldMode WorldMode, string MapIdentity, int PresentedEntrypointIndex, bool HeadlessStart, bool PlayerVehicleEnabled, DateTimeMode DateMode, DateTimeMode TimeMode, string EntrypointIdentity, string SituationIdentity) – was der Host dem Plugin in der speicherabgebildeten Datei OmsiLaunch.Handoff.<sessionId> bereitstellt.INTERNAL
StartupHandoffWireCodec: Magic 0x4F4C5348, Version 4 (liest 3 und 4), 64-Byte-Header mit SHA-256-Integritätsprüfung der Nutzdaten.INTERNAL

Das Plugin lehnt eine Übergabe ab (plugin.request.unsupported → OL_E_CAPABILITY_UNAVAILABLE), sofern nicht WorldMode NewMap oder SavedSituation ist, HeadlessStart true ist, PlayerVehicleEnabled false ist, beide Datums-/Zeitmodi Unset sind und eine gespeicherte Situation eine nicht leere Identität hat. Der Planer erzwingt dieselben Bedingungen früher, sodass ein ausführbarer Plan dies nie auslöst.

Typen der Capability-Registry#

TypZweck
PublicCapabilityRegistryProtocolVersion ("0.1"), All (36 PublicCapabilityDescriptor-Einträge), PublicRuntimeOperationIds (die 48 konkreten Operations-IDs, die ein Frontend weiterleiten darf), IsPublicRuntimeOperation, GetRuntimeArguments, ValidateRuntimeArguments (gibt PublicRuntimeArgumentValidation zurück), IsInternalResultKey. Durchgesetzt von ExecuteRuntimeAsync, der CLI und der lokalen Steuerungsebene.
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.

Vollständiger Katalog: Capabilities.

Erweiterungsmethoden von D3DRuntimeApi#

Typisierte Wrapper um ExecuteRuntimeAsync für die d3d.*-Operationen (EXPERIMENTAL, PublicExperimental-Capability d3d.texture). Sie vergeben Anforderungs-IDs aus einem prozessweiten Zähler, der bei 30 000 beginnt, und verwenden standardmäßig ein Timeout von 5 s (außer GetD3DStatusAsync, das eines verlangt).

MethodeOperationArgumente und Grenzen
GetD3DStatusAsync(IOmsiLaunch, SessionHandle, TimeSpan timeout, CancellationToken) → D3DDeviceStatusd3d.statuskeine
CreateD3DTextureAsync(..., uint width, uint height, D3DTextureFormat format, uint levels = 1, TimeSpan? timeout, ...) → D3DTextureDescriptiond3d.texture.createBreite/Höhe 1..4096, Levels 0..16
DescribeD3DTextureAsync(..., D3DTextureHandle handle, uint level = 0, ...)d3d.texture.describeLevel 0..15
UpdateD3DTextureAsync(..., D3DTextureHandle handle, D3DTextureUpdate update, ...)d3d.texture.updateD3DTextureUpdate(Level, X, Y, Width, Height, Pixels): x/y 0..4095, Breite/Höhe 1..4096, Pixel ≤ 48 KiB (in der Übertragung Base64-kodiert)
ReleaseD3DTextureAsync(..., D3DTextureHandle handle, ...)d3d.texture.releaseeine wiederholte Freigabe wird mit OL_E_D3D_RESOURCE_RELEASED abgelehnt

Typen: D3DDeviceStatus(Available, State, Generation, LiveTextureCount, ResetHookInstalled, ExecutionThreadId, LastResetThreadId, QueryInterfaceHResult, CooperativeLevelHResult, OwnedDeviceReferences); D3DDeviceState: NotReady, Ready, Lost, Resetting, Stopping, Stopped; D3DTextureHandle(Value) mit 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.

Fehler: Ein fehlgeschlagenes Ergebnis wird als OmsiRuntimeException(Code, detail) erneut ausgelöst, wobei Code der ErrorCode des Ergebnisses ist (oder OL_E_RUNTIME_OPERATION_FAILED, wenn dieser fehlt) und die Meldung "<code>: <Values["detail"]>" lautet; ein erfolgreiches Ergebnis ohne Werte oder ein unbekannter Gerätezustands-String löst OmsiRuntimeException("OL_E_RUNTIME_PROTOCOL_MISMATCH", ...) aus. Alles, was ExecuteRuntimeAsync auslöst, wird unverändert weitergegeben. Die Behandlung des Geräte-Resets ist zur Laufzeit validiert: Ein Reset führt das Gerät über Resetting zurück zu Ready und macht jede aktive Textur ungültig (OL_E_D3D_STALE_RESOURCE_HANDLE, Runtime-Abschluss D01); GetCapabilitiesAsync meldet runtime.d3d.lifecycle.reset weiterhin als IMPLEMENTED_NOT_RUNTIME_VALIDATED (Verzögerung der Selbstauskunft). Der Übergang Lost lässt sich von außerhalb des Produkts nicht herbeiführen und ist nur offline abgedeckt.

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

Typen des Prozessvertrags#

TypInhalt
PublicExitCodeSuccess = 0, SessionFailed = 1, InvalidArguments = 2, UnsupportedProfile = 3, NoActiveSession = 4, RuntimeUnavailable = 5, NotFound = 6, OperationRejected = 7, TransactionRecoveryFailed = 8, InternalError = 10. Wird nur von der CLI verwendet (Exitcodes); die API beendet niemals den Prozess.
PublicErrorCategoryString-Konstanten, die in CLI-/Steuerungs-Fehler-Envelopes verwendet werden: invalid_argument, unsupported_profile, session, runtime, not_found, transaction, internal.
PublicErrorCodesEine const string pro Code (143: 142 OL_E_-Fehler und 1 OL_W_-Warnung) sowie All, der Katalog aus PublicErrorDescriptor(Code, Category). Kategorien: Cli, Compatibility, Content, Installation, InvalidArgument, LaunchSpec, LocalControl, Other, Presentation, Process, Runtime, RuntimeD3D, Session, SessionProfile, Transaction, Warning. Referenz: Fehlercodes.
PublicErrorDescriptor(string Code, string Category).
OmsiRuntimeExceptionEigenschaft Code plus Meldung; wird nur von D3DRuntimeApi ausgelöst.

InstallationPaths (Installationsidentität und Pfadeingrenzung)#

Stabilität: STABLE_BETA (reine Funktionen, keine E/A, kein OMSI-Zustand, keine Änderung des Dateisystems, keine Beteiligung an Transaktionen, keine laufende Sitzung erforderlich). Dies ist die einzige Definition, die von der Installations-Lease, dem Namen der lokalen Steuerungs-Pipe, der Eingrenzung der Sitzungsprofil-Assets, der Validierung von Internettexturen-Zielen und dem Modellpfad für den Runtime-Spawn verwendet wird.

MitgliedVerhalten
string NormalizeRoot(string root)Path.GetFullPath(root) ohne abschließende Trennzeichen, außer bei einem Laufwerksstamm (C:\), der erhalten bleibt. Löst .- und ..-Segmente auf, behandelt / und \ gleich und fasst wiederholte Trennzeichen zusammen. Löst Junctions oder symbolische Links nicht auf. Löst ArgumentException bei einem null- oder leeren Stammverzeichnis aus.
string IdentityKey(string root)NormalizeRoot(root) in Großbuchstaben. Lexikalisch gleichwertige Schreibweisen eines Stammverzeichnisses (C:\OMSI, C:\OMSI\, C:\OMSI\., C:\foo\..\OMSI, c:\omsi) teilen sich einen Schlüssel; verschiedene Stammverzeichnisse (C:\OMSI-A, C:\OMSI-B) niemals.
bool TryGetContainedRelativePath(string root, string candidate, out string relativePath)Löst candidate auf (relativ zu root oder absolut) und gibt nur dann true zurück, wenn es strikt unterhalb von root liegt; relativePath ist die kanonische Schreibweise mit \. Verwendet Segmente von Path.GetRelativePath, sodass ein Geschwisterverzeichnis wie C:\OMSI-A\x niemals innerhalb von C:\OMSI liegt; das Stammverzeichnis selbst, andere Volumes und ..-Ausbrüche ergeben false.
IReadOnlyList<string> Segments(string relativePath)Teilt an / und \ und verwirft leere Segmente.
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"

Sitzungsprofile (OmsiLaunch.Core)#

Stabilität: EXPERIMENTAL. Diese Typen kompilieren ein YAML-Sitzungsprofil (<root>\.omsilaunch\session-profiles\<id>\profile.yaml, Schema omsilaunch.session-profile/v1) zu einer LaunchSpec. Die CLI-Option /predefined-profile:<id> /predefined-profile-index:<n> verwendet genau diese Aufrufe; ein Integrator kann sie verwenden, um ein Profil über die API zu starten.

MitgliedVerhalten
SessionProfileCompiler.Load(string installationRoot, string id, int presetIndex) → SessionProfilePackageLiest und validiert das Paket. id muss ein einfacher Verzeichnisname sein (sonst OL_E_SESSION_PROFILE_PATH_ESCAPE); presetIndex liegt im Bereich 1..5 (OL_E_SESSION_PROFILE_PRESET_NOT_FOUND). Fehlende Datei: OL_E_SESSION_PROFILE_NOT_FOUND; größer als MaxBytes (256 KiB), ungültiges YAML, Anker, unbekannte Schlüssel oder eine id, die vom Verzeichnisnamen abweicht: OL_E_SESSION_PROFILE_INVALID; anderes schema: OL_E_SESSION_PROFILE_SCHEMA_UNSUPPORTED. Asset-Pfade sind auf das Paket beschränkt (OL_E_SESSION_PROFILE_PATH_ESCAPE, OL_E_SESSION_PROFILE_ASSET_MISSING). Jeder Fehler ist eine SessionProfileException.
SessionProfileCompiler.Apply(SessionProfilePackage profile, LaunchSpec baseline, WorldMode selectedWorldMode) → LaunchSpecGibt baseline mit angewendetem Profil zurück: für NewMap den new-Block des Profils (Karte, Einstiegspunkt sowie etwaige Datums-/Zeit-/Jahres-/Wetterangaben, die den Plan auf diesem Build nicht ausführbar machen); die settings der Voreinstellung über Environment.General zusammengeführt; Presentation, InternetTextures und Behavior der Voreinstellung, sofern vorhanden; und SessionProfile = profile.Metadata. Für NewMap prüft die Methode außerdem die Karte gegen die compatibility-Liste des Profils (OL_E_SESSION_PROFILE_MAP_MISMATCH).
SessionProfileCompiler.ValidateCompatibility(SessionProfilePackage, string installationRoot, WorldSpec world, WorldMode mode)Die Kompatibilitätsprüfung für die anderen Modi (für SavedSituation wird die Karte aus der .osn gelesen). Die CLI ruft sie auf, nachdem die endgültige WorldSpec erstellt wurde.
SessionProfileCompiler.Schema, MaxBytes, SchemaKeys"omsilaunch.session-profile/v1", 262144 und die zulässigen Schlüssel pro YAML-Mapping.
SessionProfilePackage(RootPath, Metadata, CompatibleMaps, New, Preset), ProfileNew, ProfilePresetDas geladene Paket; Preset ist nur die ausgewählte Voreinstellung.
SessionProfileException(string code, string message)IOException mit Code (einer der OL_E_SESSION_PROFILE_*-Codes); die Meldung lautet "<code>: <message>".

Die CLI lehnt zusätzlich Befehlszeilen-Flags ab, die mit dem Profil in Konflikt stehen (OL_E_SESSION_PROFILE_OVERRIDE_CONFLICT); diese Prüfung ist nicht Teil des Compilers. Die vollständige Zusammenführungsreihenfolge finden Sie unter Sitzungsprofile.

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
}

Plattformtypen (OmsiLaunch.Process)#

TypStabilitätVerwendung
IRuntimePlatformSTABLE_BETA als Parametertyp des Konstruktors von OmsiLaunchServiceErkennt die Plattform, prüft die Beschreibbarkeit, startet, beobachtet, beendet Omsi.exe und wartet darauf. Übergeben Sie new CurrentWindowsX64Platform(); eine eigene Implementierung wird nicht unterstützt.
CurrentWindowsX64PlatformSTABLE_BETADie einzige Implementierung: Windows-x64-Host, CreateProcessW für Omsi.exe, TerminateProcess für den kanonischen Stopp. Ihre Methoden werden vom Service aufgerufen; Integratoren erzeugen sie nur. Mitglieder (gemeinsam mit IRuntimePlatform): Detect(root) liefert die RuntimePlatformInfo des Plans; ValidateCurrent(info) löst OL_E_UNSUPPORTED_OPERATING_SYSTEM / OL_E_UNSUPPORTED_OS_ARCHITECTURE / OL_E_PLATFORM_CAPABILITY_MISSING aus, wenn der Host keine Sitzung ausführen kann; IsInstallationWritable(root) liegt OL_E_INSTALLATION_NOT_WRITABLE zugrunde; StartAsync(request, sha256) erzeugt Omsi.exe und erfasst die Prozessidentität (PID, Erstellungszeit, Pfad und den vom Service berechneten Hash; OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED); HasExited, WaitForExitAsync und Terminate beobachten und beenden den Prozess.
InstallationLease, LaunchedProcess, ProcessIdentity, ReleaseManifest, RuntimeArtifact, RuntimeArtifactSet, StartupProcessRequest, CurrentRuntimeCommandStore, IOmsiProcessController, OmsiProcessStateINTERNALIn der Assembly öffentlich, weil der Service und die Tests sie gemeinsam verwenden. Keine Integrationsschnittstelle; LaunchedProcess kapselt intern die Prozess- und Thread-Handles von OMSI (sie sind keine öffentlichen Mitglieder) und wird niemals von IOmsiLaunch zurückgegeben.

Threadsicherheit#

  • OmsiLaunchService ist für gleichzeitige Aufrufe auf verschiedenen Sitzungen sicher: Sitzungen liegen in einem ConcurrentDictionary, und jede sitzungsbezogene Änderung erfolgt unter der privaten Sperre der Sitzung.
  • Gleichzeitige Aufrufe auf derselben Sitzung sind sicher, werden aber dort serialisiert, wo es darauf ankommt: ExecuteRuntimeAsync nimmt ein Gate pro Sitzung, sodass ein zweiter Befehl auf den ersten wartet (sein Timeout beginnt, wenn er bereitgestellt wird).
  • Der Supervisor läuft als Thread-Pool-Task (Task.Run) ab dem Moment, in dem StartSessionAsync zurückkehrt, bis sich die Sitzung im Endzustand befindet; er fragt Telemetrie und Prozess alle 100 ms ab. Aufrufer führen niemals Supervisor-Code aus.
  • StopAsync und GetStatusAsync werden synchron abgeschlossen und dürfen von jedem Thread aufgerufen werden, auch innerhalb eines ProcessExit-Handlers (die CLI tut dies mit einem Budget von 4 s).
  • Kein API-Aufruf ist an einen Thread gebunden; keiner benötigt einen Synchronisierungskontext.

Was nicht Teil der API ist#

  • Kein IntPtr, kein nint, keine Win32-Handles, keine nativen Adressen, keine VMT-Zeiger und keine Prozessobjekte. Ergebniswerte, deren Schlüssel mit internal_ beginnen oder mit _address, _pointer, _vmt enden, werden entfernt, bevor ein Ergebnis ExecuteRuntimeAsync verlässt.
  • Keine internal.*-Runtime-Operationen: internal.road-vehicles.make-basic ist in der Registry InternalOnly und liefert über API und CLI OL_E_RUNTIME_OPERATION_UNKNOWN.
  • Keine direkten Lese- oder Schreibzugriffe auf den OMSI-Speicher, kein Dateizugriff auf die Installation über das hinaus, was eine LaunchSpec deklariert.
  • Keine prozessübergreifenden Handles: Die lokale Steuerungsebene ist der einzige prozessübergreifende Weg, und sie akzeptiert ausschließlich session.status, session.events, session.stop und runtime.execute.
  • Kein kooperatives Herunterfahren von OMSI, kein LAST_MAP_STATE, keine Anwendung von Datum/Zeit/Wetter/Spielerfahrzeug, keine Overlays für Tastatur-/Controller-Dokumente auf diesem Build.

Stabilitätsübersicht#

SchnittstelleStabilität
Konstruktor von OmsiLaunchService, OmsiLaunchRuntimePathsSTABLE_BETA
PlanSessionAsync, StartSessionAsync (NEW_MAP, SAVED_SITUATION), GetStatusAsync, WaitForAsync, StopAsync, CloseAsyncSTABLE_BETA
Transport von ExecuteRuntimeAsync; PublicStableBeta-OperationenSTABLE_BETA
PublicExperimental-Operationen, D3DRuntimeApi, camera.lockEXPERIMENTAL
Listeninhalt von GetCapabilitiesAsync, Datums-/Zeit-/Wetter-/Spielerfahrzeug-/Eingabemitglieder der Spezifikation, DiagnosticsSpec, ExpectedExecutableSha256, RestoreConfiguration, ShutdownTimeoutSecondsPARTIAL
RuntimeCommandWire, StartupHandoff, StartupHandoffWire, Implementierungen von IRuntimePlatform, alle Implementierungs-AssembliesINTERNAL
WorldMode.LastMapState / LastSituation, weather.set, internal.*-OperationenUNAVAILABLE