QwenCloudBRASIL
Tradução de apoio para consulta. Código e identificadores técnicos preservados. Consultar referência original ↗

HappyOyster Referência da API do SDK do iOS

O HappyOyster O ponto de entrada do iOS SDK é o singleton em nível de processo HappyOysterEngine.shared . Os métodos de negócios são todos lançamentos assíncronos, lançando OysterSDKError em caso de falha. Uma única experiência é realizada por um identificador OysterTravel, abrangendo três modos – aventura, direção e atuação – no UIKit e no SwiftUI.

1. Conceitos Básicos

Alguns termos primeiro, para facilitar a leitura do resto.

<table><colgroup><col style={{ width: "18.518519%" }} /><col style={{ width: "81.481481%" }} /></colgroup><thead><tr><th> Conceito </th><th> Descrição </th></tr></thead><tbody><tr><td> ficha </td><td> Token de autenticação HTTP (QwenCloud chave de API temporária). Seu servidor troca-o através do QwenCloud API e entrega-a; é injetado através <code>atualizarToken(_:)</code>, e o SDK o transporta como um portador HTTP ao solicitar o gateway. O SDK mantém apenas o mais recente, não o persiste nem o atualiza; depois de expirar, você troca novamente e injeta novamente. </td></tr><tr><td> bilhete </td><td> Credencial de teste único. Seu servidor troca através da plataforma aberta <code>obter credencial de viagem</code> (prefixo <code>tk_</code>, válido para 30 minutos, uso único), usado como <code>criarViagem(bilhete:)</code> argumento; torna-se inválido quando a experiência termina ou expira e não pode ser reutilizado. </td></tr><tr><td> Mundo </td><td> Um mundo de IA, contendo personagens e cenas. Criado e gerenciado pelo seu servidor; o SDK não está envolvido. </td></tr><tr><td> Viajar </td><td> Uma única experiência em tempo real, correspondente a um <code>OysterTravel</code> manusear. Uso único; invalidado quando um estado terminal é alcançado, exigindo um novo <code>criarViagem</code> através do motor. </td></tr><tr><td> Status da sessão </td><td> <code>OysterTravelStatus</code>: <code>inativo → preparar → em execução → pausando → pausado</code> (pausado retorna para <code>preparar → correr</code> via reconexão), mais dois estados terminais <code>terminou</code> / <code>falhou</code> (ver §7). </td></tr><tr><td> Modo </td><td> <code>aventura</code> (comandos de direção/visão/ação), <code>dirigir</code> (enredo baseado em texto), ou <code>atuando</code> (desempenho de personagem baseado em texto). O <code>modo</code> retornado por <code>início()</code> determina a IU de acordo; consulte §2 para saber o que cada modo suporta. </td></tr></tbody></table>

2. Visão geral

Depois de integrar este SDK, seu aplicativo pode entrar em "mundos" gerados em tempo real pela IA e ter uma experiência de vídeo interativa em tempo real em três modos — aventura, direção ou atuação: iniciar a experiência → reprodução em tempo real → interação em tempo real → controle de processo (pausar/retomar/retroceder/finalizar) → retornos de chamada de status e erro.

Cada modo oferece suporte a um conjunto diferente de recursos. Direcione sua IU a partir do mode retornado por start():

<table><colgroup><col style={{ width: "40%" }} /><col style={{ width: "20%" }} /><col style={{ width: "20%" }} /><col style={{ width: "20%" }} /></colgroup><thead><tr><th> Capacidade </th><th> <code>aventura</code> </th><th> <code>dirigir</code> </th><th> <code>atuando</code> </th></tr></thead><tbody><tr><td> <code>enviarComando</code> (direção/visão/ação) </td><td> Sim </td><td> Não </td><td> Não </td></tr><tr><td> <code>enviarInstruir</code> (instrução de texto) </td><td> Não </td><td> Sim </td><td> Sim </td></tr><tr><td> <code>pausa()</code> / <code>currículo()</code> </td><td> Não </td><td> Sim </td><td> Sim </td></tr><tr><td> <code>retroceder (para segundos:)</code> </td><td> Não </td><td> Sim </td><td> Não – oculte o ponto de entrada </td></tr><tr><td> <code>fim()</code> </td><td> Sim </td><td> Sim </td><td> Sim </td></tr><tr><td> <code>iniciar(maxExperienceTimeSec:)</code> </td><td> Aplicado </td><td> Ignorado </td><td> Ignorado </td></tr></tbody></table>

