Tradução da página original em inglês do OmsiLaunch 0.1.0-beta3. A página em inglês é a referência normativa: se as duas divergirem, valem a página em inglês e o código.
Esta página é a referência normativa da API pública gerenciada do OmsiLaunch 0.1.0-beta3: o assembly OmsiLaunch.Api (contratos) e o ponto de entrada para integradores OmsiLaunchService em OmsiLaunch.Core. Ela documenta somente o que o código atual faz. Tudo o que um integrador pode chamar, receber ou observar está listado aqui com seu nível de estabilidade; o que não está listado não é uma superfície de integração.
O inventário da API pública gerado lista todo tipo e membro público de OmsiLaunch.Api, OmsiLaunch.Core e OmsiLaunch.Process com sua assinatura e estabilidade; um gate de documentação falha quando o inventário e os assemblies divergem. Esta página explica a semântica.
O contrato está congelado para a linha de protocolo 0.1 e o caminho está validado em runtime em research/reports/OMSILAUNCH-RUNTIME-VALIDATION-MATRIX.md.
EXPERIMENTAL
Pode ser chamado e é testado, mas o contrato ou a evidência de runtime podem mudar antes de se tornar estável.
PARTIAL
Presente no contrato; somente parte do comportamento está implementada ou validada (o texto diz qual parte).
INTERNAL
Público no assembly por razões técnicas (a ponte compartilha o tipo), mas não é uma superfície de integração; pode mudar sem aviso.
UNAVAILABLE
Presente no contrato, mas rejeitado pela build atual.
Contratos puros: records, enums, IOmsiLaunch, registro de capacidades, catálogo de erros, formatos de transmissão (wire formats), helpers de D3D. Não contém IntPtr, nint, handle Win32, endereço nativo nem objeto de processo.
OmsiLaunch.Core
OmsiLaunchService (a implementação de IOmsiLaunch), OmsiLaunchRuntimePaths, SessionPlanner, LaunchValidation, SessionProfileCompiler.
OmsiLaunch.Process
IRuntimePlatform e CurrentWindowsX64Platform (o único adaptador de plataforma), InstallationLease. Necessários para construir o serviço.
Assemblies de implementação. Seus tipos públicos são INTERNAL para integradores.
Ponto de entrada: OmsiLaunchService e 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
Inválido / padrão
platform
new CurrentWindowsX64Platform() (namespace OmsiLaunch.Process). Detecta a plataforma, cria o processo do OMSI com CreateProcessW, aguarda seu término e o encerra.
Nenhuma outra implementação é distribuída. Um IRuntimePlatform personalizado é INTERNAL.
PluginBuildDirectory
Diretório que contém os arquivos de referência do conjunto de arquivos do plugin permanente (closure do plugin): OmsiLaunch.Plugin.opl, OmsiLaunch.PluginNE.dll, OmsiLaunch.Plugin.deps.json, OmsiLaunch.Plugin.runtimeconfig.json e todo OmsiLaunch.*.dll da closure gerenciada (deve incluir OmsiLaunch.Plugin.dll). Em um pacote instalado, é <package>\plugins.
Diretório ou arquivo ausente: PlanSessionAsync retorna um plano não apto para execução com OL_E_RUNTIME_ARTIFACT_MISSING.
NativeBridgePath
Caminho de OmsiLaunch.Native.x86.dll (no pacote: <package>\plugins\OmsiLaunch.Native.x86.dll).
Igual ao anterior.
ReleaseManifestPath
release-manifest.json ao lado de OmsiLaunch.exe, quando presente. Fornece o SHA-256 esperado de cada arquivo de plugins/ (plugin.integrity.reference = manifest).
null (layout de desenvolvimento): os arquivos instalados são verificados apenas quanto à presença e à autoconsistência em relação à closure de referência (plugin.integrity.reference = self). Manifesto malformado: OL_E_RELEASE_MANIFEST_INVALID.
O serviço lê esses caminhos em toda chamada de PlanSessionAsync e StartSessionAsync; ele nunca copia, prepara nem remove arquivos do plugin (consulte plugin permanente). A CLI constrói o serviço exatamente assim (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));
Crie um serviço por processo e compartilhe-o. Estabilidade: STABLE_BETA.
StartSessionAsync adquire o lease da instalação, um semáforo nomeado Local\OmsiLaunch.Installation.<SHA-256 of the upper-cased full root path>, e o mantém até que o supervisor tenha restaurado a instalação. Um segundo início na mesma raiz, a partir de qualquer processo da mesma sessão de logon, falha com OL_E_INSTALLATION_BUSY (relatado como uma sessão Failed, consulte StartSessionAsync). O lease vale por sessão de logon, não entre sessões de logon diferentes, e não é liberado enquanto outro processo mantiver um handle para ele (risco aceito).
Handles são locais ao processo
SessionHandle encapsula o Guid da sessão. Ele só tem significado para a instância de OmsiLaunchService que o retornou. Um handle construído a partir de um Guid conhecido em outro processo (ou em outra instância do serviço) resulta em KeyNotFoundException. O controle entre processos passa pelo plano de controle local, não por handles.
Sempre chame CloseAsync
A partir de StartSessionAsync, o processo possui uma transação durável. CloseAsync solicita a parada canônica quando necessário, aguarda o supervisor (saída do processo, restauração exata, liberação do lease) e esquece a sessão. Deve ser chamado em todo caminho de saída, inclusive após um estado Failed. Sem ele, a entrada da sessão permanece na memória; a restauração em si é executada pelo supervisor de qualquer forma.
Sessões com falha continuam sendo sessões
Um início que falha depois que StartSessionAsync retornou relata SessionState.Failed; o handle continua válido para GetStatusAsync/WaitForAsync até CloseAsync.
Planos são verificados novamente
StartSessionAsync recalcula o hash de Omsi.exe e replaneja a especificação; um plano que não está mais apto para execução é rejeitado com OL_E_PLAN_NOT_RUNNABLE.
Handles desconhecidos ou já fechados lançam KeyNotFoundException ("Unknown OmsiLaunch session.").
Nenhum método exige uma sessão em execução (Running), exceto ExecuteRuntimeAsync.
Exceções que carregam um código do OmsiLaunch colocam o código no início de Exception.Message ("OL_E_PLAN_NOT_RUNNABLE: ..."). A CLI extrai códigos das mensagens da mesma forma (CliProgram.Classify).
Build suportada: somente Omsi23004_692EBFBF (além do hash da lista de permissões Steam LAA, aceito; gameplay não validado, pois exige uma instalação Steam genuína). Consulte compatibilidade.
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 um LaunchSpec em um SessionPlan sem iniciar o OMSI: validar a especificação, detectar a plataforma, calcular a impressão digital de Omsi.exe, resolver as identidades de conteúdo, calcular as mutações de arquivo planejadas, listar as capacidades exigidas e as não suportadas e decidir IsRunnable. Capacidade pública session.plan.
Parâmetros
spec: um LaunchSpec totalmente preenchido (consulte a referência do LaunchSpec). Installation, World, Date, Time, Environment (todos os oito dicionários) e Behavior não podem ser nulos; os membros opcionais podem ser null. RootPath deve ser um diretório absoluto; uma raiz vazia é registrada como OL_E_INSTALLATION_NOT_FOUND, mas a sondagem da plataforma em um caminho vazio lança ArgumentException antes que o plano seja retornado, portanto nunca passe uma raiz vazia.
Retorno
SessionPlan com um SessionId novo, BuildProfileId = "Omsi23004_692EBFBF" (sempre essa constante, mesmo quando o executável não corresponde), o Spec de entrada, Platform, ResolvedContent, TouchedFiles, RuntimeArtifacts (caminhos de destino plugins\OmsiLaunch.* mais "OmsiLaunch startup handoff v4"), RequiredCapabilities, UnsupportedRequestedFeatures, PlannedMutations, Diagnostics, IsRunnable. IsRunnable é true exatamente quando nenhum código de diagnóstico começa com OL_E_. Diagnósticos informativos (plugin.integrity.reference com mensagem self ou manifest, session_profile.selected) nunca tornam um plano não apto para execução.
Erros no resultado
Todo erro de planejamento é um diagnóstico, não uma exceção: 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 (a mensagem carrega o código de splash/ITX), OL_E_PERMANENT_PLUGIN_MISSING, OL_E_PERMANENT_PLUGIN_HASH_MISMATCH, OL_E_PERMANENT_PLUGIN_MANIFEST_INCOMPLETE (a closure do plugin instalada em plugins\ é verificada em relação ao manifesto de release no momento do planejamento), OL_E_RUNTIME_ARTIFACT_MISSING (a mensagem pode carregar OL_E_RELEASE_MANIFEST_INVALID). Condições completas: regras de validação do LaunchSpec.
Exceções lançadas
OperationCanceledException se o token já estiver cancelado na entrada (o único ponto de verificação); ArgumentException/NotSupportedException para caminhos de raiz sintaticamente inválidos; NullReferenceException/ArgumentNullException para membros obrigatórios nulos; System.Text.Json.JsonException para um manifesto de release sintaticamente inválido.
Cancelamento
Verificado uma vez na entrada. Depois disso, o planejamento é trabalho síncrono no sistema de arquivos.
Exige sessão em execução
Não.
Altera o estado do OMSI
Não.
Altera o sistema de arquivos
Não (lê Omsi.exe, arquivos de conteúdo, a closure do plugin, o manifesto). Os valores de configuração não são validados aqui (somente a existência da chave e a possibilidade de escrita); um valor inválido falha no início com OL_E_INVALID_SETTING_VALUE.
Transação / restauração
Nenhuma.
Limitações
Solicitar qualquer modo de Date/Time/Year diferente de Unset, qualquer modo de Weather diferente de Unset, qualquer campo de PlayerVehicle, documentos de Input, EntrypointIdentity ou WorldMode.LastMapState produz OL_E_CAPABILITY_UNAVAILABLE e um plano não apto para execução nesta build (entradas STATICALLY_PARTIAL / UNSUPPORTED_FOR_CURRENT_PROFILE em UnsupportedRequestedFeatures).
Estabilidade
STABLE_BETA.
Exemplo
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 uma sessão gerenciada e transacional do OMSI a partir de um plano apto para execução: adquirir o lease da instalação, recuperar um journal obsoleto, validar a closure do plugin permanente, fazer snapshot dos arquivos da sessão e aplicar overlays sobre eles, criar o handoff de inicialização, o slot de telemetria e o mailbox de runtime, iniciar Omsi.exe, registrar o processo no journal e entregar a sessão a um supervisor em segundo plano. Capacidade pública session.start.
Parâmetros
plan: um SessionPlan com IsRunnable == true. A especificação dentro do plano é replanejada; do plano do chamador, somente plan.SessionId é mantido. plan.Spec.Behavior.StartupTimeoutSeconds deve estar em 1..600.
Retorno
SessionHandle(plan.SessionId) assim que Omsi.exe for criado e registrado (estado WaitingForPlugin), ou assim que o caminho de início falhar (estado Failed). Não aguarda o gameplay; use WaitForAsync(session, SessionState.Running, ...).
Exceções lançadas
InvalidOperationException("OL_E_PLAN_NOT_RUNNABLE") quando plan.IsRunnable é false; InvalidOperationException("OL_E_PLAN_NOT_RUNNABLE: <codes>") quando o replanejamento não está apto para execução (por exemplo, Omsi.exe foi alterado, conteúdo foi removido, closure do plugin ausente); InvalidOperationException("Duplicate session id.") quando uma sessão com o mesmo id ainda está registrada (chame CloseAsync antes); ArgumentOutOfRangeException quando StartupTimeoutSeconds está fora de 1..600; OperationCanceledException quando cancelado antes ou durante o replanejamento; e tudo o que PlanSessionAsync lança. Em todos os casos com exceção, nenhuma sessão é registrada.
Erros no resultado
Qualquer falha após o replanejamento é capturada dentro do caminho de início: a sessão é registrada, seu estado é Failed e seus diagnósticos contêm OL_E_START_SESSION, cuja mensagem é a mensagem interna (começando pelo código interno, quando houver): OL_E_INSTALLATION_BUSY (lease ocupado ou um processo do OMSI registrado no journal ainda 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 (somente quando a nova tentativa adiada com os overlays desta sessão ainda não consegue provar a propriedade), OL_E_RECOVERY_JOURNAL_REMOVE_FAILED, OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED. A limpeza pode acrescentar OL_E_PROCESS_CLEANUP_FAILED, OL_E_RESTORE_DEFERRED (saída do OMSI não confirmada; journal mantido) ou OL_E_RESTORE_FAILED. Falhas posteriores são relatadas pelo supervisor (consulte ciclo de vida da sessão).
Cancelamento
Antes/durante o replanejamento: lança exceção. Depois disso, o token é repassado à transação e à criação do processo; um cancelamento nesse ponto é tratado como qualquer falha de início (Failed + OL_E_START_SESSION: The operation was canceled.), o processo (se criado) é encerrado e a instalação é restaurada.
Exige sessão em execução
Não.
Altera o estado do OMSI
Sim: cria o processo do OMSI com as variáveis de ambiente OMSILAUNCH_SESSION_ID, OMSILAUNCH_HANDOFF_NAME, OMSILAUNCH_TELEMETRY_NAME, OMSILAUNCH_RUNTIME_CHANNEL, OMSILAUNCH_INTERNET_TEXTURES_MODE.
Altera o sistema de arquivos
Sim, dentro da raiz da instalação: .omsilaunch\diagnostics\<sessionId>-host.log (retenção: as 50 sessões mais recentes), .omsilaunch\journal.json, .omsilaunch\backup\<sessionId>\*.bin, .omsilaunch\assets\splash\*.bmp (copiado uma vez para o splash gerenciado), overlays da sessão (patches de options.cfg, GUI\NewSplashscreen_*.bmp, Texture\standard.itx), exclusões da sessão (destinos ITX, Texture\standard.ipr, closecheck) e remoção permanente de um closecheck obsoleto preexistente quando SuppressStaleClosecheckWarning é true (diagnóstico closecheck.stale-removed).
Transação / restauração
Abre a transação (Prepared → Applied → RuntimeDeployed → HandoffCreated → ProcessStarted). Todo caminho de saída da sessão termina em restauração. Consulte transações e recuperação.
Limitações
Somente WorldMode.NewMap com PresentedEntrypointIndex e WorldMode.SavedSituation chegam ao gameplay. WorldMode.LastMapState é UNAVAILABLE. Solicitações de data/hora/clima/veículo do jogador/entrada nunca chegam a este método, porque já são não aptas para execução no momento do planejamento.
Estabilidade
STABLE_BETA (os ciclos de vida NEW_MAP e SAVED_SITUATION são validados em runtime).
Exemplo
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);
Ler o estado semântico do ciclo de vida, os diagnósticos coletados até o momento e a lista limitada de eventos de runtime. Capacidade pública session.status.
Parâmetros
session: um handle retornado por StartSessionAsync e ainda não fechado.
Retorno
SessionStatus(SessionId, State, Diagnostics, RuntimeEvents): um snapshot imutável (os arrays são copiados sob o lock da sessão). RuntimeEvents nunca é null para uma sessão ativa.
Exceções lançadas
KeyNotFoundException para handles desconhecidos/fechados. Fora isso, nunca lança exceção.
Cancelamento
O token é ignorado (a chamada é concluída de forma síncrona).
Exige sessão em execução
Não.
Altera OMSI / sistema de arquivos / transação
Não / Não / Nenhuma.
Estabilidade
STABLE_BETA.
Exemplo
var status = await launch.GetStatusAsync(session); Console.WriteLine($"{status.State} events={status.RuntimeEvents!.Count}");
Consultar periodicamente (a cada 100 ms) até que a sessão esteja em state, ou em um estado terminal (Completed, Failed), ou até que o timeout expire; então retornar o status atual.
Parâmetros
state: qualquer SessionState. Aguardar um estado transitório que já passou (ou que nunca é definido, consulte ciclo de vida da sessão) aguarda até um estado terminal ou até o timeout. timeout: qualquer TimeSpan não negativo ou Timeout.InfiniteTimeSpan.
Retorno
O status no momento em que a espera terminou. No timeout, o status é retornado, não uma exceção: verifique State você mesmo. Uma espera por Running que termina em Failed retorna imediatamente com os diagnósticos da falha.
Exceções lançadas
KeyNotFoundException; OperationCanceledException quando o token do chamador é cancelado (somente o cancelamento do chamador se propaga; o timeout interno não).
Cancelamento
O token do chamador é respeitado a cada tick de 100 ms.
Exige sessão em execução
Não.
Altera OMSI / sistema de arquivos / transação
Não / Não / Nenhuma.
Estabilidade
STABLE_BETA.
Exemplo
var running = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(185)); if (running.State != SessionState.Running) { /* timed out or Failed */ }
Solicitar a parada canônica. Define a flag de parada e retorna imediatamente; o supervisor observa a flag dentro do seu loop de 100 ms, chama TerminateProcess em Omsi.exe, aguarda a saída, marca o journal como ProcessExited, restaura todo arquivo de propriedade da sessão e libera o lease. Trata-se de um encerramento forçado: a rotina de desligamento do próprio OMSI não é executada e o OMSI não reescreve options.cfg ao sair (deliberado, protege a transação). O desligamento cooperativo via WM_CLOSE não está implementado (decisão de produto; no fechamento de runtime, o OMSI não fechou em até 30 s após WM_CLOSE, L05b). Capacidade pública session.stop.
Parâmetros
session.
Retorno
Tarefa concluída; não aguarda o encerramento nem a restauração. Use WaitForAsync(session, SessionState.Completed, ...) para observar a conclusão.
Exceções lançadas
KeyNotFoundException.
Cancelamento
Token ignorado.
Exige sessão em execução
Não. Idempotente; uma parada solicitada antes que o supervisor inicie é atendida assim que ele iniciar; uma parada em uma sessão terminal não tem efeito.
Altera o estado do OMSI
Sim: encerra o processo do OMSI (código de saída 1).
Altera o sistema de arquivos
Indiretamente: dispara a restauração, a exclusão do journal e a remoção dos backups pelo supervisor.
Transação / restauração
Dispara ProcessExited → Restoring → Restored. Alterações do lado do runtime feitas por meio de ExecuteRuntimeAsync (escritas no relógio, veículos gerados, variáveis de script, texturas D3D) não são restauradas; elas desaparecem com o processo.
Estabilidade
STABLE_BETA.
Exemplo
await launch.StopAsync(session); var done = await launch.WaitForAsync(session, SessionState.Completed, TimeSpan.FromMinutes(1));
Liberar o handle do consumidor sem deixar a transação abandonada: se a sessão não for terminal, solicitar a parada canônica; em seguida, aguardar a tarefa de ciclo de vida do supervisor (saída do processo, restauração, liberação do lease); depois, esquecer a sessão.
Parâmetros
session.
Retorno
É concluído quando a sessão está terminal e foi removida. Depois que retorna, o handle é desconhecido (KeyNotFoundException em qualquer chamada posterior, inclusive um segundo CloseAsync).
Exceções lançadas
KeyNotFoundException; OperationCanceledException se o chamador cancelar enquanto aguarda o supervisor. Nesse caso, a sessão não é removida e o supervisor continua em execução; chame CloseAsync novamente.
Cancelamento
Aplica-se somente à espera; nunca cancela a restauração.
Exige sessão em execução
Não.
Altera o estado do OMSI
Sim, quando a sessão ainda está ativa (igual a StopAsync).
Altera o sistema de arquivos
Indiretamente (restauração pelo supervisor).
Transação / restauração
Garante que a transação seja levada até a conclusão antes que o handle seja liberado (quando o supervisor foi iniciado). Para uma sessão que falhou antes do início do supervisor, o caminho de início já restaurou ou relatou OL_E_RESTORE_DEFERRED.
Executar uma operação de runtime pública dentro do processo do OMSI em execução por meio do mailbox da sessão, de voo único (mapeado em memória, 64 KiB, requisição vinculada ao id da sessão e ao id da requisição). O plugin executa a operação no thread de UI do OMSI. Catálogo de operações: controle de runtime e capacidades.
Parâmetros
command.SessionId deve ser igual a session.SessionId. command.RequestId: ulong escolhido pelo chamador; use um contador estritamente crescente por processo (os helpers de D3D começam em 30 000, o proprietário da CLI em 10 001/50 000). command.Operation: um id de operação pública de PublicCapabilityRegistry.PublicRuntimeOperationIds (por exemplo, time.read, road-vehicle.read, d3d.texture.create). command.Arguments: valores string indexados por nomes ordinais; os nomes obrigatórios por operação vêm de PublicCapabilityRegistry.GetRuntimeArguments. timeout: medido a partir do momento em que a requisição é colocada no mailbox (a espera na fila atrás de outro comando em andamento não é contada). A CLI usa 5 s (15 s para road-vehicles.spawn) como proprietário e 8 s / 30 s como cliente.
Ordem das verificações
1. Validação no registro (antes da busca da sessão): operação desconhecida ou internal.* → resultado Succeeded=false, ErrorCode=OL_E_RUNTIME_OPERATION_UNKNOWN; argumento obrigatório ausente (inexistente ou só com espaços em branco) → OL_E_RUNTIME_ARGUMENT_REQUIRED. 2. Busca da sessão → KeyNotFoundException. 3. command.SessionId != session.SessionId → InvalidOperationException("OL_E_RUNTIME_SESSION_MISMATCH"). 4. Estado diferente de Running → InvalidOperationException("OL_E_SESSION_NOT_RUNNING"). 5. Requisição ao mailbox. 6. Valores do resultado cuja chave começa com internal_ ou termina com _address, _pointer, _vmt são removidos.
Retorno
RuntimeCommandResult(SessionId, RequestId, Succeeded, ErrorCode, Values). Em caso de sucesso, Values contém as strings semânticas da operação (documentadas por operação em controle de runtime).
Erros no resultado
OL_E_RUNTIME_OPERATION_UNKNOWN, OL_E_RUNTIME_ARGUMENT_REQUIRED (registro); OL_E_RUNTIME_RESPONSE_TOO_LARGE (o resultado do plugin excedeu o mailbox; resultados de listas limitadas são, em vez disso, encurtados com truncated=true); OL_E_RUNTIME_SETTING_NOT_PERSISTENT (weather.set, sempre); todo código OL_E_D3D_* (com Values["detail"] e Values["native_status"]); e OL_E_RUNTIME_OPERATION_FAILED para qualquer outra falha do lado do plugin. Neste último caso, o código específico não está em ErrorCode: ele é o primeiro token de Values["detail"] (por exemplo, detail = "OL_E_RUNTIME_OBJECT_HANDLE_STALE", exception = "InvalidOperationException"). Códigos que chegam dessa forma: OL_E_RUNTIME_OPERATION_UNAVAILABLE, OL_E_RUNTIME_ARGUMENT_REQUIRED (verificações do lado do 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 erro.
Exceções lançadas
KeyNotFoundException; InvalidOperationException com OL_E_RUNTIME_SESSION_MISMATCH, OL_E_SESSION_NOT_RUNNING, OL_E_RUNTIME_CHANNEL_CLOSED (mailbox já descartado pelo supervisor), OL_E_RUNTIME_CHANNEL_BUSY (o slot ainda contém uma requisição abandonada), OL_E_RUNTIME_REQUEST_ID_REUSED (uma resposta obsoleta para o mesmo id de requisição ainda está no slot); TimeoutException("OL_E_RUNTIME_REQUEST_TIMEOUT"); InvalidDataException("OL_E_RUNTIME_RESPONSE_INVALID") (resposta corrompida, estranha ou não correspondente); ArgumentOutOfRangeException quando a requisição serializada excede o mailbox; OperationCanceledException.
Cancelamento
Respeitado durante a espera pelo gate por sessão e a cada 20 ms durante a consulta periódica da resposta. Cancelar com a requisição em andamento não reinicia o slot: a próxima chamada nessa sessão pode falhar com OL_E_RUNTIME_CHANNEL_BUSY até que o plugin publique sua resposta (que é então descartada como obsoleta). Prefira o timeout; um timeout reinicia o slot, e uma resposta tardia é detectada e descartada.
Exige sessão em execução
Sim (SessionState.Running); caso contrário, OL_E_SESSION_NOT_RUNNING é lançado. O mailbox existe até que o supervisor o descarte durante a restauração.
Altera o estado do OMSI
Depende da operação: operações Read não alteram; operações Write/Action (time.set, camera.set, camera.lock, camera.unlock, road-vehicles.spawn, road-vehicles.place-random, vehicle.variable.set, d3d.texture.*) alteram estado dentro do processo que não é restaurado.
Altera o sistema de arquivos
Nenhuma escrita pelo host. O OMSI pode escrever seus próprios arquivos como consequência (não rastreado).
Transação / restauração
Nenhuma.
Limitações
Um comando em andamento por sessão (chamadas na mesma sessão são serializadas). Requisição e resposta são limitadas, cada uma, a 64 KiB menos 8 bytes; payloads de pixels D3D, a 48 KiB. internal.road-vehicles.make-basic é INTERNAL e inacessível. weather.set é UNAVAILABLE. timetable.logs.read não é limitada e pode retornar OL_E_RUNTIME_RESPONSE_TOO_LARGE em tabelas de horários grandes. camera.lock é EXPERIMENTAL; exige um veículo do jogador e está validada em runtime (CAM01), embora a string RuntimeValidation do registro ainda indique STATICALLY_VALIDATED. Os handles (rv-NNNNNN, hb-NNNNNN, d3dtex-<session>-<hex>) têm escopo de sessão.
Estabilidade
Transporte e contrato STABLE_BETA; a estabilidade por operação segue PublicCapabilityRegistry (PublicStableBeta → STABLE_BETA, PublicExperimental → EXPERIMENTAL), com as exceções acima.
Exemplo
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"]}");
Retornar o inventário de evidências do produto para uma instalação: uma lista fixa de entradas Capability(Name, Available, EvidenceState, Reason) mantida em OmsiLaunchService. Somente runtime.current-windows-x64 é calculada (a partir da detecção da plataforma); todas as demais entradas são constantes.
Parâmetros
installation.RootPath: diretório usado para a sondagem da plataforma (a possibilidade de escrita exige que o diretório exista, não seja somente leitura e contenha plugins\). ExpectedExecutableSha256 é ignorado.
PublicCapabilityRegistry.All é o catálogo da superfície de controle em tempo de compilação (36 descritores com classificação, tipo, rotas de API e CLI, argumentos obrigatórios) que a API e a CLI aplicam; ele não depende da instalação. GetCapabilitiesAsync é um relatório de evidências de runtime (estado de validação e motivos). Use o registro para decidir o que você pode chamar; use esta lista para decidir o que foi comprovado. Nenhuma das listas é derivada da outra.
Exceções lançadas
OperationCanceledException na entrada; ArgumentException para um caminho de raiz vazio.
Cancelamento
Verificado uma vez na entrada.
Exige sessão em execução
Não.
Altera OMSI / sistema de arquivos / transação
Não / Não / Nenhuma.
Estabilidade
Contrato da chamada STABLE_BETA; o conteúdo da lista é um inventário mantido manualmente: PARTIAL.
Exemplo
foreach (var c in await launch.GetCapabilitiesAsync(new InstallationSpec(root))) Console.WriteLine($"{c.Name} {c.Available} {c.EvidenceState} {c.Reason}");
Enumerar o conteúdo instalado e retornar identidades canônicas utilizáveis em um LaunchSpec. A descoberta ignora pontos de nova análise (reparse points) (ciclos de junções não conseguem travá-la), lê os arquivos do OMSI como Windows-1252 (UTF-8/UTF-16 marcados com BOM são respeitados) e nunca segue links simbólicos.
Parâmetros
query e scope conforme a tabela abaixo. scope é obrigatório para Entrypoints (identidade do mapa), Repaints, FleetNumbers, Registrations (identidade do veículo).
Retorno
Lista ordenada de ContentIdentity(Identity, Kind, DisplayName). As identidades são caminhos relativos à instalação com barras invertidas; as comparações não diferenciam maiúsculas de minúsculas.
Exceções lançadas
OperationCanceledException na entrada; ArgumentException quando Entrypoints é consultado sem escopo ou a raiz está vazia; FileNotFoundException (sem código OL_E_; a CLI o mapeia para OL_E_NOT_FOUND) quando o mapa ou veículo do escopo não está instalado. Uma raiz ou diretório de conteúdo ausente produz uma lista vazia, não um erro.
Cancelamento
Verificado uma vez na entrada.
Exige sessão em execução
Não.
Altera OMSI / sistema de arquivos / transação
Não / Não / Nenhuma.
Estabilidade
Maps, Situations, Vehicles: STABLE_BETA (todo plano validado em runtime é resolvido por meio deles). Entrypoints, Repaints, Hofs, FleetNumbers, Registrations, Addons: EXPERIMENTAL (somente evidência estática).
Exemplo
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 e resultados de ContentQueryKind:
Valor
Escopo
Identity
Kind
DisplayName
Maps
nenhum
maps\<dir>\global.cfg
map
nome do diretório do mapa
Situations
nenhum
situations\...\<file>.osn
situation
identidade do mapa referenciado pelo .osn (pode ser null)
Vehicles
nenhum
Vehicles\...\<file>.bus
vehicle
[friendlyname] ou nome do arquivo
Repaints
identidade do veículo (obrigatória; sem ela: lista vazia)
<cti path>#item:<ordinal>
repaint
nome de [item]
Hofs
nenhum
Vehicles\...\<file>.hof
hof
null
FleetNumbers
identidade do veículo (obrigatória; sem ela: lista vazia)
caminho de origem de [number] relativo ao veículo
fleet-number
null
Registrations
identidade do veículo (obrigatória; sem ela: lista vazia)
identidade do mapa (obrigatória; sem ela: ArgumentException)
<map identity>#entrypoint:<SHA-256 of the 12-line record>
entrypoint
rótulo do ponto de entrada
As identidades de ponto de entrada servem somente para descoberta: o caminho de inicialização usa PresentedEntrypointIndex; passar um EntrypointIdentity torna o plano não apto para execução nesta build (world.entrypoint-identity, RUNTIME_PARTIAL).
Relatar ou concluir uma transação durável obsoleta (<root>\.omsilaunch\journal.json) deixada por um proprietário que travou. Adquire o lease da instalação durante a chamada, para nunca restaurar por baixo de uma sessão que está iniciando. Capacidade pública session.recover; CLI /recovery-status e /recover.
Parâmetros
installation.RootPath: a raiz da instalação (normalizada com Path.GetFullPath). restore: false = somente relatar; true = restaurar, verificar, excluir o journal e os backups.
Retorno
RecoveryStatus(Pending, Recovered, Diagnostics): Pending = havia um journal quando a chamada começou; Recovered = uma restauração foi solicitada, foi executada e nenhum journal permanece; Diagnostics = notas da restauração (restore.session-artifact-removed com Data["sha256"], OL_W_RESTORE_FOREIGN_FILE_RETAINED), vazio quando nada foi restaurado.
Exceções lançadas
InvalidOperationException("OL_E_INSTALLATION_BUSY: another OmsiLaunch owner holds this installation.") quando o lease está ocupado; IOException("OL_E_INSTALLATION_BUSY: a journaled OMSI process is still alive.") quando o PID + horário de criação + caminho do executável registrados no journal ainda correspondem a um processo ativo ou (journal além de HandoffCreated sem PID) quando qualquer Omsi.exe dessa raiz está em execução; IOException com OL_E_RECOVERY_BACKUP_CORRUPT, OL_E_RECOVERY_ABSENT_OWNERSHIP_MISMATCH, OL_E_RECOVERY_ABSENT_OWNERSHIP_UNVERIFIED, OL_E_RECOVERY_JOURNAL_REMOVE_FAILED ou uma mensagem de verificação ("Restore hash mismatch: ...", "Restore presence mismatch: ..."); InvalidDataException("Invalid OmsiLaunch journal.") / JsonException para um journal corrompido; ArgumentException para uma raiz vazia; OperationCanceledException. Sempre que lança exceção depois que uma restauração começou, o journal é mantido e a próxima chamada reexecuta de forma idempotente.
Cancelamento
Repassado às escritas do journal/backup; cancelar no meio da restauração deixa o journal pendente.
Exige sessão em execução
Não (recusa enquanto um proprietário está ativo).
Altera o estado do OMSI
Não.
Altera o sistema de arquivos
Somente com restore == true: reescreve os originais a partir de backups verificados (bytes, horário da última escrita, horário de criação, atributos; originais somente leitura são tratados; write-through + flush; nenhum *.omsilaunch.tmp é deixado), remove os artefatos da sessão, exclui journal.json e backup\<sessionId>.
Transação / restauração
Conclui a transação pendente (Restoring → Restored → journal removido).
Estabilidade
STABLE_BETA: caminho de relatório e recuperação após saída antecipada (matriz RV-008), recuperação após uma restauração com falha (fechamento de runtime F01), recusa sob um proprietário ativo e na janela anterior ao PID (S04) e recuperação adiada anterior à impressão digital no início (S05); consulte status da validação em runtime.
Exemplo
var r = await launch.RecoverPendingAsync(new InstallationSpec(root), restore: true); Console.WriteLine($"pending={r.Pending} recovered={r.Recovered}");
Todos os records abaixo estão documentados propriedade por propriedade na referência do LaunchSpec; esta tabela fixa o inventário de tipos.
Tipo
Finalidade
Estabilidade
LaunchSpec
Record raiz da requisição com os acessores EffectiveYear, EffectiveWeather, EffectiveInput, EffectiveDiagnostics, EffectivePresentation, EffectiveInternetTextures, que substituem membros opcionais null por valores padrão.
STABLE_BETA
InstallationSpec
RootPath, ExpectedExecutableSha256 (transportado, não consumido).
Seleção do mundo. WorldMode: NewMap = 0, SavedSituation = 1, LastMapState = 2, LastSituation = 2 (alias obsoleto de LastMapState; nunca "o .osn mais recente"). EntrypointMode: Unset, PresentedIndex, Identity (calculado a partir de WorldSpec.Entrypoint).
Procedência de um perfil de sessão compilado (Id, Name, Version, Author, PresetId, PresetIndex, PresetName, PackagePath).
STABLE_BETA
LaunchBehaviorSpec
RestoreConfiguration (transportado; a restauração sempre acontece), SuppressStaleClosecheckWarning, StartupTimeoutSeconds (1..600, padrão 180), ShutdownTimeoutSeconds (transportado, não consumido).
SessionId (novo Guid por plano), BuildProfileId ("Omsi23004_692EBFBF"), Spec, Platform (RuntimePlatformInfo), ResolvedContent (lista de ContentIdentity: map, vehicle, repaint, hof, situation, situation-map), TouchedFiles (caminhos relativos distintos de PlannedMutations), RuntimeArtifacts, RequiredCapabilities (Capability com STATICALLY_VALIDATED ou UNAVAILABLE), UnsupportedRequestedFeatures (entradas Capability para recursos solicitados, mas não suportados), PlannedMutations, Diagnostics, IsRunnable.
Um record público: pode ser editado ou ficar desatualizado, e é por isso que StartSessionAsync replaneja.
RuntimePlatformInfo
OsFamily, OsVersion, OsArchitecture, HostArchitecture, OmsiArchitecture (X86), PluginArchitecture (X86), CurrentPlatformSupported (Windows 10+, SO x64 e host x64), LegacyPlatform (sempre false), Wow64Available, InstallationWritable, ProcessLaunchSupported, PluginRuntimeSupported, NativeInteropSupported, SharedMemorySupported, ExactRestoreSupported (todos iguais a CurrentPlatformSupported).
Capability
Name, Available, EvidenceState, Reason.
As strings de evidência são texto livre (RUNTIME_VALIDATED, STATICALLY_VALIDATED, STATICALLY_PARTIAL, RUNTIME_PARTIAL, UNAVAILABLE, UNSUPPORTED_FOR_CURRENT_PROFILE, IMPLEMENTED_NOT_RUNTIME_VALIDATED, RELEASE_IF_CLOSED).
Mutações de apresentação usam estas chaves: session-presentation.splash, internet-textures.override, internet-textures.cache, internet-textures.target.
LaunchDiagnostic
Code, Message, Data (mapa de strings opcional).
Códigos que começam com OL_E_ são erros, OL_W_ são avisos e qualquer outro é informativo.
SessionStatus
SessionId, State (SessionState), Diagnostics, RuntimeEvents.
Os diagnósticos da sessão não incluem os diagnósticos do plano.
RuntimeEvent
Type, TimestampUtc (horário de recebimento pelo host), Sequence (começando em 1, por sessão), Data.
Limitado aos 256 eventos mais recentes (os mais antigos são descartados). O slot de telemetria guarda apenas o valor mais recente: eventos emitidos mais rápido do que a consulta periódica de 100 ms do host podem ser perdidos. Não é um log sem perdas.
Enum de byte na ordem de declaração: Created, ValidatingPlatform, Planning, AcquiringInstallationLock, RecoveringPreviousTransaction, Snapshotting, ApplyingConfiguration, DeployingRuntime, CreatingStartupHandoff, StartingProcess, WaitingForPlugin, PluginBootstrap, StartingWorld, EnteringGameplay, Running, ProcessExited, Restoring, CleaningRuntime, Completed, Failed. ValidatingPlatform, Planning e EnteringGameplay nunca são definidos pelo serviço atual; Snapshotting é transitório e, na prática, não observável. Estados terminais: Completed, Failed. Semântica completa: ciclo de vida da sessão.
Codec estático usado pelo host e pelo plugin para o envelope do mailbox: magic 0x4F4C5243 ("OLRC"), versão 1, cabeçalho little-endian de 72 bytes (magic, versão, tipo 1 = requisição / 2 = resposta, comprimento total, Guid da sessão, id da requisição, comprimento do payload, SHA-256 do payload) seguido de um payload JSON em UTF-8. SerializeRequest, SerializeResponse, TryDeserializeRequest, TryDeserializeResponse, TryReadRequestId.
INTERNAL: público porque as duas extremidades da ponte o compartilham; não é uma superfície de integração; o formato pode mudar com a versão do protocolo.
StartupHandoff
(Guid SessionId, string BuildProfileId, WorldMode WorldMode, string MapIdentity, int PresentedEntrypointIndex, bool HeadlessStart, bool PlayerVehicleEnabled, DateTimeMode DateMode, DateTimeMode TimeMode, string EntrypointIdentity, string SituationIdentity) — o que o host publica para o plugin no arquivo mapeado em memória OmsiLaunch.Handoff.<sessionId>.
INTERNAL
StartupHandoffWire
Codec: magic 0x4F4C5348, versão 4 (lê 3 e 4), cabeçalho de 64 bytes com integridade do payload por SHA-256.
INTERNAL
O plugin rejeita um handoff (plugin.request.unsupported → OL_E_CAPABILITY_UNAVAILABLE), a menos que WorldMode seja NewMap ou SavedSituation, HeadlessStart seja true, PlayerVehicleEnabled seja false, ambos os modos de data/hora sejam Unset e uma situação salva tenha uma identidade não vazia. O planejador aplica as mesmas restrições antes, portanto um plano apto para execução nunca provoca essa rejeição.
ProtocolVersion ("0.1"), All (36 entradas PublicCapabilityDescriptor), PublicRuntimeOperationIds (os 48 ids de operação concretos que um frontend pode encaminhar), IsPublicRuntimeOperation, GetRuntimeArguments, ValidateRuntimeArguments (retorna PublicRuntimeArgumentValidation), IsInternalResultKey. Aplicado por ExecuteRuntimeAsync, pela CLI e pelo plano de controle local.
Wrappers tipados sobre ExecuteRuntimeAsync para as operações d3d.* (EXPERIMENTAL, capacidade PublicExperimentald3d.texture). Eles alocam ids de requisição a partir de um contador global do processo que começa em 30 000 e usam 5 s como timeout padrão (exceto GetD3DStatusAsync, que exige um timeout).
Erros: um resultado com falha é relançado como OmsiRuntimeException(Code, detail), em que Code é o ErrorCode do resultado (ou OL_E_RUNTIME_OPERATION_FAILED quando ausente) e a mensagem é "<code>: <Values["detail"]>"; um resultado bem-sucedido sem valores, ou uma string de estado do dispositivo desconhecida, lança OmsiRuntimeException("OL_E_RUNTIME_PROTOCOL_MISMATCH", ...). Tudo o que ExecuteRuntimeAsync lança se propaga sem alteração. O tratamento do reset do dispositivo está validado em runtime: um reset leva o dispositivo por Resetting de volta a Ready e invalida toda textura ativa (OL_E_D3D_STALE_RESOURCE_HANDLE, fechamento de runtime D01); GetCapabilitiesAsync ainda relata runtime.d3d.lifecycle.reset como IMPLEMENTED_NOT_RUNTIME_VALIDATED (defasagem do autorrelato). A transição para Lost não pode ser produzida de fora do produto e é coberta somente 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. Usado somente pela CLI (códigos de saída); a API nunca encerra o processo.
PublicErrorCategory
Constantes string usadas nos envelopes de erro da CLI/controle: invalid_argument, unsupported_profile, session, runtime, not_found, transaction, internal.
PublicErrorCodes
Uma const string por código (143: 142 erros OL_E_ e 1 aviso OL_W_) e All, o catálogo PublicErrorDescriptor(Code, Category). Categorias: Cli, Compatibility, Content, Installation, InvalidArgument, LaunchSpec, LocalControl, Other, Presentation, Process, Runtime, RuntimeD3D, Session, SessionProfile, Transaction, Warning. Referência: códigos de erro.
PublicErrorDescriptor
(string Code, string Category).
OmsiRuntimeException
Propriedade Code mais a mensagem; lançada somente por D3DRuntimeApi.
InstallationPaths (identidade da instalação e contenção de caminhos)#
Estabilidade: STABLE_BETA (funções puras, sem E/S, sem estado do OMSI, sem alteração do sistema de arquivos, sem participação em transação, sem exigir sessão em execução). É a definição única usada pelo lease da instalação, pelo nome do pipe de controle local, pelo confinamento de assets do perfil de sessão, pela validação de destinos das texturas da internet e pelo caminho do modelo para spawn em runtime.
Membro
Comportamento
string NormalizeRoot(string root)
Path.GetFullPath(root) sem separadores finais, exceto na raiz de uma unidade (C:\), que é mantida. Resolve segmentos . e .., trata / e \ da mesma forma e reduz separadores repetidos. Não resolve junções nem links simbólicos. Lança ArgumentException para uma raiz nula ou em branco.
string IdentityKey(string root)
NormalizeRoot(root) em maiúsculas. Grafias lexicalmente equivalentes de uma mesma raiz (C:\OMSI, C:\OMSI\, C:\OMSI\., C:\foo\..\OMSI, c:\omsi) compartilham uma chave; raízes diferentes (C:\OMSI-A, C:\OMSI-B) nunca compartilham.
bool TryGetContainedRelativePath(string root, string candidate, out string relativePath)
Resolve candidate (relativo a root, ou absoluto) e retorna true somente quando ele está estritamente abaixo de root; relativePath é a grafia canônica com \. Usa os segmentos de Path.GetRelativePath, de modo que um irmão como C:\OMSI-A\x nunca está dentro de C:\OMSI; a própria raiz, outros volumes e escapes com .. retornam 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"
Estabilidade: EXPERIMENTAL. Estes tipos compilam um perfil de sessão YAML (<root>\.omsilaunch\session-profiles\<id>\profile.yaml, schema omsilaunch.session-profile/v1) em um LaunchSpec. O comando da CLI /predefined-profile:<id> /predefined-profile-index:<n> usa exatamente estas chamadas; um integrador pode usá-las para iniciar um perfil pela API.
Membro
Comportamento
SessionProfileCompiler.Load(string installationRoot, string id, int presetIndex) → SessionProfilePackage
Lê e valida o pacote. id deve ser um nome de diretório simples (caso contrário, OL_E_SESSION_PROFILE_PATH_ESCAPE); presetIndex é 1..5 (OL_E_SESSION_PROFILE_PRESET_NOT_FOUND). Arquivo ausente: OL_E_SESSION_PROFILE_NOT_FOUND; maior que MaxBytes (256 KiB), YAML inválido, âncoras, chaves desconhecidas ou um id diferente do nome do diretório: OL_E_SESSION_PROFILE_INVALID; outro schema: OL_E_SESSION_PROFILE_SCHEMA_UNSUPPORTED. Os caminhos de assets ficam confinados ao pacote (OL_E_SESSION_PROFILE_PATH_ESCAPE, OL_E_SESSION_PROFILE_ASSET_MISSING). Toda falha é uma SessionProfileException.
Retorna baseline com o perfil aplicado: para NewMap, o bloco new do perfil (mapa, ponto de entrada e qualquer data/hora/ano/clima, que tornam o plano não apto para execução nesta build); as settings da predefinição mescladas sobre Environment.General; Presentation, InternetTextures e Behavior da predefinição, quando presentes; e SessionProfile = profile.Metadata. Para NewMap, também verifica o mapa em relação à lista compatibility do perfil (OL_E_SESSION_PROFILE_MAP_MISMATCH).
A verificação de compatibilidade para os demais modos (para SavedSituation, o mapa é lido do .osn). A CLI a chama depois que o WorldSpec final é construído.
IOException com Code (um dos códigos OL_E_SESSION_PROFILE_*); a mensagem é "<code>: <message>".
A CLI também rejeita flags de linha de comando que conflitam com o perfil (OL_E_SESSION_PROFILE_OVERRIDE_CONFLICT); essa verificação não faz parte do compilador. Consulte perfis de sessão para a ordem completa de mesclagem.
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 do parâmetro do construtor de OmsiLaunchService
Detecta a plataforma, verifica a possibilidade de escrita, inicia, observa, encerra e aguarda Omsi.exe. Passe new CurrentWindowsX64Platform(); implementá-lo por conta própria não é suportado.
CurrentWindowsX64Platform
STABLE_BETA
A única implementação: host Windows x64, CreateProcessW para Omsi.exe, TerminateProcess para a parada canônica. Seus métodos são chamados pelo serviço; integradores apenas o constroem. Membros (compartilhados com IRuntimePlatform): Detect(root) retorna o RuntimePlatformInfo do plano; ValidateCurrent(info) lança OL_E_UNSUPPORTED_OPERATING_SYSTEM / OL_E_UNSUPPORTED_OS_ARCHITECTURE / OL_E_PLATFORM_CAPABILITY_MISSING quando o host não consegue executar uma sessão; IsInstallationWritable(root) fundamenta OL_E_INSTALLATION_NOT_WRITABLE; StartAsync(request, sha256) cria Omsi.exe e registra a identidade do processo (PID, horário de criação, caminho e o hash calculado pelo serviço; OL_E_PROCESS_START_FAILED, OL_E_PROCESS_CREATION_TIME_FAILED); HasExited, WaitForExitAsync e Terminate o observam e o encerram.
Públicos no assembly porque o serviço e os testes os compartilham. Não são uma superfície de integração; LaunchedProcess encapsula internamente os handles de processo e de thread do OMSI (eles não são membros públicos) e nunca é retornado por IOmsiLaunch.
OmsiLaunchService é seguro para chamadas simultâneas em sessões diferentes: as sessões ficam em um ConcurrentDictionary e toda alteração por sessão acontece sob o lock privado da sessão.
Chamadas simultâneas na mesma sessão são seguras, mas serializadas onde isso importa: ExecuteRuntimeAsync adquire um gate por sessão, de modo que um segundo comando aguarda o primeiro (seu timeout começa quando ele é colocado no mailbox).
O supervisor é executado em uma tarefa do pool de threads (Task.Run) desde o momento em que StartSessionAsync retorna até que a sessão seja terminal; ele consulta a telemetria e o processo a cada 100 ms. Os chamadores nunca executam código do supervisor.
StopAsync e GetStatusAsync são concluídos de forma síncrona e podem ser chamados de qualquer thread, inclusive dentro de um handler de ProcessExit (a CLI faz isso com um orçamento de 4 s).
Nenhuma chamada da API tem afinidade de thread; nenhuma exige um contexto de sincronização.
Nenhum IntPtr, nint, handle Win32, endereço nativo, ponteiro de VMT ou objeto de processo. Valores de resultado cujas chaves começam com internal_ ou terminam com _address, _pointer, _vmt são removidos antes que um resultado saia de ExecuteRuntimeAsync.
Nenhuma operação de runtime internal.*: internal.road-vehicles.make-basic é InternalOnly no registro e retorna OL_E_RUNTIME_OPERATION_UNKNOWN pela API e pela CLI.
Nenhuma leitura ou escrita bruta da memória do OMSI, nenhum acesso em nível de arquivo à instalação além do que um LaunchSpec declara.
Nenhum handle entre processos: o plano de controle local é a única rota entre processos, e ele aceita somente session.status, session.events, session.stop e runtime.execute.
Nenhum desligamento cooperativo do OMSI, nenhum LAST_MAP_STATE, nenhuma aplicação de data/hora/clima/veículo do jogador, nenhum overlay de documento de teclado/controle nesta build.
Conteúdo da lista de GetCapabilitiesAsync, membros de especificação de data/hora/clima/veículo do jogador/entrada, DiagnosticsSpec, ExpectedExecutableSha256, RestoreConfiguration, ShutdownTimeoutSeconds
PARTIAL
RuntimeCommandWire, StartupHandoff, StartupHandoffWire, implementações de IRuntimePlatform, todos os assemblies de implementação