Ü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.
Der Vertrag ist für die Protokolllinie 0.1 eingefroren, und der Pfad ist in research/reports/OMSILAUNCH-RUNTIME-VALIDATION-MATRIX.md zur Laufzeit validiert.
EXPERIMENTAL
Aufrufbar und getestet, aber der Vertrag oder der Runtime-Nachweis kann sich ändern, bevor er stabil wird.
PARTIAL
Im Vertrag vorhanden; nur ein Teil des Verhaltens ist implementiert oder validiert (der Text gibt an, welcher Teil).
INTERNAL
Aus technischen Gründen in der Assembly öffentlich (die Bridge verwendet den Typ gemeinsam), aber keine Integrationsschnittstelle; kann sich ohne Ankündigung ändern.
UNAVAILABLE
Im Vertrag vorhanden, wird aber vom aktuellen Build abgelehnt.
Reine 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.Core
OmsiLaunchService (die Implementierung von IOmsiLaunch), OmsiLaunchRuntimePaths, SessionPlanner, LaunchValidation, SessionProfileCompiler.
OmsiLaunch.Process
IRuntimePlatform und CurrentWindowsX64Platform (der einzige Plattformadapter), InstallationLease. Wird zum Erzeugen des Service benötigt.
Implementierungs-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);
}
Parameter
Gültiger Wert
Ungültig / Standard
platform
new 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.
PluginBuildDirectory
Verzeichnis, 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.
NativeBridgePath
Pfad von OmsiLaunch.Native.x86.dll (im Paket: <package>\plugins\OmsiLaunch.Native.x86.dll).
Wie oben.
ReleaseManifestPath
release-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.
StartSessionAsync 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 prozesslokal
SessionHandle 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 auf
Ab 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 Sitzungen
Ein 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üft
StartSessionAsync 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.
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.
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
}
Kompiliert 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.
Parameter
spec: 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ückgabe
SessionPlan 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 Fehler
Jeder 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öst
OperationCanceledException, 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.
Abbruch
Wird einmal beim Eintritt geprüft. Danach ist die Planung synchrone Dateisystemarbeit.
Laufende Sitzung erforderlich
Nein.
Verändert den OMSI-Zustand
Nein.
Verändert das Dateisystem
Nein (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 / Wiederherstellung
Keine.
Einschränkungen
Die 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ät
STABLE_BETA.
Beispiel
var plan = await launch.PlanSessionAsync(spec); Console.WriteLine(plan.IsRunnable ? "READY" : string.Join(", ", plan.Diagnostics.Where(d => d.Code.StartsWith("OL_E_")).Select(d => d.Code)));
Startet 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.
Parameter
plan: 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ückgabe
SessionHandle(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öst
InvalidOperationException("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 Fehler
Jeder 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).
Abbruch
Vor/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 erforderlich
Nein.
Verändert den OMSI-Zustand
Ja: 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 Dateisystem
Ja, 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änkungen
Nur 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ät
STABLE_BETA (die Lebenszyklen NEW_MAP und SAVED_SITUATION sind zur Laufzeit validiert).
Beispiel
var session = await launch.StartSessionAsync(plan); var s = await launch.GetStatusAsync(session); if (s.State == SessionState.Failed) Console.WriteLine(s.Diagnostics.Last(d => d.Code.StartsWith("OL_E_")).Message);
Liest den semantischen Lebenszykluszustand, die bisher gesammelten Diagnosen und die begrenzte Liste der Runtime-Ereignisse. Öffentliche Capability session.status.
Parameter
session: ein von StartSessionAsync zurückgegebenes, noch nicht geschlossenes Handle.
Rückgabe
SessionStatus(SessionId, State, Diagnostics, RuntimeEvents): ein unveränderlicher Snapshot (Arrays werden unter der Sitzungssperre kopiert). RuntimeEvents ist für eine aktive Sitzung nie null.
Ausgelöst
KeyNotFoundException bei unbekannten/geschlossenen Handles. Löst sonst nie eine Ausnahme aus.
Abbruch
Das Token wird ignoriert (der Aufruf wird synchron abgeschlossen).
Laufende Sitzung erforderlich
Nein.
Verändert OMSI / Dateisystem / Transaktion
Nein / Nein / Keine.
Stabilität
STABLE_BETA.
Beispiel
var status = await launch.GetStatusAsync(session); Console.WriteLine($"{status.State} events={status.RuntimeEvents!.Count}");
Fragt (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.
Parameter
state: 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ückgabe
Der 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öst
KeyNotFoundException; OperationCanceledException, wenn das Token des Aufrufers abgebrochen wird (nur der Abbruch durch den Aufrufer wird weitergegeben, das interne Timeout nicht).
Abbruch
Das Token des Aufrufers wird bei jedem 100-ms-Takt berücksichtigt.
Laufende Sitzung erforderlich
Nein.
Verändert OMSI / Dateisystem / Transaktion
Nein / Nein / Keine.
Stabilität
STABLE_BETA.
Beispiel
var running = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(185)); if (running.State != SessionState.Running) { /* timed out or Failed */ }
Fordert 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.
Parameter
session.
Rückgabe
Abgeschlossener Task; wartet weder auf die Beendigung noch auf die Wiederherstellung. Verwenden Sie WaitForAsync(session, SessionState.Completed, ...), um den Abschluss zu beobachten.
Ausgelöst
KeyNotFoundException.
Abbruch
Token wird ignoriert.
Laufende Sitzung erforderlich
Nein. 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-Zustand
Ja: beendet den OMSI-Prozess (Exitcode 1).
Verändert das Dateisystem
Indirekt: löst Wiederherstellung, Löschen des Journals und Entfernen der Backups durch den Supervisor aus.
Transaktion / Wiederherstellung
Lö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ät
STABLE_BETA.
Beispiel
await launch.StopAsync(session); var done = await launch.WaitForAsync(session, SessionState.Completed, TimeSpan.FromMinutes(1));
Gibt 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.
Parameter
session.
Rückgabe
Wird 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öst
KeyNotFoundException; 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.
Abbruch
Gilt nur für das Warten; die Wiederherstellung wird niemals abgebrochen.
Laufende Sitzung erforderlich
Nein.
Verändert den OMSI-Zustand
Ja, wenn die Sitzung noch aktiv ist (wie bei StopAsync).
Verändert das Dateisystem
Indirekt (Wiederherstellung durch den Supervisor).
Transaktion / Wiederherstellung
Garantiert, 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.
Fü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.
Parameter
command.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üfungen
1. 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ückgabe
RuntimeCommandResult(SessionId, RequestId, Succeeded, ErrorCode, Values). Bei Erfolg enthält Values die semantischen Zeichenfolgen der Operation (pro Operation in Runtime-Steuerung dokumentiert).
Im Ergebnis übermittelte Fehler
OL_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öst
KeyNotFoundException; 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.
Abbruch
Wird 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 erforderlich
Ja (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-Zustand
Abhä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 Dateisystem
Keine Schreibvorgänge des Hosts. OMSI kann infolgedessen eigene Dateien schreiben (nicht nachverfolgt).
Transaktion / Wiederherstellung
Keine.
Einschränkungen
Ein 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ät
Transport und Vertrag STABLE_BETA; die Stabilität pro Operation folgt PublicCapabilityRegistry (PublicStableBeta → STABLE_BETA, PublicExperimental → EXPERIMENTAL) mit den oben genannten Ausnahmen.
Beispiel
var r = await launch.ExecuteRuntimeAsync(session, new RuntimeCommand(session.SessionId, 42, "road-vehicle.read", new Dictionary<string, string> { ["handle"] = "rv-000001" }), TimeSpan.FromSeconds(5)); if (!r.Succeeded) Console.WriteLine($"{r.ErrorCode} {r.Values?["detail"]}");
Gibt 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.
Parameter
installation.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ückgabe
51 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 PublicCapabilityRegistry
PublicCapabilityRegistry.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öst
OperationCanceledException beim Eintritt; ArgumentException bei einem leeren Stammpfad.
Abbruch
Wird einmal beim Eintritt geprüft.
Laufende Sitzung erforderlich
Nein.
Verändert OMSI / Dateisystem / Transaktion
Nein / Nein / Keine.
Stabilität
Aufrufvertrag STABLE_BETA; der Listeninhalt ist ein von Hand gepflegtes Inventar: PARTIAL.
Beispiel
foreach (var c in await launch.GetCapabilitiesAsync(new InstallationSpec(root))) Console.WriteLine($"{c.Name} {c.Available} {c.EvidenceState} {c.Reason}");
Zä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.
Parameter
query und scope gemäß der folgenden Tabelle. scope ist erforderlich für Entrypoints (Kartenidentität), Repaints, FleetNumbers, Registrations (Fahrzeugidentität).
Rückgabe
Sortierte Liste von ContentIdentity(Identity, Kind, DisplayName). Identitäten sind installationsrelative Pfade mit umgekehrten Schrägstrichen; Vergleiche unterscheiden nicht zwischen Groß- und Kleinschreibung.
Ausgelöst
OperationCanceledException 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.
Abbruch
Wird einmal beim Eintritt geprüft.
Laufende Sitzung erforderlich
Nein.
Verändert OMSI / Dateisystem / Transaktion
Nein / Nein / Keine.
Stabilität
Maps, Situations, Vehicles: STABLE_BETA (jeder zur Laufzeit validierte Plan wird über sie aufgelöst). Entrypoints, Repaints, Hofs, FleetNumbers, Registrations, Addons: EXPERIMENTAL (nur statische Nachweise).
Beispiel
var maps = await launch.DiscoverAsync(new InstallationSpec(root), ContentQueryKind.Maps); var entries = await launch.DiscoverAsync(new InstallationSpec(root), ContentQueryKind.Entrypoints, OptionalValue<string>.Set(maps[0].Identity));
Werte von ContentQueryKind und Ergebnisse:
Wert
Scope
Identity
Kind
DisplayName
Maps
keiner
maps\<dir>\global.cfg
map
Name des Kartenverzeichnisses
Situations
keiner
situations\...\<file>.osn
situation
von der .osn referenzierte Kartenidentität (kann null sein)
Vehicles
keiner
Vehicles\...\<file>.bus
vehicle
[friendlyname] oder Dateiname
Repaints
Fahrzeugidentität (erforderlich; ohne sie: leere Liste)
<cti path>#item:<ordinal>
repaint
Name aus [item]
Hofs
keiner
Vehicles\...\<file>.hof
hof
null
FleetNumbers
Fahrzeugidentität (erforderlich; ohne sie: leere Liste)
fahrzeugrelativer Quellpfad von [number]
fleet-number
null
Registrations
Fahrzeugidentität (erforderlich; ohne sie: leere Liste)
Kartenidentität (erforderlich; ohne sie: ArgumentException)
<map identity>#entrypoint:<SHA-256 of the 12-line record>
entrypoint
Bezeichnung 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).
Meldet 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.
Parameter
installation.RootPath: das Installationsstammverzeichnis (normalisiert mit Path.GetFullPath). restore: false = nur melden; true = wiederherstellen, verifizieren, Journal und Backups löschen.
Rückgabe
RecoveryStatus(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öst
InvalidOperationException("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.
Abbruch
Wird an die Schreibvorgänge für Journal/Backups weitergegeben; ein Abbruch während der Wiederherstellung lässt das Journal ausstehend.
Laufende Sitzung erforderlich
Nein (die Methode verweigert den Vorgang, solange ein Eigentümer aktiv ist).
Verändert den OMSI-Zustand
Nein.
Verändert das Dateisystem
Nur 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 / Wiederherstellung
Vollendet die ausstehende Transaktion (Restoring → Restored → Journal entfernt).
Stabilität
STABLE_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.
Beispiel
var r = await launch.RecoverPendingAsync(new InstallationSpec(root), restore: true); Console.WriteLine($"pending={r.Pending} recovered={r.Recovered}");
Alle folgenden Records sind in der LaunchSpec-Referenz Eigenschaft für Eigenschaft dokumentiert; diese Tabelle legt das Typinventar fest.
Typ
Zweck
Stabilität
LaunchSpec
Stamm-Record der Anforderung mit den Accessoren EffectiveYear, EffectiveWeather, EffectiveInput, EffectiveDiagnostics, EffectivePresentation, EffectiveInternetTextures, die für optionale Mitglieder mit null Standardwerte einsetzen.
STABLE_BETA
InstallationSpec
RootPath, ExpectedExecutableSha256 (mitgeführt, nicht ausgewertet).
RestoreConfiguration (mitgeführt, die Wiederherstellung erfolgt immer), SuppressStaleClosecheckWarning, StartupTimeoutSeconds (1..600, Standard 180), ShutdownTimeoutSeconds (mitgeführt, nicht ausgewertet).
Darstellungsänderungen verwenden die Schlüssel session-presentation.splash, internet-textures.override, internet-textures.cache, internet-textures.target.
LaunchDiagnostic
Code, Message, Data (optionale String-Map).
Codes, die mit OL_E_ beginnen, sind Fehler, OL_W_ Warnungen, alles andere ist informativ.
SessionStatus
SessionId, State (SessionState), Diagnostics, RuntimeEvents.
Sitzungsdiagnosen enthalten keine Plandiagnosen.
RuntimeEvent
Type, 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.
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.
Statischer 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
StartupHandoffWire
Codec: 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 WorldModeNewMap 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.
ProtocolVersion ("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.
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).
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);
}
Success = 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.
PublicErrorCategory
String-Konstanten, die in CLI-/Steuerungs-Fehler-Envelopes verwendet werden: invalid_argument, unsupported_profile, session, runtime, not_found, transaction, internal.
PublicErrorCodes
Eine 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).
OmsiRuntimeException
Eigenschaft 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.
Mitglied
Verhalten
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.
var same = InstallationPaths.IdentityKey(@"C:\OMSI") == InstallationPaths.IdentityKey(@"c:\foo\..\OMSI\"); // true
InstallationPaths.TryGetContainedRelativePath(@"C:\OMSI", @"Sceneryobjects\\x\texture\a.tga", out var relative); // true, "Sceneryobjects\x\texture\a.tga"
Stabilitä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.
Mitglied
Verhalten
SessionProfileCompiler.Load(string installationRoot, string id, int presetIndex) → SessionProfilePackage
Liest 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.
Gibt 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).
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.
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
}
STABLE_BETA als Parametertyp des Konstruktors von OmsiLaunchService
Erkennt 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.
CurrentWindowsX64Platform
STABLE_BETA
Die 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.
In 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.
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.
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.
Listeninhalt von GetCapabilitiesAsync, Datums-/Zeit-/Wetter-/Spielerfahrzeug-/Eingabemitglieder der Spezifikation, DiagnosticsSpec, ExpectedExecutableSha256, RestoreConfiguration, ShutdownTimeoutSeconds
PARTIAL
RuntimeCommandWire, StartupHandoff, StartupHandoffWire, Implementierungen von IRuntimePlatform, alle Implementierungs-Assemblies