acting os mundos priorizam o retrato: start() retorna um aspectRatio (9:16 / 16:9) para a sessão. Use-o para escolher a orientação do player e o tamanho do contêiner antes de puxar o fluxo (consulte §8).

import HappyOysterSDK; os principais pontos de entrada são de dois tipos.

HappyOysterEngine — o ponto de entrada da orquestração, um singleton em nível de processo HappyOysterEngine.shared.

<table><colgroup><col style={{ width: "40%" }} /><col style={{ width: "60%" }} /></colgroup><thead><tr><th> Método/Propriedade </th><th> Descrição </th></tr></thead><tbody><tr><td> <code>inicializar(configuração:)</code> </td><td> Inicialize o tempo de execução e registre automaticamente o mecanismo em tempo real (uma vez, antes <code>criarViagem</code>) </td></tr><tr><td> <code>atualizarToken(_:)</code> </td><td> Injetar/atualizar o token de autenticação HTTP </td></tr><tr><td> <code>criarViagem(bilhete:)</code> </td><td> Crie um identificador de sessão com uma credencial única </td></tr><tr><td> <code>limpeza()</code> </td><td> Liberar recursos (pode <code>inicializar</code> novamente) </td></tr><tr><td> <code>versão</code> </td><td> A versão atual do SDK </td></tr></tbody></table>

OysterTravel — o identificador de sessão para uma experiência, criado por createTravel(ticket:).

<table><colgroup><col style={{ width: "40%" }} /><col style={{ width: "60%" }} /></colgroup><thead><tr><th> Método/Propriedade </th><th> Descrição </th></tr></thead><tbody><tr><td> <code>vídeoVer</code> / <code>OysterVideoView(viagem:)</code> </td><td> Visualização de reprodução (UIKit/SwiftUI) </td></tr><tr><td> <code>eventos</code> / <code>estado</code> </td><td> Mudança de status + fluxo de push de erro / status atual observável </td></tr><tr><td> <code>início()</code> / <code>iniciar(maxExperienceTimeSec:)</code> </td><td> Conecte-se e jogue (opcionalmente, solicite a duração máxima de uma experiência de aventura) </td></tr><tr><td> <code>enviarInstruct(conteúdo:)</code> </td><td> Instruções de texto no modo de direção </td></tr><tr><td> <code>enviarComando(_:)</code> / <code>flushCommands()</code> </td><td> Controle do modo aventura </td></tr><tr><td> <code>pausa()</code> / <code>currículo()</code> </td><td> Pausar/retomar (<code>dirigir</code> e <code>atuando</code>) </td></tr><tr><td> <code>retroceder (para segundos:)</code> </td><td> Retroceder (apenas pausado) </td></tr><tr><td> <code>fim()</code> </td><td> Fim (idempotente, certifique-se de chamá-lo) </td></tr><tr><td> <code>pausaLocalAudioCapture()</code> / <code>retomarLocalAudioCapture()</code> </td><td> Ceder/restaurar temporariamente o microfone </td></tr></tbody></table>

Existem também algumas exportações auxiliares: OysterLog por assumir o registro interno do SDK; HappyOysterEngine.version para ler a versão; OysterVideoView é a visualização de reprodução do SwiftUI (equivalente a videoView). O uso é descrito posteriormente.

Ciclo de vida: inicializar → injetar token → criar sessão → montar vídeo + inscrever-se em eventos → iniciar a reprodução → interagir → finalizar. O SDK não é responsável pela criação ou gerenciamento de mundos (feito pelo seu servidor), nem expõe detalhes de comunicação de baixo nível em tempo real.

3. Início rápido

Um fluxo completo da inicialização ao final, com comentários passo a passo. Os detalhes de cada API estão em §6.

import HappyOysterSDK

// 1) Initialize (as early as possible after app launch, once before createTravel)
let engine = HappyOysterEngine.shared
engine.initialize(config: OysterConfig(
    apiHost: "maas.qwencloudapi.com",// API Host (maas.qwencloudapi.com)
    model: "happyoyster-1.0-adventure"                  // Versioned model name enabled for your account, required; see the Happy Oyster model documentation
))

// 2) (Optional) Take over logging: set level + custom output
OysterLog.setMinimumLevel(.info)
OysterLog.setHandler { level, tag, message in
    print("[Oyster][\(level)][\(tag)] \(message)")
}

