# Referência da API pública (`OmsiLaunch.Api`)
> Tradução da [página original em inglês](https://github.com/lmonteirotech/OmsiLaunch/blob/v0.1.0-beta.3/docs/reference/public-api.md) 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](https://omsilaunch.omsimods.com.br/docs/reference/public-api-inventory/index.md) 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.
Páginas relacionadas: [referência do LaunchSpec](https://omsilaunch.omsimods.com.br/docs/reference/launchspec/index.md), [códigos de erro](https://omsilaunch.omsimods.com.br/docs/reference/errors/index.md), [ciclo de vida da sessão](https://omsilaunch.omsimods.com.br/docs/concepts/session-lifecycle/index.md), [transações e recuperação](https://omsilaunch.omsimods.com.br/docs/concepts/transactions-and-recovery/index.md), [controle de runtime](https://omsilaunch.omsimods.com.br/docs/reference/runtime-control/index.md), [capacidades](https://omsilaunch.omsimods.com.br/docs/reference/capabilities/index.md), [plano de controle local](https://omsilaunch.omsimods.com.br/docs/reference/local-control/index.md), [códigos de saída](https://omsilaunch.omsimods.com.br/docs/reference/exit-codes/index.md), [status da validação em runtime](https://omsilaunch.omsimods.com.br/docs/status/runtime-validation-status/index.md).
## Vocabulário de estabilidade
| Nível | Significado nesta página |
| --- | --- |
| `STABLE_BETA` | 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. |
## Visão geral dos assemblies
| Assembly | Papel para integradores |
| --- | --- |
| `OmsiLaunch.Api` | 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. |
| `OmsiLaunch.Configuration`, `OmsiLaunch.Content`, `OmsiLaunch.Interop`, `OmsiLaunch.Plugin`, `OmsiLaunch.Builds.Omsi23004` | Assemblies de implementação. Seus tipos públicos são `INTERNAL` para integradores. |
## Ponto de entrada: `OmsiLaunchService` e `OmsiLaunchRuntimePaths`
```csharp
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, é `\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: `\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](https://omsilaunch.omsimods.com.br/docs/concepts/permanent-plugin/index.md)). A CLI constrói o serviço exatamente assim (`tools/OmsiLaunch.Cli/Program.cs`):
```csharp
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`.
## Regras de propriedade da sessão
| Regra | Detalhe |
| --- | --- |
| Um proprietário por instalação | `StartSessionAsync` adquire o lease da instalação, um semáforo nomeado `Local\OmsiLaunch.Installation.`, 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](https://omsilaunch.omsimods.com.br/docs/reference/local-control/index.md), 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`. |
## `IOmsiLaunch`
```csharp
public interface IOmsiLaunch
{
Task PlanSessionAsync(LaunchSpec spec, CancellationToken cancellationToken = default);
Task StartSessionAsync(SessionPlan plan, CancellationToken cancellationToken = default);
Task GetStatusAsync(SessionHandle session, CancellationToken cancellationToken = default);
Task WaitForAsync(SessionHandle session, SessionState state, TimeSpan timeout, CancellationToken cancellationToken = default);
Task StopAsync(SessionHandle session, CancellationToken cancellationToken = default);
Task CloseAsync(SessionHandle session, CancellationToken cancellationToken = default);
Task ExecuteRuntimeAsync(SessionHandle session, RuntimeCommand command, TimeSpan timeout, CancellationToken cancellationToken = default);
Task> GetCapabilitiesAsync(InstallationSpec installation, CancellationToken cancellationToken = default);
Task> DiscoverAsync(InstallationSpec installation, ContentQueryKind query, OptionalValue scope = default, CancellationToken cancellationToken = default);
Task RecoverPendingAsync(InstallationSpec installation, bool restore, CancellationToken cancellationToken = default);
}
```
Fatos comuns a todos os métodos:
- 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](https://omsilaunch.omsimods.com.br/docs/reference/compatibility/index.md).
### Exemplo mínimo completo
```csharp
var none = new Dictionary>();
var spec = new LaunchSpec(
Installation: new InstallationSpec(@"C:\OMSI 2"),
World: new WorldSpec(WorldMode.NewMap, OptionalValue.Set(@"maps\Grundorf\global.cfg"), OptionalValue.Unset, OptionalValue.Set(1)),
Date: new DateSpec(DateTimeMode.Unset, OptionalValue.Unset),
Time: new TimeSpec(DateTimeMode.Unset, OptionalValue.Unset),
PlayerVehicle: OptionalValue.Unset,
Environment: new EnvironmentSpec(none, none, none, none, none, none, none, none),
Behavior: new LaunchBehaviorSpec());
var plan = await launch.PlanSessionAsync(spec);
if (!plan.IsRunnable) { foreach (var d in plan.Diagnostics) Console.WriteLine($"{d.Code}: {d.Message}"); return; }
var session = await launch.StartSessionAsync(plan);
try
{
var status = await launch.WaitForAsync(session, SessionState.Running, TimeSpan.FromSeconds(plan.Spec.Behavior.StartupTimeoutSeconds + 5));
if (status.State == SessionState.Running)
{
var time = await launch.ExecuteRuntimeAsync(session, new RuntimeCommand(session.SessionId, 1, "time.read"), TimeSpan.FromSeconds(5));
Console.WriteLine(time.Succeeded ? $"{time.Values!["hour"]}:{time.Values["minute"]}" : time.ErrorCode);
await launch.StopAsync(session);
}
var final = await launch.WaitForAsync(session, SessionState.Completed, Timeout.InfiniteTimeSpan);
Console.WriteLine(final.State); // Completed, or Failed with diagnostics
}
finally
{
await launch.CloseAsync(session); // always
}
```
### `PlanSessionAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task PlanSessionAsync(LaunchSpec spec, CancellationToken cancellationToken = default)` |
| Finalidade | 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](https://omsilaunch.omsimods.com.br/docs/reference/launchspec/index.md)). `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](https://omsilaunch.omsimods.com.br/docs/reference/launchspec/index.md#validation-rules-and-non-runnable-diagnostics). |
| 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)));` |
### `StartSessionAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task StartSessionAsync(SessionPlan plan, CancellationToken cancellationToken = default)` |
| Finalidade | 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: ")` 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](https://omsilaunch.omsimods.com.br/docs/concepts/session-lifecycle/index.md)). |
| 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\-host.log` (retenção: as 50 sessões mais recentes), `.omsilaunch\journal.json`, `.omsilaunch\backup\\*.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](https://omsilaunch.omsimods.com.br/docs/concepts/transactions-and-recovery/index.md). |
| 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);` |
### `GetStatusAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task GetStatusAsync(SessionHandle session, CancellationToken cancellationToken = default)` |
| Finalidade | 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}");` |
### `WaitForAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task WaitForAsync(SessionHandle session, SessionState state, TimeSpan timeout, CancellationToken cancellationToken = default)` |
| Finalidade | 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](https://omsilaunch.omsimods.com.br/docs/concepts/session-lifecycle/index.md)) 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 */ }` |
### `StopAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task StopAsync(SessionHandle session, CancellationToken cancellationToken = default)` |
| Finalidade | 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));` |
### `CloseAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task CloseAsync(SessionHandle session, CancellationToken cancellationToken = default)` |
| Finalidade | 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`. |
| Estabilidade | `STABLE_BETA`. |
| Exemplo | `try { ... } finally { await launch.CloseAsync(session); }` |
### `ExecuteRuntimeAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task ExecuteRuntimeAsync(SessionHandle session, RuntimeCommand command, TimeSpan timeout, CancellationToken cancellationToken = default)` |
| Finalidade | 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](https://omsilaunch.omsimods.com.br/docs/reference/runtime-control/index.md) e [capacidades](https://omsilaunch.omsimods.com.br/docs/reference/capabilities/index.md). |
| 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](https://omsilaunch.omsimods.com.br/docs/reference/runtime-control/index.md)). |
| 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](https://omsilaunch.omsimods.com.br/docs/reference/errors/index.md). |
| 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--`) 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 { ["handle"] = "rv-000001" }), TimeSpan.FromSeconds(5)); if (!r.Succeeded) Console.WriteLine($"{r.ErrorCode} {r.Values?["detail"]}");` |
### `GetCapabilitiesAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task> GetCapabilitiesAsync(InstallationSpec installation, CancellationToken cancellationToken = default)` |
| Finalidade | 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. |
| Retorno | 51 entradas, por exemplo `runtime.time.read` (`RUNTIME_VALIDATED`), `runtime.weather.write` (`false`, `RUNTIME_PARTIAL`), `world.last-map-state` (`false`, `UNSUPPORTED_FOR_CURRENT_PROFILE`), `world.date.explicit` (`false`, `STATICALLY_PARTIAL`), `content.maps` (`STATICALLY_VALIDATED`), `runtime.d3d.lifecycle.reset` (`IMPLEMENTED_NOT_RUNTIME_VALIDATED`). |
| Diferença em relação a `PublicCapabilityRegistry` | `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}");` |
### `DiscoverAsync`
| Aspecto | Detalhe |
| --- | --- |
| Assinatura | `Task> DiscoverAsync(InstallationSpec installation, ContentQueryKind query, OptionalValue scope = default, CancellationToken cancellationToken = default)` |
| Finalidade | 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.Set(maps[0].Identity));` |
Valores e resultados de `ContentQueryKind`:
| Valor | Escopo | `Identity` | `Kind` | `DisplayName` |
| --- | --- | --- | --- | --- |
| `Maps` | nenhum | `maps\\global.cfg` | `map` | nome do diretório do mapa |
| `Situations` | nenhum | `situations\...\.osn` | `situation` | identidade do mapa referenciado pelo `.osn` (pode ser `null`) |
| `Vehicles` | nenhum | `Vehicles\...\.bus` | `vehicle` | `[friendlyname]` ou nome do arquivo |
| `Repaints` | identidade do veículo (obrigatória; sem ela: lista vazia) | `#item:` | `repaint` | nome de `[item]` |
| `Hofs` | nenhum | `Vehicles\...\.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) | `registration_automatic` / `registration_list` / `registration_free` | `registration` | primeira linha de valor (`null` para free) |
| `Addons` | nenhum | `Addons\` | `addon` | `directory-only` |
| `Entrypoints` | identidade do mapa (obrigatória; sem ela: `ArgumentException`) | `