Traducción de la página original en inglés de OmsiLaunch 0.1.0-beta3. La página en inglés es la referencia normativa: si hay diferencias, prevalecen la página en inglés y el código.
Esta página es la referencia normativa de la API pública administrada de OmsiLaunch 0.1.0-beta3: el ensamblado OmsiLaunch.Api (contratos) y el punto de entrada para integradores OmsiLaunchService en OmsiLaunch.Core. Documenta únicamente lo que hace el código actual. Todo lo que un integrador puede llamar, recibir u observar se enumera aquí con su nivel de estabilidad; todo lo que no aparece en esta página no es una superficie de integración.
El inventario de la API pública generado enumera cada tipo y miembro público de OmsiLaunch.Api, OmsiLaunch.Core y OmsiLaunch.Process con su firma y su estabilidad; un gate de documentación (control de calidad de la documentación) falla cuando el inventario y los ensamblados difieren. Esta página explica la semántica.
El contrato está congelado para la línea de protocolo 0.1 y la ruta está validada en runtime en research/reports/OMSILAUNCH-RUNTIME-VALIDATION-MATRIX.md.
EXPERIMENTAL
Se puede llamar y está probado, pero el contrato o la evidencia de runtime pueden cambiar antes de que pase a ser estable.
PARTIAL
Presente en el contrato; solo una parte del comportamiento está implementada o validada (el texto indica qué parte).
INTERNAL
Es público en el ensamblado por razones técnicas (el puente comparte el tipo), pero no es una superficie de integración; puede cambiar sin previo aviso.
UNAVAILABLE
Presente en el contrato, pero el build actual lo rechaza.
Contratos puros: records, enums, IOmsiLaunch, registro de capacidades, catálogo de errores, formatos de transmisión, helpers de D3D. No contiene ningún IntPtr, nint, handle de Win32, dirección nativa ni objeto de proceso.
OmsiLaunch.Core
OmsiLaunchService (la implementación de IOmsiLaunch), OmsiLaunchRuntimePaths, SessionPlanner, LaunchValidation, SessionProfileCompiler.
OmsiLaunch.Process
IRuntimePlatform y CurrentWindowsX64Platform (el único adaptador de plataforma), InstallationLease. Necesarios para construir el servicio.
Ensamblados de implementación. Sus tipos públicos son INTERNAL para los integradores.
Punto de entrada: OmsiLaunchService y OmsiLaunchRuntimePaths#
public sealed record OmsiLaunchRuntimePaths(string PluginBuildDirectory, string NativeBridgePath, string? ReleaseManifestPath = null);
public sealed class OmsiLaunchService : IOmsiLaunch
{
public OmsiLaunchService(IRuntimePlatform platform, OmsiLaunchRuntimePaths runtimePaths);
}
Parámetro
Valor válido
No válido / predeterminado
platform
new CurrentWindowsX64Platform() (espacio de nombres OmsiLaunch.Process). Detecta la plataforma, crea el proceso de OMSI con CreateProcessW, espera a que termine y lo finaliza.
No se distribuye ninguna otra implementación. Un IRuntimePlatform personalizado es INTERNAL.
PluginBuildDirectory
Directorio que contiene los archivos de referencia del cierre del plugin permanente: OmsiLaunch.Plugin.opl, OmsiLaunch.PluginNE.dll, OmsiLaunch.Plugin.deps.json, OmsiLaunch.Plugin.runtimeconfig.json y cada OmsiLaunch.*.dll del cierre administrado (debe incluir OmsiLaunch.Plugin.dll). En un paquete instalado es <package>\plugins.
Directorio o archivo faltante: PlanSessionAsync devuelve un plan no ejecutable con OL_E_RUNTIME_ARTIFACT_MISSING.
NativeBridgePath
Ruta de OmsiLaunch.Native.x86.dll (en el paquete: <package>\plugins\OmsiLaunch.Native.x86.dll).
Igual que en la fila anterior.
ReleaseManifestPath
release-manifest.json junto a OmsiLaunch.exe, cuando existe. Proporciona el SHA-256 esperado de cada archivo de plugins/ (plugin.integrity.reference = manifest).
null (estructura de desarrollo): de los archivos instalados solo se verifica su presencia y su coherencia interna con respecto al cierre de referencia (plugin.integrity.reference = self). Manifiesto mal formado: OL_E_RELEASE_MANIFEST_INVALID.
El servicio lee estas rutas en cada PlanSessionAsync y StartSessionAsync; nunca copia, prepara ni elimina archivos del plugin (consulte plugin permanente). La CLI construye el servicio exactamente así (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));
Cree un único servicio por proceso y compártalo. Estabilidad: STABLE_BETA.
StartSessionAsync adquiere el lease de la instalación (bloqueo exclusivo), un semáforo con nombre Local\OmsiLaunch.Installation.<SHA-256 of the upper-cased full root path> (SHA-256 de la ruta raíz completa en mayúsculas), y lo mantiene hasta que el supervisor haya restaurado la instalación. Un segundo inicio sobre la misma raíz desde cualquier proceso de la misma sesión de inicio de sesión de Windows falla con OL_E_INSTALLATION_BUSY (se informa como una sesión Failed, consulte StartSessionAsync). El lease es por sesión de inicio de sesión de Windows, no entre sesiones de inicio de sesión distintas, y no se libera mientras otro proceso tenga un handle abierto hacia él (riesgo aceptado).
Los handles son locales al proceso
SessionHandle encapsula el Guid de la sesión. Solo tiene sentido para la instancia de OmsiLaunchService que lo devolvió. Un handle construido a partir de un Guid conocido en otro proceso (o en otra instancia del servicio) produce KeyNotFoundException. El control entre procesos pasa por el plano de control local, no por handles.
Llame siempre a CloseAsync
A partir de StartSessionAsync, el proceso es dueño de una transacción duradera. CloseAsync solicita la detención canónica cuando hace falta, espera al supervisor (salida del proceso, restauración exacta, liberación del lease) y olvida la sesión. Debe llamarse en todas las rutas de salida, incluso después de un estado Failed. Sin esa llamada, la entrada de la sesión permanece en memoria; la restauración en sí la realiza el supervisor de todos modos.
Las sesiones fallidas siguen siendo sesiones
Un inicio que falla después de que StartSessionAsync retornó informa SessionState.Failed; el handle sigue siendo válido para GetStatusAsync/WaitForAsync hasta CloseAsync.
Los planes se vuelven a verificar
StartSessionAsync vuelve a calcular el hash de Omsi.exe y vuelve a planificar la especificación; un plan que ya no es ejecutable se rechaza con OL_E_PLAN_NOT_RUNNABLE.
Los handles desconocidos o ya cerrados lanzan KeyNotFoundException ("Unknown OmsiLaunch session.").
Ningún método requiere una sesión en estado Running, salvo ExecuteRuntimeAsync.
Las excepciones que llevan un código de OmsiLaunch colocan el código al comienzo de Exception.Message ("OL_E_PLAN_NOT_RUNNABLE: ..."). La CLI extrae los códigos de los mensajes de la misma forma (CliProgram.Classify).
Build compatible: solo Omsi23004_692EBFBF (más el hash de la lista de permitidos de Steam LAA, que se acepta; el gameplay no está validado, ya que requiere una instalación genuina de Steam). Consulte compatibilidad.
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
}
Compilar un LaunchSpec en un SessionPlan sin iniciar OMSI: validar la especificación, detectar la plataforma, obtener la huella de Omsi.exe, resolver las identidades de contenido, calcular las mutaciones de archivos planificadas, enumerar las capacidades requeridas y las no compatibles, y decidir IsRunnable. Capacidad pública session.plan.
Parámetros
spec: un LaunchSpec completamente poblado (consulte la referencia de LaunchSpec). Installation, World, Date, Time, Environment (los ocho diccionarios) y Behavior no deben ser null; los miembros opcionales pueden ser null. RootPath debe ser un directorio absoluto; una raíz vacía se registra como OL_E_INSTALLATION_NOT_FOUND, pero la sonda de plataforma sobre una ruta vacía lanza ArgumentException antes de que se devuelva el plan, por lo que nunca debe pasarse una raíz vacía.
Devuelve
SessionPlan con un SessionId nuevo, BuildProfileId = "Omsi23004_692EBFBF" (siempre esta constante, incluso cuando el archivo ejecutable no coincide), el Spec de entrada, Platform, ResolvedContent, TouchedFiles, RuntimeArtifacts (rutas de destino plugins\OmsiLaunch.* más "OmsiLaunch startup handoff v4"), RequiredCapabilities, UnsupportedRequestedFeatures, PlannedMutations, Diagnostics, IsRunnable. IsRunnable es true exactamente cuando ningún código de diagnóstico comienza con OL_E_. Los diagnósticos informativos (plugin.integrity.reference con el mensaje self o manifest, session_profile.selected) nunca vuelven no ejecutable un plan.
Errores incluidos en el resultado
Todo error de planificación es un diagnóstico, no una excepción: 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 (el mensaje lleva el código de splash/ITX), OL_E_PERMANENT_PLUGIN_MISSING, OL_E_PERMANENT_PLUGIN_HASH_MISMATCH, OL_E_PERMANENT_PLUGIN_MANIFEST_INCOMPLETE (el cierre del plugin instalado en plugins\ se verifica contra el manifiesto de la release durante la planificación), OL_E_RUNTIME_ARTIFACT_MISSING (el mensaje puede llevar OL_E_RELEASE_MANIFEST_INVALID). Condiciones completas: reglas de validación de LaunchSpec.
Excepciones
OperationCanceledException si el token ya está cancelado al entrar (el único punto de verificación); ArgumentException/NotSupportedException para rutas raíz sintácticamente no válidas; NullReferenceException/ArgumentNullException para miembros obligatorios null; System.Text.Json.JsonException para un manifiesto de release sintácticamente no válido.
Cancelación
Se verifica una vez al entrar. Después, la planificación es trabajo sincrónico sobre el sistema de archivos.
Requiere una sesión en ejecución
No.
Modifica el estado de OMSI
No.
Modifica el sistema de archivos
No (lee Omsi.exe, los archivos de contenido, el cierre del plugin y el manifiesto). Los valores de los ajustes no se validan aquí (solo la existencia de la clave y si se puede escribir); un valor no válido falla al iniciar con OL_E_INVALID_SETTING_VALUE.
Transacción / restauración
Ninguna.
Limitaciones
Solicitar cualquier modo de Date/Time/Year distinto de Unset, cualquier modo de Weather distinto de Unset, cualquier campo de PlayerVehicle, documentos de Input, EntrypointIdentity o WorldMode.LastMapState produce OL_E_CAPABILITY_UNAVAILABLE y un plan no ejecutable en este build (entradas STATICALLY_PARTIAL / UNSUPPORTED_FOR_CURRENT_PROFILE en UnsupportedRequestedFeatures).
Estabilidad
STABLE_BETA.
Ejemplo
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)));
Iniciar una sesión transaccional administrada de OMSI a partir de un plan ejecutable: adquirir el lease de la instalación, recuperar un journal (registro de la transacción) obsoleto, validar el cierre del plugin permanente, tomar el snapshot y aplicar los overlays de los archivos de la sesión, crear el handoff de arranque, el slot de telemetría y el buzón de runtime, iniciar Omsi.exe, registrar el proceso en el journal y entregar la sesión a un supervisor en segundo plano. Capacidad pública session.start.
Parámetros
plan: un SessionPlan con IsRunnable == true. La especificación contenida en el plan se vuelve a planificar; del plan del llamador solo se conserva plan.SessionId. plan.Spec.Behavior.StartupTimeoutSeconds debe estar en el rango 1..600.
Devuelve
SessionHandle(plan.SessionId) en cuanto Omsi.exe se creó y quedó registrado (estado WaitingForPlugin), o en cuanto la ruta de inicio falló (estado Failed). No espera al gameplay; use WaitForAsync(session, SessionState.Running, ...).
Excepciones
InvalidOperationException("OL_E_PLAN_NOT_RUNNABLE") cuando plan.IsRunnable es false; InvalidOperationException("OL_E_PLAN_NOT_RUNNABLE: <codes>") cuando el plan recalculado no es ejecutable (por ejemplo, Omsi.exe cambió, se eliminó contenido o falta el cierre del plugin); InvalidOperationException("Duplicate session id.") cuando todavía hay registrada una sesión con el mismo id (llame primero a CloseAsync); ArgumentOutOfRangeException cuando StartupTimeoutSeconds está fuera del rango 1..600; OperationCanceledException cuando se cancela antes o durante la nueva planificación; además de todo lo que lanza PlanSessionAsync. En todos los casos con excepción, no se registra ninguna sesión.
Errores incluidos en el resultado
Cualquier fallo posterior a la nueva planificación se captura dentro de la ruta de inicio: la sesión se registra, su estado es Failed y sus diagnósticos contienen OL_E_START_SESSION, cuyo mensaje es el mensaje interno (que comienza con el código interno cuando lo hay): OL_E_INSTALLATION_BUSY (lease ocupado o un proceso de OMSI registrado en el journal que sigue vivo), 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 (solo cuando el reintento diferido con los overlays de esta sesión sigue sin poder demostrar la propiedad), OL_E_RECOVERY_JOURNAL_REMOVE_FAILED, OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED. La limpieza puede agregar OL_E_PROCESS_CLEANUP_FAILED, OL_E_RESTORE_DEFERRED (salida de OMSI no confirmada; el journal se conserva) u OL_E_RESTORE_FAILED. Los fallos posteriores los informa el supervisor (consulte ciclo de vida de la sesión).
Cancelación
Antes o durante la nueva planificación: lanza una excepción. Después, el token se pasa a la transacción y a la creación del proceso; una cancelación en ese punto se trata como cualquier fallo de inicio (Failed + OL_E_START_SESSION: The operation was canceled.), el proceso (si se creó) se finaliza y la instalación se restaura.
Requiere una sesión en ejecución
No.
Modifica el estado de OMSI
Sí: crea el proceso de OMSI con las variables de entorno OMSILAUNCH_SESSION_ID, OMSILAUNCH_HANDOFF_NAME, OMSILAUNCH_TELEMETRY_NAME, OMSILAUNCH_RUNTIME_CHANNEL, OMSILAUNCH_INTERNET_TEXTURES_MODE.
Modifica el sistema de archivos
Sí, dentro de la raíz de la instalación: .omsilaunch\diagnostics\<sessionId>-host.log (retención: las 50 sesiones más recientes), .omsilaunch\journal.json, .omsilaunch\backup\<sessionId>\*.bin, .omsilaunch\assets\splash\*.bmp (se copian una vez para la pantalla de presentación (splash) administrada), overlays de la sesión (parches de options.cfg, GUI\NewSplashscreen_*.bmp, Texture\standard.itx), eliminaciones de la sesión (destinos ITX, Texture\standard.ipr, closecheck) y la eliminación permanente de un closecheck obsoleto preexistente cuando SuppressStaleClosecheckWarning es true (diagnóstico closecheck.stale-removed).
Transacción / restauración
Abre la transacción (Prepared → Applied → RuntimeDeployed → HandoffCreated → ProcessStarted). Toda ruta de salida de la sesión termina en una restauración. Consulte transacciones y recuperación.
Limitaciones
Solo WorldMode.NewMap con PresentedEntrypointIndex y WorldMode.SavedSituation llegan al gameplay. WorldMode.LastMapState es UNAVAILABLE. Las solicitudes de fecha/hora/clima/vehículo del jugador/entrada nunca llegan a este método, porque son no ejecutables en el momento de la planificación.
Estabilidad
STABLE_BETA (los ciclos de vida NEW_MAP y SAVED_SITUATION están validados en runtime).
Ejemplo
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);
Leer el estado semántico del ciclo de vida, los diagnósticos recopilados hasta el momento y la lista acotada de eventos de runtime. Capacidad pública session.status.
Parámetros
session: un handle devuelto por StartSessionAsync que todavía no se cerró.
Devuelve
SessionStatus(SessionId, State, Diagnostics, RuntimeEvents): un snapshot inmutable (los arreglos se copian bajo el bloqueo de la sesión). RuntimeEvents nunca es null para una sesión viva.
Excepciones
KeyNotFoundException para handles desconocidos o cerrados. En cualquier otro caso nunca lanza excepciones.
Cancelación
El token se ignora (la llamada se completa de forma sincrónica).
Requiere una sesión en ejecución
No.
Modifica OMSI / sistema de archivos / transacción
No / No / Ninguna.
Estabilidad
STABLE_BETA.
Ejemplo
var status = await launch.GetStatusAsync(session); Console.WriteLine($"{status.State} events={status.RuntimeEvents!.Count}");
Consultar periódicamente (cada 100 ms) hasta que la sesión esté en state o en un estado terminal (Completed, Failed), o hasta que transcurra el timeout; luego devolver el estado actual.
Parámetros
state: cualquier SessionState. Esperar un estado transitorio que ya pasó (o que nunca se establece, consulte ciclo de vida de la sesión) espera hasta un estado terminal o hasta el timeout. timeout: cualquier TimeSpan no negativo o Timeout.InfiniteTimeSpan.
Devuelve
El estado en el momento en que terminó la espera. Al vencer el timeout se devuelve el estado, no una excepción: verifique State usted mismo. Una espera de Running que termina en Failed retorna de inmediato con los diagnósticos del fallo.
Excepciones
KeyNotFoundException; OperationCanceledException cuando se cancela el token del llamador (solo se propaga la cancelación del llamador; el timeout interno no).
Cancelación
El token del llamador se respeta en cada intervalo de 100 ms.
Requiere una sesión en ejecución
No.
Modifica OMSI / sistema de archivos / transacción
No / No / Ninguna.
Estabilidad
STABLE_BETA.
Ejemplo
var running = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(185)); if (running.State != SessionState.Running) { /* timed out or Failed */ }
Solicitar la detención canónica. Establece el flag de detención y retorna de inmediato; el supervisor detecta el flag dentro de su ciclo de 100 ms, llama a TerminateProcess sobre Omsi.exe, espera la salida, marca el journal como ProcessExited, restaura cada archivo propiedad de la sesión y libera el lease. Se trata de una finalización forzada: la rutina de cierre propia de OMSI no se ejecuta y OMSI no reescribe options.cfg al salir (deliberado; protege la transacción). El cierre cooperativo mediante WM_CLOSE no está implementado (decisión de producto; en el cierre de validación en runtime, OMSI no se cerró dentro de los 30 s posteriores a WM_CLOSE, L05b). Capacidad pública session.stop.
Parámetros
session.
Devuelve
Una tarea completada; no espera la finalización ni la restauración. Use WaitForAsync(session, SessionState.Completed, ...) para observar la finalización.
Excepciones
KeyNotFoundException.
Cancelación
El token se ignora.
Requiere una sesión en ejecución
No. Es idempotente; una detención solicitada antes de que arranque el supervisor se respeta en cuanto este arranca; una detención sobre una sesión terminal no tiene efecto.
Modifica el estado de OMSI
Sí: finaliza el proceso de OMSI (código de salida 1).
Modifica el sistema de archivos
Indirectamente: desencadena la restauración, la eliminación del journal y la eliminación de las copias de seguridad por parte del supervisor.
Transacción / restauración
Desencadena ProcessExited → Restoring → Restored. Los cambios del lado del runtime realizados mediante ExecuteRuntimeAsync (escrituras del reloj, vehículos generados, variables de script, texturas D3D) no se restauran; desaparecen con el proceso.
Estabilidad
STABLE_BETA.
Ejemplo
await launch.StopAsync(session); var done = await launch.WaitForAsync(session, SessionState.Completed, TimeSpan.FromMinutes(1));
Liberar el handle del consumidor sin dejar abandonada la transacción: si la sesión no es terminal, solicitar la detención canónica; luego esperar la tarea de ciclo de vida del supervisor (salida del proceso, restauración, liberación del lease); por último, olvidar la sesión.
Parámetros
session.
Devuelve
Se completa cuando la sesión es terminal y se eliminó. Después de que retorna, el handle es desconocido (KeyNotFoundException en cualquier llamada posterior, incluida una segunda llamada a CloseAsync).
Excepciones
KeyNotFoundException; OperationCanceledException si el llamador cancela mientras espera al supervisor. En ese caso la sesión no se elimina y el supervisor sigue ejecutándose; llame de nuevo a CloseAsync.
Cancelación
Se aplica solo a la espera; nunca cancela la restauración.
Requiere una sesión en ejecución
No.
Modifica el estado de OMSI
Sí, cuando la sesión sigue viva (igual que StopAsync).
Modifica el sistema de archivos
Indirectamente (restauración por parte del supervisor).
Transacción / restauración
Garantiza que la transacción se lleve hasta el final antes de liberar el handle (cuando el supervisor llegó a iniciarse). Para una sesión que falló antes de que se iniciara el supervisor, la ruta de inicio ya restauró o informó OL_E_RESTORE_DEFERRED.
Ejecutar una operación pública de runtime dentro del proceso de OMSI en ejecución a través del buzón de la sesión de una sola solicitud en curso (mapeado en memoria, 64 KiB, solicitud vinculada al id de sesión y al id de solicitud). El plugin ejecuta la operación en el thread de la interfaz de OMSI. Catálogo de operaciones: control de runtime y capacidades.
Parámetros
command.SessionId debe ser igual a session.SessionId. command.RequestId: un ulong elegido por el llamador; use un contador estrictamente creciente por proceso (los helpers de D3D comienzan en 30 000, el propietario de la CLI en 10 001/50 000). command.Operation: un id de operación pública de PublicCapabilityRegistry.PublicRuntimeOperationIds (por ejemplo, time.read, road-vehicle.read, d3d.texture.create). command.Arguments: valores de cadena indexados por nombres ordinales; los nombres obligatorios de cada operación provienen de PublicCapabilityRegistry.GetRuntimeArguments. timeout: se mide desde el momento en que la solicitud se deposita en el buzón (la espera en cola detrás de otro comando en curso no se cuenta). La CLI usa 5 s (15 s para road-vehicles.spawn) como propietario y 8 s / 30 s como cliente.
Orden de las verificaciones
1. Validación del registro (antes de buscar la sesión): operación desconocida o internal.* → resultado Succeeded=false, ErrorCode=OL_E_RUNTIME_OPERATION_UNKNOWN; argumento obligatorio faltante (ausente o solo con espacios en blanco) → OL_E_RUNTIME_ARGUMENT_REQUIRED. 2. Búsqueda de la sesión → KeyNotFoundException. 3. command.SessionId != session.SessionId → InvalidOperationException("OL_E_RUNTIME_SESSION_MISMATCH"). 4. Estado distinto de Running → InvalidOperationException("OL_E_SESSION_NOT_RUNNING"). 5. Solicitud al buzón. 6. Se eliminan los valores del resultado cuya clave comienza con internal_ o termina en _address, _pointer, _vmt.
Devuelve
RuntimeCommandResult(SessionId, RequestId, Succeeded, ErrorCode, Values). En caso de éxito, Values contiene las cadenas semánticas de la operación (documentadas para cada operación en control de runtime).
Errores incluidos en el resultado
OL_E_RUNTIME_OPERATION_UNKNOWN, OL_E_RUNTIME_ARGUMENT_REQUIRED (registro); OL_E_RUNTIME_RESPONSE_TOO_LARGE (el resultado del plugin excedió el buzón; los resultados de listas acotadas, en cambio, se acortan con truncated=true); OL_E_RUNTIME_SETTING_NOT_PERSISTENT (weather.set, siempre); cada código OL_E_D3D_* (con Values["detail"] y Values["native_status"]); y OL_E_RUNTIME_OPERATION_FAILED para cualquier otro fallo del lado del plugin. En este último caso, el código específico no está en ErrorCode: es el primer token de Values["detail"] (por ejemplo, detail = "OL_E_RUNTIME_OBJECT_HANDLE_STALE", exception = "InvalidOperationException"). Códigos que llegan de esta forma: OL_E_RUNTIME_OPERATION_UNAVAILABLE, OL_E_RUNTIME_ARGUMENT_REQUIRED (verificaciones del lado del plugin), OL_E_RUNTIME_VALUE_OUT_OF_RANGE, OL_E_RUNTIME_VALUE_INVALID, OL_E_RUNTIME_OBJECT_HANDLE_REQUIRED, OL_E_RUNTIME_OBJECT_HANDLE_STALE, OL_E_RUNTIME_SCRIPT_OBJECT_UNAVAILABLE, OL_E_RUNTIME_VARIABLE_NOT_FOUND, OL_E_RUNTIME_VARIABLE_UNAVAILABLE, OL_E_RUNTIME_STRING_VARIABLE_NOT_FOUND, OL_E_RUNTIME_CONSTANT_NOT_FOUND, OL_E_RUNTIME_CONSTANTS_UNAVAILABLE, OL_E_RUNTIME_CURVE_NOT_FOUND, OL_E_RUNTIME_CURVE_EMPTY, OL_E_RUNTIME_CURVE_DEGENERATE, OL_E_RUNTIME_CURVE_INVALID, OL_E_RUNTIME_HOF_UNAVAILABLE, OL_E_RUNTIME_PLAYER_VEHICLE_UNAVAILABLE, OL_E_CAMERA_PRESET_FAMILY_UNSUPPORTED, OL_E_TIME_APPLY_FAILED, OL_E_RUNTIME_BUS_IDENTITY_INVALID, OL_E_MAKEVEHICLE_BUS_NOT_FOUND, OL_E_MAKEVEHICLE_DELTA_ZERO, OL_E_MAKEVEHICLE_DELTA_MULTIPLE, OL_E_MAKEVEHICLE_NATIVE_FAILED, OL_E_RUNTIME_CREATED_OBJECT_NOT_IN_COLLECTION, OL_E_RUNTIME_CREATED_OBJECT_INVALID, OL_E_PLACE_RANDOM_BUS_FAILED, OL_E_RUNTIME_SETTING_UNAVAILABLE. Consulte códigos de error.
Excepciones
KeyNotFoundException; InvalidOperationException con OL_E_RUNTIME_SESSION_MISMATCH, OL_E_SESSION_NOT_RUNNING, OL_E_RUNTIME_CHANNEL_CLOSED (el supervisor ya liberó el buzón), OL_E_RUNTIME_CHANNEL_BUSY (el slot todavía contiene una solicitud abandonada), OL_E_RUNTIME_REQUEST_ID_REUSED (todavía hay en el slot una respuesta obsoleta para el mismo id de solicitud); TimeoutException("OL_E_RUNTIME_REQUEST_TIMEOUT"); InvalidDataException("OL_E_RUNTIME_RESPONSE_INVALID") (respuesta dañada, ajena o que no coincide); ArgumentOutOfRangeException cuando la solicitud serializada excede el buzón; OperationCanceledException.
Cancelación
Se respeta mientras se espera el gate por sesión y cada 20 ms mientras se consulta la respuesta. Cancelar con la solicitud en curso no restablece el slot: la siguiente llamada sobre esa sesión puede fallar con OL_E_RUNTIME_CHANNEL_BUSY hasta que el plugin publique su respuesta (que entonces se descarta por obsoleta). Es preferible usar el timeout; un timeout restablece el slot y una respuesta tardía se detecta y se descarta.
Requiere una sesión en ejecución
Sí (SessionState.Running); de lo contrario se lanza OL_E_SESSION_NOT_RUNNING. El buzón existe hasta que el supervisor lo libera durante la restauración.
Modifica el estado de OMSI
Depende de la operación: las operaciones Read no lo modifican; las operaciones Write/Action (time.set, camera.set, camera.lock, camera.unlock, road-vehicles.spawn, road-vehicles.place-random, vehicle.variable.set, d3d.texture.*) modifican estado dentro del proceso que no se restaura.
Modifica el sistema de archivos
El host no escribe nada. OMSI puede escribir sus propios archivos como consecuencia (sin seguimiento).
Transacción / restauración
Ninguna.
Limitaciones
Un solo comando en curso por sesión (las llamadas sobre la misma sesión se serializan). La solicitud y la respuesta están limitadas cada una a 64 KiB menos 8 bytes; las cargas de píxeles D3D, a 48 KiB. internal.road-vehicles.make-basic es INTERNAL e inaccesible. weather.set es UNAVAILABLE. timetable.logs.read no es una lista acotada y puede devolver OL_E_RUNTIME_RESPONSE_TOO_LARGE con horarios grandes. camera.lock es EXPERIMENTAL; requiere un vehículo del jugador y está validado en runtime (CAM01), aunque la cadena RuntimeValidation del registro todavía indica STATICALLY_VALIDATED. Los handles (rv-NNNNNN, hb-NNNNNN, d3dtex-<session>-<hex>) tienen alcance de sesión.
Estabilidad
Transporte y contrato: STABLE_BETA; la estabilidad de cada operación sigue a PublicCapabilityRegistry (PublicStableBeta → STABLE_BETA, PublicExperimental → EXPERIMENTAL), con las excepciones indicadas arriba.
Ejemplo
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"]}");
Devolver el inventario de evidencias del producto para una instalación: una lista fija de entradas Capability(Name, Available, EvidenceState, Reason) mantenida en OmsiLaunchService. Solo runtime.current-windows-x64 se calcula (a partir de la detección de la plataforma); todas las demás entradas son constantes.
Parámetros
installation.RootPath: directorio usado para la sonda de plataforma (para que se considere escribible, el directorio debe existir, no ser de solo lectura y contener plugins\). ExpectedExecutableSha256 se ignora.
PublicCapabilityRegistry.All es el catálogo de la superficie de control en tiempo de compilación (36 descriptores con clasificación, tipo, rutas de API y de CLI y argumentos obligatorios) que la API y la CLI hacen cumplir; no depende de la instalación. GetCapabilitiesAsync es un informe de evidencia de runtime (estado de validación y motivos). Use el registro para decidir qué puede llamar; use esta lista para decidir qué se ha demostrado. Ninguna de las dos listas se deriva de la otra.
Excepciones
OperationCanceledException al entrar; ArgumentException para una ruta raíz vacía.
Cancelación
Se verifica una vez al entrar.
Requiere una sesión en ejecución
No.
Modifica OMSI / sistema de archivos / transacción
No / No / Ninguna.
Estabilidad
Contrato de la llamada: STABLE_BETA; el contenido de la lista es un inventario mantenido a mano: PARTIAL.
Ejemplo
foreach (var c in await launch.GetCapabilitiesAsync(new InstallationSpec(root))) Console.WriteLine($"{c.Name} {c.Available} {c.EvidenceState} {c.Reason}");
Enumerar el contenido instalado y devolver identidades canónicas que se pueden usar en un LaunchSpec. El descubrimiento omite los puntos de reanálisis (reparse points), de modo que los ciclos de junctions no pueden bloquearlo; lee los archivos de OMSI como Windows-1252 (respeta UTF-8/UTF-16 marcados con BOM) y nunca sigue vínculos simbólicos.
Parámetros
query y scope según la tabla siguiente. scope es obligatorio para Entrypoints (identidad del mapa), Repaints, FleetNumbers, Registrations (identidad del vehículo).
Devuelve
Lista ordenada de ContentIdentity(Identity, Kind, DisplayName). Las identidades son rutas relativas a la instalación con barras invertidas; las comparaciones no distinguen mayúsculas de minúsculas.
Excepciones
OperationCanceledException al entrar; ArgumentException cuando se consulta Entrypoints sin un alcance o la raíz está vacía; FileNotFoundException (sin código OL_E_; la CLI lo asigna a OL_E_NOT_FOUND) cuando el mapa o el vehículo del alcance no está instalado. Una raíz o un directorio de contenido faltantes producen una lista vacía, no un error.
Cancelación
Se verifica una vez al entrar.
Requiere una sesión en ejecución
No.
Modifica OMSI / sistema de archivos / transacción
No / No / Ninguna.
Estabilidad
Maps, Situations, Vehicles: STABLE_BETA (todos los planes validados en runtime se resuelven a través de ellos). Entrypoints, Repaints, Hofs, FleetNumbers, Registrations, Addons: EXPERIMENTAL (solo evidencia estática).
Ejemplo
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));
Valores de ContentQueryKind y resultados:
Valor
Alcance
Identity
Kind
DisplayName
Maps
ninguno
maps\<dir>\global.cfg
map
nombre del directorio del mapa
Situations
ninguno
situations\...\<file>.osn
situation
identidad del mapa referenciada por el .osn (puede ser null)
Vehicles
ninguno
Vehicles\...\<file>.bus
vehicle
[friendlyname] o nombre del archivo
Repaints
identidad del vehículo (obligatoria; sin ella: lista vacía)
<cti path>#item:<ordinal>
repaint
nombre de [item]
Hofs
ninguno
Vehicles\...\<file>.hof
hof
null
FleetNumbers
identidad del vehículo (obligatoria; sin ella: lista vacía)
ruta de origen de [number] relativa al vehículo
fleet-number
null
Registrations
identidad del vehículo (obligatoria; sin ella: lista vacía)
identidad del mapa (obligatoria; sin ella: ArgumentException)
<map identity>#entrypoint:<SHA-256 of the 12-line record>
entrypoint
etiqueta del punto de entrada
Las identidades de punto de entrada son solo para el descubrimiento: la ruta de inicio usa PresentedEntrypointIndex; pasar un EntrypointIdentity vuelve no ejecutable el plan en este build (world.entrypoint-identity, RUNTIME_PARTIAL).
Informar o completar una transacción duradera obsoleta (<root>\.omsilaunch\journal.json) que dejó un propietario que se bloqueó. Toma el lease de la instalación durante toda la llamada, para no restaurar nunca por debajo de una sesión que se está iniciando. Capacidad pública session.recover; en la CLI, /recovery-status y /recover.
Parámetros
installation.RootPath: la raíz de la instalación (normalizada con Path.GetFullPath). restore: false = solo informar; true = restaurar, verificar y eliminar el journal y las copias de seguridad.
Devuelve
RecoveryStatus(Pending, Recovered, Diagnostics): Pending = existía un journal cuando comenzó la llamada; Recovered = se solicitó una restauración, se ejecutó y no queda ningún journal; Diagnostics = notas de la restauración (restore.session-artifact-removed con Data["sha256"], OL_W_RESTORE_FOREIGN_FILE_RETAINED), vacío cuando no se restauró nada.
Excepciones
InvalidOperationException("OL_E_INSTALLATION_BUSY: another OmsiLaunch owner holds this installation.") cuando el lease está ocupado; IOException("OL_E_INSTALLATION_BUSY: a journaled OMSI process is still alive.") cuando el PID + la hora de creación + la ruta del archivo ejecutable del journal siguen coincidiendo con un proceso vivo o, si el journal está más allá de HandoffCreated sin PID, cuando hay algún Omsi.exe de esa raíz en ejecución; IOException con OL_E_RECOVERY_BACKUP_CORRUPT, OL_E_RECOVERY_ABSENT_OWNERSHIP_MISMATCH, OL_E_RECOVERY_ABSENT_OWNERSHIP_UNVERIFIED, OL_E_RECOVERY_JOURNAL_REMOVE_FAILED o un mensaje de verificación ("Restore hash mismatch: ...", "Restore presence mismatch: ..."); InvalidDataException("Invalid OmsiLaunch journal.") / JsonException para un journal dañado; ArgumentException para una raíz vacía; OperationCanceledException. Siempre que lanza una excepción después de iniciada una restauración, el journal se conserva y la siguiente llamada la vuelve a ejecutar de forma idempotente.
Cancelación
Se pasa a las escrituras del journal y de las copias de seguridad; cancelar a mitad de la restauración deja el journal pendiente.
Requiere una sesión en ejecución
No (se niega a actuar mientras haya un propietario activo).
Modifica el estado de OMSI
No.
Modifica el sistema de archivos
Solo con restore == true: reescribe los originales a partir de copias de seguridad verificadas (bytes, hora de última escritura, hora de creación, atributos; se manejan los originales de solo lectura; escritura directa + vaciado del búfer; no quedan archivos *.omsilaunch.tmp), elimina los artefactos de la sesión y borra journal.json y backup\<sessionId>.
Transacción / restauración
Completa la transacción pendiente (Restoring → Restored → journal eliminado).
Estabilidad
STABLE_BETA: ruta de informe y recuperación tras una salida anticipada (matriz RV-008), recuperación después de una restauración fallida (cierre de validación en runtime F01), rechazo con un propietario vivo y en la ventana previa al PID (S04) y recuperación diferida previa a la huella al iniciar (S05); consulte estado de la validación en runtime.
Ejemplo
var r = await launch.RecoverPendingAsync(new InstallationSpec(root), restore: true); Console.WriteLine($"pending={r.Pending} recovered={r.Recovered}");
Todos los records siguientes están documentados propiedad por propiedad en la referencia de LaunchSpec; esta tabla fija el inventario de tipos.
Tipo
Propósito
Estabilidad
LaunchSpec
Record raíz de la solicitud, con los descriptores de acceso EffectiveYear, EffectiveWeather, EffectiveInput, EffectiveDiagnostics, EffectivePresentation, EffectiveInternetTextures, que sustituyen por valores predeterminados los miembros opcionales null.
STABLE_BETA
InstallationSpec
RootPath, ExpectedExecutableSha256 (se transporta, no se consume).
Selección del mundo. WorldMode: NewMap = 0, SavedSituation = 1, LastMapState = 2, LastSituation = 2 (alias obsoleto de LastMapState; nunca significa "el .osn más reciente"). EntrypointMode: Unset, PresentedIndex, Identity (se calcula a partir de WorldSpec.Entrypoint).
SessionId (un Guid nuevo por plan), BuildProfileId ("Omsi23004_692EBFBF"), Spec, Platform (RuntimePlatformInfo), ResolvedContent (lista de ContentIdentity: map, vehicle, repaint, hof, situation, situation-map), TouchedFiles (rutas relativas distintas de PlannedMutations), RuntimeArtifacts, RequiredCapabilities (Capability con STATICALLY_VALIDATED o UNAVAILABLE), UnsupportedRequestedFeatures (entradas Capability para funciones solicitadas pero no compatibles), PlannedMutations, Diagnostics, IsRunnable.
Es un record público: puede editarse o quedar obsoleto, y por eso StartSessionAsync vuelve a planificar.
RuntimePlatformInfo
OsFamily, OsVersion, OsArchitecture, HostArchitecture, OmsiArchitecture (X86), PluginArchitecture (X86), CurrentPlatformSupported (Windows 10 o posterior, sistema operativo x64 y host x64), LegacyPlatform (siempre false), Wow64Available, InstallationWritable, ProcessLaunchSupported, PluginRuntimeSupported, NativeInteropSupported, SharedMemorySupported, ExactRestoreSupported (todos iguales a CurrentPlatformSupported).
Capability
Name, Available, EvidenceState, Reason.
Las cadenas de evidencia son texto libre (RUNTIME_VALIDATED, STATICALLY_VALIDATED, STATICALLY_PARTIAL, RUNTIME_PARTIAL, UNAVAILABLE, UNSUPPORTED_FOR_CURRENT_PROFILE, IMPLEMENTED_NOT_RUNTIME_VALIDATED, RELEASE_IF_CLOSED).
Las mutaciones de presentación usan las claves session-presentation.splash, internet-textures.override, internet-textures.cache, internet-textures.target.
LaunchDiagnostic
Code, Message, Data (mapa de cadenas opcional).
Los códigos que comienzan con OL_E_ son errores; los que comienzan con OL_W_, advertencias; cualquier otro es informativo.
SessionStatus
SessionId, State (SessionState), Diagnostics, RuntimeEvents.
Los diagnósticos de la sesión no incluyen los diagnósticos del plan.
RuntimeEvent
Type, TimestampUtc (hora de recepción en el host), Sequence (basado en 1, por sesión), Data.
Limitado a los 256 eventos más recientes (se descartan los más antiguos). El slot de telemetría es un slot de último valor: los eventos emitidos más rápido que el sondeo de 100 ms del host pueden perderse. No es un registro sin pérdidas.
Enum de tipo byte en orden de declaración: Created, ValidatingPlatform, Planning, AcquiringInstallationLock, RecoveringPreviousTransaction, Snapshotting, ApplyingConfiguration, DeployingRuntime, CreatingStartupHandoff, StartingProcess, WaitingForPlugin, PluginBootstrap, StartingWorld, EnteringGameplay, Running, ProcessExited, Restoring, CleaningRuntime, Completed, Failed. El servicio actual nunca establece ValidatingPlatform, Planning ni EnteringGameplay; Snapshotting es transitorio y en la práctica no se puede observar. Estados terminales: Completed, Failed. Semántica completa: ciclo de vida de la sesión.
Códec estático que usan el host y el plugin para el envoltorio del buzón: valor mágico 0x4F4C5243 ("OLRC"), versión 1, encabezado little-endian de 72 bytes (valor mágico, versión, tipo 1 = solicitud / 2 = respuesta, longitud total, Guid de la sesión, id de solicitud, longitud de la carga útil, SHA-256 de la carga útil) seguido de una carga útil JSON en UTF-8. SerializeRequest, SerializeResponse, TryDeserializeRequest, TryDeserializeResponse, TryReadRequestId.
INTERNAL: es público porque ambos extremos del puente lo comparten; no es una superficie de integración; el formato puede cambiar con la versión del protocolo.
StartupHandoff
(Guid SessionId, string BuildProfileId, WorldMode WorldMode, string MapIdentity, int PresentedEntrypointIndex, bool HeadlessStart, bool PlayerVehicleEnabled, DateTimeMode DateMode, DateTimeMode TimeMode, string EntrypointIdentity, string SituationIdentity): lo que el host publica para el plugin en el archivo mapeado en memoria OmsiLaunch.Handoff.<sessionId>.
INTERNAL
StartupHandoffWire
Códec: valor mágico 0x4F4C5348, versión 4 (lee 3 y 4), encabezado de 64 bytes con integridad de la carga útil mediante SHA-256.
INTERNAL
El plugin rechaza un handoff (plugin.request.unsupported → OL_E_CAPABILITY_UNAVAILABLE) salvo que WorldMode sea NewMap o SavedSituation, HeadlessStart sea true, PlayerVehicleEnabled sea false, ambos modos de fecha/hora sean Unset y, en el caso de una situación guardada, esta tenga una identidad no vacía. El planificador aplica las mismas restricciones antes, por lo que un plan ejecutable nunca provoca este rechazo.
ProtocolVersion ("0.1"), All (36 entradas PublicCapabilityDescriptor), PublicRuntimeOperationIds (los 48 ids de operación concretos que un frontend puede reenviar), IsPublicRuntimeOperation, GetRuntimeArguments, ValidateRuntimeArguments (devuelve PublicRuntimeArgumentValidation), IsInternalResultKey. Lo hacen cumplir ExecuteRuntimeAsync, la CLI y el plano de control local.
Wrappers tipados sobre ExecuteRuntimeAsync para las operaciones d3d.* (EXPERIMENTAL, capacidad PublicExperimentald3d.texture). Asignan los ids de solicitud a partir de un contador global del proceso que comienza en 30 000 y usan un timeout predeterminado de 5 s (excepto GetD3DStatusAsync, que requiere uno explícito).
Errores: un resultado fallido se vuelve a lanzar como OmsiRuntimeException(Code, detail), donde Code es el ErrorCode del resultado (o OL_E_RUNTIME_OPERATION_FAILED cuando falta) y el mensaje es "<code>: <Values["detail"]>"; un resultado exitoso sin valores, o una cadena de estado del dispositivo desconocida, lanza OmsiRuntimeException("OL_E_RUNTIME_PROTOCOL_MISMATCH", ...). Todo lo que lanza ExecuteRuntimeAsync se propaga sin cambios. El manejo del reinicio del dispositivo (D3D) está validado en runtime: un reinicio hace pasar el dispositivo por Resetting y lo devuelve a Ready, e invalida todas las texturas vivas (OL_E_D3D_STALE_RESOURCE_HANDLE, cierre de validación en runtime D01); GetCapabilitiesAsync todavía informa runtime.d3d.lifecycle.reset como IMPLEMENTED_NOT_RUNTIME_VALIDATED (desfase del autoinforme). La transición a Lost no puede producirse desde fuera del producto y solo está cubierta offline.
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. Solo lo usa la CLI (códigos de salida); la API nunca finaliza el proceso.
PublicErrorCategory
Constantes de cadena usadas en los envoltorios de error de la CLI y del control local: invalid_argument, unsupported_profile, session, runtime, not_found, transaction, internal.
PublicErrorCodes
Una const string por código (143: 142 errores OL_E_ y 1 advertencia OL_W_) y All, el catálogo de PublicErrorDescriptor(Code, Category). Categorías: Cli, Compatibility, Content, Installation, InvalidArgument, LaunchSpec, LocalControl, Other, Presentation, Process, Runtime, RuntimeD3D, Session, SessionProfile, Transaction, Warning. Referencia: códigos de error.
PublicErrorDescriptor
(string Code, string Category).
OmsiRuntimeException
Propiedad Code más el mensaje; solo la lanza D3DRuntimeApi.
InstallationPaths (identidad de la instalación y contención de rutas)#
Estabilidad: STABLE_BETA (funciones puras, sin E/S, sin estado de OMSI, sin modificaciones del sistema de archivos, sin participación en transacciones, sin requerir una sesión en estado Running). Es la única definición que usan el lease de la instalación, el nombre del pipe de control local, el confinamiento de los assets de los perfiles de sesión, la validación de los destinos de Internet Textures y la ruta del modelo de generación (spawn) en runtime.
Miembro
Comportamiento
string NormalizeRoot(string root)
Path.GetFullPath(root) sin separadores finales, excepto en la raíz de una unidad (C:\), que se conserva. Resuelve los segmentos . y .., trata / y \ de la misma forma y colapsa los separadores repetidos. No resuelve junctions ni vínculos simbólicos. Lanza ArgumentException para una raíz null o en blanco.
string IdentityKey(string root)
NormalizeRoot(root) en mayúsculas. Las grafías léxicamente equivalentes de una misma raíz (C:\OMSI, C:\OMSI\, C:\OMSI\., C:\foo\..\OMSI, c:\omsi) comparten una misma clave; raíces distintas (C:\OMSI-A, C:\OMSI-B) nunca la comparten.
bool TryGetContainedRelativePath(string root, string candidate, out string relativePath)
Resuelve candidate (relativo a root, o absoluto) y devuelve true solo cuando se encuentra estrictamente por debajo de root; relativePath es la grafía canónica con \. Usa los segmentos de Path.GetRelativePath, de modo que un directorio hermano como C:\OMSI-A\x nunca está dentro de C:\OMSI; la propia raíz, otros volúmenes y los escapes mediante .. devuelven false.
Divide por / y \, descartando los segmentos vacíos.
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"
Estabilidad: EXPERIMENTAL. Estos tipos compilan un perfil de sesión en YAML (<root>\.omsilaunch\session-profiles\<id>\profile.yaml, esquema omsilaunch.session-profile/v1) en un LaunchSpec. La CLI /predefined-profile:<id> /predefined-profile-index:<n> usa exactamente estas llamadas; un integrador puede usarlas para iniciar un perfil mediante la API.
Miembro
Comportamiento
SessionProfileCompiler.Load(string installationRoot, string id, int presetIndex) → SessionProfilePackage
Lee y valida el paquete. id debe ser un nombre de directorio simple (de lo contrario, OL_E_SESSION_PROFILE_PATH_ESCAPE); presetIndex está en el rango 1..5 (OL_E_SESSION_PROFILE_PRESET_NOT_FOUND). Archivo faltante: OL_E_SESSION_PROFILE_NOT_FOUND; tamaño mayor que MaxBytes (256 KiB), YAML no válido, anclas, claves desconocidas o un id distinto del nombre del directorio: OL_E_SESSION_PROFILE_INVALID; otro schema: OL_E_SESSION_PROFILE_SCHEMA_UNSUPPORTED. Las rutas de los assets quedan confinadas al paquete (OL_E_SESSION_PROFILE_PATH_ESCAPE, OL_E_SESSION_PROFILE_ASSET_MISSING). Todo fallo es una SessionProfileException.
Devuelve baseline con el perfil aplicado: para NewMap, el bloque new del perfil (mapa, punto de entrada y cualquier fecha/hora/año/clima, lo que vuelve no ejecutable el plan en este build); los settings del preset combinados sobre Environment.General; Presentation, InternetTextures y Behavior del preset cuando están presentes; y SessionProfile = profile.Metadata. Para NewMap también verifica el mapa contra la lista compatibility del perfil (OL_E_SESSION_PROFILE_MAP_MISMATCH).
La verificación de compatibilidad para los demás modos (para SavedSituation, el mapa se lee del .osn). La CLI la llama después de construir el WorldSpec final.
IOException con Code (uno de los códigos OL_E_SESSION_PROFILE_*); el mensaje es "<code>: <message>".
La CLI además rechaza los flags de línea de comandos que entran en conflicto con el perfil (OL_E_SESSION_PROFILE_OVERRIDE_CONFLICT); esa verificación no forma parte del compilador. Consulte perfiles de sesión para ver el orden de combinación completo.
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 como tipo del parámetro del constructor de OmsiLaunchService
Detecta la plataforma, verifica si se puede escribir, inicia, observa, finaliza y espera a Omsi.exe. Pase new CurrentWindowsX64Platform(); implementarlo por cuenta propia no está soportado.
CurrentWindowsX64Platform
STABLE_BETA
La única implementación: host Windows x64, CreateProcessW para Omsi.exe, TerminateProcess para la detención canónica. El servicio llama a sus métodos; los integradores solo lo construyen. Miembros (compartidos con IRuntimePlatform): Detect(root) devuelve el RuntimePlatformInfo del plan; ValidateCurrent(info) lanza OL_E_UNSUPPORTED_OPERATING_SYSTEM / OL_E_UNSUPPORTED_OS_ARCHITECTURE / OL_E_PLATFORM_CAPABILITY_MISSING cuando el host no puede ejecutar una sesión; IsInstallationWritable(root) respalda OL_E_INSTALLATION_NOT_WRITABLE; StartAsync(request, sha256) crea Omsi.exe y registra la identidad del proceso (PID, hora de creación, ruta y el hash calculado por el servicio; OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED); HasExited, WaitForExitAsync y Terminate lo observan y lo finalizan.
Públicos en el ensamblado porque el servicio y las pruebas los comparten. No son una superficie de integración; LaunchedProcess encapsula internamente los handles del proceso y del thread de OMSI (no son miembros públicos) y IOmsiLaunch nunca lo devuelve.
OmsiLaunchService admite llamadas concurrentes sobre sesiones distintas: las sesiones residen en un ConcurrentDictionary y cada modificación por sesión ocurre bajo el bloqueo privado de esa sesión.
Las llamadas concurrentes sobre la misma sesión son seguras, pero se serializan donde importa: ExecuteRuntimeAsync toma un gate por sesión, por lo que un segundo comando espera al primero (su timeout comienza cuando se deposita en el buzón).
El supervisor se ejecuta en una tarea del grupo de threads (Task.Run) desde el momento en que StartSessionAsync retorna hasta que la sesión es terminal; consulta la telemetría y el proceso cada 100 ms. Los llamadores nunca ejecutan código del supervisor.
StopAsync y GetStatusAsync se completan de forma sincrónica y se pueden llamar desde cualquier thread, incluso dentro de un manejador de ProcessExit (la CLI lo hace con un margen de 4 s).
Ninguna llamada de la API tiene afinidad de thread; ninguna requiere un contexto de sincronización.
No hay IntPtr, nint, handles de Win32, direcciones nativas, punteros a VMT ni objetos de proceso. Los valores del resultado cuyas claves comienzan con internal_ o terminan en _address, _pointer, _vmt se eliminan antes de que un resultado salga de ExecuteRuntimeAsync.
No hay operaciones de runtime internal.*: internal.road-vehicles.make-basic es InternalOnly en el registro y devuelve OL_E_RUNTIME_OPERATION_UNKNOWN desde la API y desde la CLI.
No hay lecturas ni escrituras directas de la memoria de OMSI, ni acceso a nivel de archivo a la instalación más allá de lo que declara un LaunchSpec.
No hay handles entre procesos: el plano de control local es la única ruta entre procesos, y solo acepta session.status, session.events, session.stop y runtime.execute.
En este build no hay cierre cooperativo de OMSI, ni LAST_MAP_STATE, ni aplicación de fecha/hora/clima/vehículo del jugador, ni overlays de documentos de teclado/controlador.
Contenido de la lista de GetCapabilitiesAsync, miembros de la especificación de fecha/hora/clima/vehículo del jugador/entrada, DiagnosticsSpec, ExpectedExecutableSha256, RestoreConfiguration, ShutdownTimeoutSeconds
PARTIAL
RuntimeCommandWire, StartupHandoff, StartupHandoffWire, implementaciones de IRuntimePlatform, todos los ensamblados de implementación