// 3) Inject the HTTP auth token (QwenCloud temporary API Key, delivered by your server)
engine.updateToken(temporaryApiKey)

// 4) Create a session handle with a one-time ticket (not yet connected)
let travel = try engine.createTravel(ticket: ticket)

Task { @MainActor in
    // 5) Mount the video (UIKit; for SwiftUI use OysterVideoView(travel:))
    containerView.addSubview(travel.videoView)

    // 6) Subscribe to events before start, to avoid missing early statuses
    let eventTask = Task {
        for await event in travel.events {
            switch event {
            case .statusChanged(let status): render(status)   // §7 status table
            case .error(let error):          handle(error)     // §9 error.code / error.kind
            }
        }
    }

    do {
        // 7) Connect and play
        let data = try await travel.start()

        // 8) Interact based on mode
        if data.mode == .directing {
            _ = try await travel.sendInstruct(content: "Suddenly it starts to pour")
        } else {
            travel.sendCommand(OysterAdventureCommand(translation: .front))
        }
    } catch let error as OysterSDKError {
        handle(error)
    }

    // 9) Funnel every exit path into a single end()
    _ = try? await travel.end()
    eventTask.cancel()
}

// Exit the SDK / switch gateway: await engine.cleanup()

Para configuração do projeto, configuração de dependência (incluindo o adaptador AliRTC necessário e o binário do fornecedor), coordenação do lado do servidor e uma execução ponta a ponta, consulte a documentação do projeto de amostra.

4. Requisitos

<table><colgroup><col style={{ width: "25%" }} /><col style={{ width: "75%" }} /></colgroup><thead><tr><th> Artigo </th><th> Requisito </th></tr></thead><tbody><tr><td> SO mínimo </td><td> iOS 15.0+ (todos os tipos públicos estão marcados <code>@disponível(iOS 15.0, *)</code>) </td></tr><tr><td> Idioma </td><td> Rápido (<code>assíncrono/aguardar</code>) </td></tr><tr><td> Simultaneidade </td><td> Acesso ao thread principal (os tipos de entrada são marcados <code>@MainActor</code>) </td></tr><tr><td> Rede </td><td> É necessário acesso à rede pública </td></tr><tr><td> Permissões </td><td> <code>Informações.plist</code> deve incluir <code>NSMicrofoneUsoDescrição</code> (veja abaixo) </td></tr></tbody></table>

Permissão de microfone: uma experiência de vídeo interativa em tempo real precisa de um canal de áudio/vídeo bidirecional (uplink + downlink) em tempo real, para que o SDK ocupe o microfone local durante a execução — isso não é gravação. Info.plist deve fornecer NSMicrophoneUsageDescription, caso contrário, iniciar a captura em tempo real travará. Quando você precisar de acesso exclusivo ao microfone (por exemplo, reconhecimento de fala), use pauseLocalAudioCapture() / resumeLocalAudioCapture() ceder e restaurá-lo temporariamente (ver §6.2).

5. Integração e Autenticação

5.1 Integração e Dependências

import HappyOysterSDK é tudo que você precisa no nível do código. O SDK é distribuído como um binário pré-compilado (xcframework) por meio de subespecificações do CocoaPods, publicado no CocoaPods Trunk público — declare as dependências em seu Podfile:

# HappyOysterSDK / AliVCSDK_ARTC are both published on the public CocoaPods source.
pod 'HappyOysterSDK', '1.0.3'              # Aggregate entry point (Core + World)
pod 'HappyOysterSDK/UI', '1.0.3'           # Optional default UI components (video view, control HUD)
pod 'HappyOysterSDK/StreamAliRTC', '1.0.3' # Video stream + AliRTC engine adapter (already depends on Stream)

# RTC vendor binary: weak-linked by the SDK, not redistributed with it — bring your own.
pod 'AliVCSDK_ARTC', '7.11.0'

AliVCSDK_ARTC é obrigatório sempre que você entra HappyOysterSDK/StreamAliRTC: se estiver faltando, o SDK volta silenciosamente para Loopback — ele pode se conectar e alcançar running, mas mostra uma tela preta sem erros.

A configuração do projeto e o fluxo de inicialização ponta a ponta são gerenciados pelo projeto de amostra; consulte o exemplo de documentação do projeto. Este documento se concentra na própria interface.

5.2 Modelo de autenticação

O SDK não obtém nem atualiza tokens, mantendo as coisas leves. A autenticação possui duas camadas e o integrador gerencia seus ciclos de vida:

  1. Token de autenticação HTTP (QwenCloud chave de API temporária): seu servidor a troca por meio do QwenCloud API e entrega-a; é injetado através updateToken(_:). Alguns serviços internos do SDK chamam o QwenCloud gateway diretamente, carregando esse token para autenticação — portanto, deve ser uma chave API temporária emitida por QwenCloud, não um token do seu próprio serviço comercial. O SDK mantém apenas o mais recente, não o persiste nem o atualiza; depois de expirar, você troca novamente e injeta novamente.
  2. Credencial de avaliação única ticket: Seu servidor troca através da plataforma aberta get-travel-credential (prefixo tk_, válido por 30 minutos, uso único), usado como createTravel(ticket:) argumento; torna-se inválido quando a experiência termina (normal ou anormalmente) ou expira e não pode ser reutilizado.

AK, chaves de assinatura e outras credenciais de alto privilégio existem apenas no seu servidor; o SDK do cliente nunca os toca. O que o cliente recebe é sempre uma chave de API temporária de curta duração.

Feature Gate (troca remota/atualização forçada): O servidor pode desabilitar remotamente o SDK ou definir uma versão mínima suportada. Enquanto desativadas, as chamadas bloqueadas (start / pause / resume / rewind / sendInstruct / sendCommand) são rejeitados com 108001 e qualquer experiência a bordo será encerrada pelo SDK (ver §9). OysterSDKError.raw carrega uma razão legível por humanos; solicitar que o usuário atualize quando a versão for muito baixa.

Tratamento da expiração do token: depois que o token de autenticação HTTP acima expirar, solicite imediatamente um novo token do seu servidor e injete-o via updateToken(_:). Existem dois locais onde você precisa determinar se o token expirou:

  1. Ao chamar APIs como engine.createTravel ou travel.start, trate o erro e verifique os tipos de erro de token expirado/inválido (101001 / 101002); após injetar um novo token, chame novamente a API correspondente.
  2. Ao ouvir o .error evento de OysterTravelEvent, verifique os tipos de erro de token expirado/inválido, solicite novamente o token e injete-o.

6. Referência da API

Existem dois tipos de entrada, ambos @MainActor e @available(iOS 15.0, *). Os métodos de negócios são async throws e jogue OysterSDKError em caso de falha; métodos com valores de retorno estão todos marcados @discardableResult.

6.1 HappyOysterEngine

Um singleton no nível do processo, o ponto de entrada da orquestração. init não é público - sempre use HappyOysterEngine.shared; não instancie você mesmo (o mecanismo de tempo real subjacente também é um processo singleton).

@MainActor @available(iOS 15.0, *)
public final class HappyOysterEngine {
    public static let shared: HappyOysterEngine            // the unique process-level instance
    public static let version: String                      // current SDK version

    @discardableResult
    public func initialize(config: OysterConfig) -> Bool  // initialize (once before createTravel)
    public func updateToken(_ token: String)               // inject/update the HTTP auth token
    public func createTravel(ticket: String) throws -> OysterTravel  // create a session handle with a one-time credential
    public func cleanup() async                            // release resources (can initialize again)
}

initialize(config:)

OysterLog

updateToken(_:)

createTravel(ticket:)

cleanup()

6.2 OysterTravel

O identificador de sessão criado por createTravel; uso único, invalidado quando um estado terminal (end / fim do servidor / falha) é atingido, exigindo uma nova createTravel através do motor. É também um ObservableObject (@Published status, pode conduzir diretamente o SwiftUI).

@MainActor @available(iOS 15.0, *)
public final class OysterTravel: ObservableObject {
    @Published public private(set) var status: OysterTravelStatus   // current external status (observable)
    public var isEnded: Bool { get }                                // whether a terminal state is reached (synchronously readable)
    public var videoView: UIView { get }                            // UIKit playback view; for SwiftUI use OysterVideoView(travel:)
    public var events: AsyncStream<OysterTravelEvent> { get }       // status change + error, multi-subscribe

    @discardableResult public func start() async throws -> OysterStartTravelData   // connect and play
    @discardableResult public func start(maxExperienceTimeSec: Int?) async throws -> OysterStartTravelData  // connect and play + request max experience duration (adventure mode only)
    @discardableResult public func pause() async throws -> OysterTravelStateData   // pause (directing / acting)
    @discardableResult public func resume() async throws -> OysterTravelStateData  // resume
    @discardableResult public func rewind(toSec: TimeInterval) async throws -> OysterRewindTravelData  // rewind (paused only)
    @discardableResult public func end() async throws -> OysterEndTravelData       // end (idempotent, be sure to call)
    @discardableResult public func sendInstruct(content: String) async throws -> OysterSendInstructData  // directing-mode text instruction

    public func sendCommand(_ command: OysterAdventureCommand)      // adventure-mode control (fire-and-forget)
    public func flushCommands()                                     // manually send an already-submitted pending command
    public func pauseLocalAudioCapture() async                      // temporarily yield the microphone
    public func resumeLocalAudioCapture() async                     // restore microphone occupation
}

Visualização do vídeo videoView / OysterVideoView(travel:)

Eventos e status events / status / isEnded

start() / start(maxExperienceTimeSec:)

pause() / resume()

rewind(toSec:)

end()

sendInstruct(content:) (modo de direção)

sendCommand(_:) / flushCommands() (modo aventura)

pauseLocalAudioCapture() / resumeLocalAudioCapture()

7. Eventos e status

Os eventos estão anexados a OysterTravel.events e são o canal push ativo do SDK para você, usados para revelar situações não acionadas por suas próprias chamadas (por exemplo, problemas com a conexão em tempo real gerenciada internamente ou pesquisa de status).

var events: AsyncStream<OysterTravelEvent> { get }

@available(iOS 15.0, *)
public enum OysterTravelEvent {
    case statusChanged(OysterTravelStatus)
    // The SDK's internal flow errored, e.g. an internal API request error or a streaming error; note you must check for token expiration here and re-request the token
    case error(OysterSDKError)
}

Existem dois estilos de consumo, escolha um:

let task = Task {
    for await event in travel.events {
        switch event {
        case .statusChanged(let status): render(status)   // see table below
        case .error(let error):          handle(error)     // error.code / error.kind, see §9
        }
    }
}
// When done: task.cancel()

OysterTravelStatus (5 estados de processo + 2 estados terminais):

<table><colgroup><col style={{ width: "16.666667%" }} /><col style={{ width: "50%" }} /><col style={{ width: "33.333333%" }} /></colgroup><thead><tr><th> Estado </th><th> Descrição </th><th> Manuseio típico </th></tr></thead><tbody><tr><td> <code>ocioso</code> </td><td> depois de criar, antes de começar </td><td> — </td></tr><tr><td> <code>preparar</code> </td><td> conectar/reconectar (conectar/reconectar internamente) </td><td> mostrar dica de conexão/reconexão </td></tr><tr><td> <code>correndo</code> </td><td> pronto para transmissão, interativo (reprodução interna) </td><td> mostrar imagem e controles </td></tr><tr><td> <code>pausando</code> </td><td> pausa aceita, aguardando confirmação do servidor </td><td> mostrar "pausando…" </td></tr><tr><td> <code>pausado</code> </td><td> pausado (confirmado) </td><td> mostrar estado de pausa (somente <code>dirigir</code> mostra a entrada de retrocesso) </td></tr><tr><td> <code>terminou</code> </td><td> finalizado (extremidade ativa ou extremidade do lado do servidor). Terminais </td><td> encerrar e fechar a página </td></tr><tr><td> <code>falhou</code> </td><td> falhou. Terminais </td><td> mostrar erro e finalizar </td></tr></tbody></table>

Depois de entrar ended / failed, a sessão foi encerrada e todas as operações da sessão (pause/resume/sendCommand…) não terão mais efeito. Os retornos de chamada podem ser acionados no thread principal, para que você possa atualizar a IU diretamente.

Durante a pesquisa de status interna, o SDK relata automaticamente as pulsações do estado de pull/reprodução do cliente (conectando/reproduzindo/pausado/reconectando/desconectado), para que o servidor possa distinguir os estados do lado do fluxo dos do lado do cliente — comportamento puramente interno do SDK, do qual o host não precisa estar ciente ou do qual participar.

8. Modelos de dados

Todos os tipos públicos estão marcados @available(iOS 15.0, *). Os valores/parâmetros de retorno abaixo são saídas do SDK, construídas internamente com tipos Swift nativos (Date / TimeInterval / OysterTravelStatus); eles não Codable e não exponha detalhes do fio (snake_case) — a decodificação do fio acontece dentro do SDK.

// Configuration
public struct OysterConfig: Sendable {
    public let apiHost: String             // QwenCloud gateway URL (not a business server), required, passed explicitly by the host
    public let model: String               // Versioned model name (e.g. "happyoyster-1.0-adventure"), required, no default
    public let logLevel: OysterLogLevel?   // .debug/.info/.warning/.error; defaults to .warning when nil
    public let callbackTimeoutMs: Int      // global callback timeout, default 30000; surfaced as 105005 on timeout
    public init(apiHost: String, model: String, logLevel: OysterLogLevel? = nil, callbackTimeoutMs: Int = 30_000)
}

public enum OysterLogLevel: Int, Comparable, CaseIterable, Sendable {
    case debug, info, warning, error
}

// External session status (§7)
public enum OysterTravelStatus: String, Equatable, Sendable, CustomStringConvertible {
    case idle      // after create, before start
    case prepare   // connecting / reconnecting
    case running   // stream ready, interactive
    case pausing   // pause accepted, awaiting server confirmation
    case paused    // paused (confirmed)
    case ended     // terminal: active end or server-side end
    case failed    // terminal: failed
}

// Open string value (preserves unknown values for server-side extension; encoded/decoded as a bare JSON string "running")
public struct OysterRawValue: RawRepresentable, Equatable, Hashable, Codable, Sendable {
    public let rawValue: String
    public init(rawValue: String) { self.rawValue = rawValue }
}
public typealias OysterModeValue = OysterRawValue
public extension OysterModeValue {
    static let adventure = OysterModeValue(rawValue: "adventure")
    static let directing  = OysterModeValue(rawValue: "directing")
    static let acting     = OysterModeValue(rawValue: "acting")
}

// start() return (SDK output, not Codable)
public struct OysterStartTravelData: Equatable, Sendable {
    public let encryptedTravelId: String  // identifier for this experience
    public let encryptedWorldId: String
    public let mode: OysterModeValue       // adventure / directing / acting
    public let playUrl: String?            // currently always returned as null by the server
    public let firstFrame: String?         // first-frame image URL, may be null (produced asynchronously)
    public let version: String             // world version identifier (diagnostics; drive interaction UI from `mode`)
    public let aspectRatio: String?        // "9:16" / "16:9"; only set for acting worlds, nil otherwise
}

// Control API returns (SDK output, not Codable; status is the external enum OysterTravelStatus)
public struct OysterTravelStateData: Equatable, Sendable { public let encryptedTravelId: String; public let status: OysterTravelStatus }
public struct OysterRewindTravelData: Equatable, Sendable { public let encryptedTravelId: String; public let status: OysterTravelStatus; public let resumedAtSec: TimeInterval }
public struct OysterEndTravelData: Equatable, Sendable { public let encryptedTravelId: String; public let status: OysterTravelStatus; public let endedAt: Date; public let duration: TimeInterval }
public struct OysterSendInstructData: Equatable, Sendable { public let encryptedTravelId: String; public let content: String; public let accepted: Bool }

// Adventure-mode control command (strongly typed enums; values aligned with the internal WorldControlParams)
public struct OysterAdventureCommand: Equatable, Sendable {
    public enum Translation: String { case none, front, back, left, right, frontLeft, frontRight, backLeft, backRight }
    public enum Rotation: String { case none, mouseUp, mouseDown, mouseLeft, mouseRight, mouseUpLeft, mouseUpRight, mouseDownLeft, mouseDownRight }
    public enum Interaction: String { case none, jump, attack, crouch, sprint }
    public let translation: Translation   // movement for this command; submit every frame while held, then stop
    public let rotation: Rotation          // view rotation for this command; submit every frame while held, then stop
    public let interaction: Interaction    // one-shot action for this command; submit once
    public init(translation: Translation = .none, rotation: Rotation = .none, interaction: Interaction = .none)
    public init(_ params: WorldControlParams)   // convenient bridge from the default control WorldControlParams (consistent rawValue)
}

// Unified error (§9)
public struct OysterSDKError: Error {
    public let code: Int
    public let raw: Any?                  // the SDK's internal raw error info; structure is not guaranteed stable, for logging/diagnostics only
    public var kind: OysterErrorKind      // typed view of the numeric code, for exhaustive switch (computed property)
    public init(code: Int, raw: Any? = nil)
    // Error code constants are in the nested OysterSDKError.Code (e.g. .notInitialized = 100001)
}

// Typed semantic view of error codes: local codes are named cases; server-side 4xxxxx/5xxxxx converge to .server(code:)
public enum OysterErrorKind: Equatable, Sendable {
    case notInitialized, tokenMissing, tokenInvalid, noActiveTravel, invalidState, sendCommandInDirecting
    case concurrentTravel, realtimeConnectFailed, realtimeJoinTimeout, firstFrameTimeout
    case channelNotReady, callbackTimeout, localNetwork, responseDecodeFailed
    case streamAutoEnd, proxyOrUnrecognized, featureGateDisabled
    case server(code: Int), unknown(code: Int)
}

9. Códigos de erro

O SDK relata erros uniformemente como OysterSDKError, e o tipo é sempre distinguido por code — não julgue o tipo por "de qual caminho veio o erro": o mesmo code pode ser lançado por um método de negócios (async throws) ou apareceu através do .error de events. Para correspondência digitada, use error.kind (ver §8 OysterErrorKind).

Códigos de erro: servidor 4xxxxx/5xxxxx, cliente-local 1xxxxx.

Códigos de erro do servidor (comuns)

<table><colgroup><col style={{ width: "25%" }} /><col style={{ width: "50%" }} /><col style={{ width: "25%" }} /></colgroup><thead><tr><th> código </th><th> Significado </th><th> Manuseio sugerido </th></tr></thead><tbody><tr><td> <code>400000</code> </td><td> Parâmetros inválidos (valor enum inválido, etc.) </td><td> Verifique os parâmetros da solicitação ou a versão do SDK </td></tr><tr><td> <code>401010</code> </td><td> Credencial de experiência (<code>bilhete</code>) inválido ou expirado </td><td> Faça com que o servidor emita novamente a credencial </td></tr><tr><td> <code>401011</code> </td><td> Credencial de experiência (<code>bilhete</code>) já usado </td><td> Credencial de uso único; reeditar </td></tr><tr><td> <code>403001</code> </td><td> O mundo não existe, foi excluído ou não pertence ao desenvolvedor atual (incluindo um mundo excluído após a emissão da credencial) </td><td> Escolha um mundo válido novamente </td></tr><tr><td> <code>403002</code> </td><td> Estado mundial não está pronto </td><td> Espere o mundo estar pronto antes de começar </td></tr><tr><td> <code>403003</code> </td><td> A API permite apenas a chave de API primária </td><td> Uma chave temporária não pode ser usada para esta API </td></tr><tr><td> <code>403004</code> </td><td> Conteúdo de entrada rejeitado pela moderação de conteúdo; aplica-se a <code>enviarInstruir</code> instruções de texto </td><td> Altere a entrada e tente novamente </td></tr><tr><td> <code>403007</code> </td><td> A especificação de serviço solicitada não está habilitada </td><td> Não tente novamente como se a capacidade estivesse cheia; mudar para uma especificação habilitada (normalmente: a conta não tem especificação de atuação) </td></tr><tr><td> <code>403008</code> </td><td> Configuração de capacidade temporariamente indisponível </td><td> Tente novamente mais tarde </td></tr><tr><td> <code>404000</code> </td><td> O recurso não existe (mundo/wander propriedade ou nenhum artefato) </td><td> Verifique ID/status </td></tr><tr><td> <code>409000</code> </td><td> A solicitação está em conflito com o estado atual do recurso </td><td> Verifique o estado da experiência </td></tr><tr><td> <code>429001</code> </td><td> Limite de simultaneidade atingido para esta especificação </td><td> Tente novamente após o término de uma sessão existente (não confunda com <code>500001</code>) </td></tr><tr><td> <code>429002</code> </td><td> Capacidade disponível insuficiente </td><td> Tente novamente mais tarde </td></tr><tr><td> <code>500000</code> </td><td> Erro interno do sistema </td><td> Tente novamente mais tarde/denuncie </td></tr><tr><td> <code>500001</code> </td><td> Alocação de recursos de inferência ou falha de serviço interno </td><td> Tente novamente mais tarde </td></tr></tbody></table>

Códigos de erro locais do cliente

<table><colgroup><col style={{ width: "16.666667%" }} /><col style={{ width: "33.333333%" }} /><col style={{ width: "16.666667%" }} /><col style={{ width: "33.333333%" }} /></colgroup><thead><tr><th> código </th><th> Significado </th><th> SDK encerra sessão automaticamente </th><th> Manuseio sugerido </th></tr></thead><tbody><tr><td> <code>100001</code> </td><td> Chamado antes da inicialização do SDK; também abrange uma <code>inicializar</code> que não entrou em vigor porque <code>apiHost</code>/<code>modelo</code> estava em branco ou inválido </td><td> Não (lançado de forma síncrona, rejeitando esta chamada) </td><td> <code>inicializar</code> primeiro e verifique ambos <code>apiHost</code> e <code>modelo</code> estão configurados corretamente </td></tr><tr><td> <code>101001</code> </td><td> Token de autenticação HTTP não injetado </td><td> Não </td><td> tente novamente depois <code>atualizarToken</code> </td></tr><tr><td> <code>101002</code> </td><td> Token de autenticação HTTP inválido/rejeitado </td><td> Não </td><td> troque novamente o token e tente novamente <code>atualizarToken</code> </td></tr><tr><td> <code>103001</code> </td><td> Nenhuma experiência ativa atualmente </td><td> Não (rejeitando esta chamada) </td><td> <code>criarViagem</code> + <code>começar</code> primeiro </td></tr><tr><td> <code>103002</code> </td><td> O estado/versão atual não permite esta operação </td><td> Não (rejeitando esta chamada) </td><td> verifique o estado da experiência / <code>versão</code> </td></tr><tr><td> <code>103003</code> </td><td> Incompatibilidade de modo (por ex. <code>enviarComando</code> apelou a um não-<code>aventura</code> mundo) </td><td> Não (rejeitando esta chamada) </td><td> escolha a API certa <code>modo</code>, consulte a tabela de capacidade §2 </td></tr><tr><td> <code>103004</code> </td><td> Criação/início simultâneo de experiência </td><td> Não (lançado de forma síncrona) </td><td> serializar chamadas, <code>fim</code> a sessão antiga primeiro </td></tr><tr><td> <code>105001</code> </td><td> Falha na conexão em tempo real </td><td> Sim </td><td> terminar e reiniciar </td></tr><tr><td> <code>105002</code> </td><td> Tempo limite de adesão em tempo real </td><td> Sim </td><td> terminar e reiniciar </td></tr><tr><td> <code>105003</code> </td><td> Tempo limite aguardando o primeiro quadro de vídeo </td><td> Sim </td><td> terminar e reiniciar </td></tr><tr><td> <code>105004</code> </td><td> Canal em tempo real não pronto/falha no envio </td><td> depende (falha de envio ativo; pulsação é somente relatório) </td><td> enviar depois <code>correndo</code> </td></tr><tr><td> <code>105005</code> </td><td> Tempo limite de retorno de chamada (padrão 30s) </td><td> Não </td><td> tente novamente e aumente <code>callbackTimeoutMs</code> se necessário </td></tr><tr><td> <code>105006</code> </td><td> Nenhuma transmissão após entrar; O SDK encerra automaticamente a experiência </td><td> Sim </td><td> terminar e reiniciar </td></tr><tr><td> <code>106001</code> </td><td> Erro de rede local </td><td> Não </td><td> repetível </td></tr><tr><td> <code>106002</code> </td><td> Falha na análise da resposta </td><td> depende </td><td> atualizar o SDK/relatório </td></tr><tr><td> <code>106003</code> </td><td> Nenhum código de erro reconhecível/código de erro de string de proxy </td><td> Não </td><td> repetível </td></tr><tr><td> <code>108001</code> </td><td> Desativado remotamente pela opção de recurso do servidor (desligamento total ou versão muito baixa; motivo em <code>cru</code>) </td><td> Sim </td><td> Acompanhe o motivo em <code>cru</code>; orientar o usuário a atualizar quando a versão for muito baixa </td></tr></tbody></table>

Como julgar a fatalidade: a fatalidade não é mais exposta como um campo booleano (OysterSDKError não tem isFatal). Semanticamente, "fatal" significa especificamente se o SDK encerra ativamente a sessão (desconectar o RTC, liberar a sessão inteira) —