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

HappyOyster Guia de integração do Web SDK

Este documento é destinado a desenvolvedores frontend/full-stack e explica como integrar o HappyOyster Web SDK em produção confiável em seus modos de aventura, direção e atuação.

Para APIs individuais, consulte a Referência da API.

0. Exemplo de integração mínima

Se o seu back-end já retornou um QwenCloud chave API temporária token e uma viagem ticket, o fluxo do SDK de front-end será semelhante a este:

import { HappyOysterEngine, isSdkError } from "@happy-oyster/js-sdk";

const engine = new HappyOysterEngine({
  APIHost: "open-platform.example.com",
  model: "happyoyster-1.0-adventure", // Required; Adventure model shown as an example
  logLevel: "warn",
});

engine.updateToken(token);

const videoElement = document.querySelector("video") as HTMLVideoElement;
const travel = engine.createTravel({
  ticket,
  videoElement,
  maxExperienceTimeSec: 90, // Optional, Adventure only: 60 / 90 / 120
});

const offStatus = travel.on("statusChanged", (status) => {
  // Use status to drive loading states, disabled buttons, and player UI.
});

const offError = travel.onError((error) => {
  // Usually log, show a toast, and refresh the token if it has expired.
  if (isSdkError(error)) console.error(error.code, error.message);
});

try {
  await travel.start();
  // Enable controls or Directing input with travel.can(action) after the session is running.
} finally {
  offStatus();
  offError();
  await travel.end();
}

Este snippet abrange apenas o lado do SDK. Criando o mundo, esperando até que esteja pronto, solicitando o ticket, e solicitando o QwenCloud chave API temporária token tudo deve acontecer no back-end do seu aplicativo ou por meio dele.

1. Visão geral do fluxo ponta a ponta

Uma experiência completa envolve principalmente os seguintes participantes.

1.1 Participantes

<table><colgroup><col style={{ width: "25%" }} /><col style={{ width: "75%" }} /></colgroup><thead><tr><th> Participante </th><th> Responsabilidade </th></tr></thead><tbody><tr><td> Interface do aplicativo </td><td> interface do usuário, <code><video></code>, orquestração de sessão e chamadas de SDK </td></tr><tr><td> Back-end do aplicativo </td><td> Armazena a chave de API primária, chama APIs mundiais de plataforma aberta em nome do cliente e emite QwenCloud credenciais </td></tr><tr><td> HappyOyster API aberta </td><td> Expõe as APIs exigidas pelo HappyOyster modelo mundial. </td></tr><tr><td> HappyOyster SDK </td><td> O SDK usado pelo front-end do aplicativo. Ele gerencia principalmente sessões de viagem e acesso ao stream de vídeo no cliente. </td></tr></tbody></table>

happy-oyster-overview.png

1.2 Fluxo de experiência

<table><colgroup><col style={{ width: "10%" }} /><col style={{ width: "22%" }} /><col style={{ width: "28%" }} /><col style={{ width: "40%" }} /></colgroup><thead><tr><th> Palco </th><th> Ator </th><th> Ação </th><th> Descrição </th></tr></thead><tbody><tr><td> ① Crie um mundo </td><td> Back-end do aplicativo → HappyOyster API aberta </td><td> <code>POSTAR /openapi/v1/mundos</code> </td><td> Devoluções <code>criptografadoWorldId</code>, <code>estado</code>e <code>primeiroQuadro</code> </td></tr><tr><td> ② Espere até estar pronto </td><td> Back-end do aplicativo → HappyOyster API aberta </td><td> <code>OBTER /openapi/v1/worlds/build-status</code> </td><td> Enquete até <code>estado === pronto</code> (pule esta etapa se o mundo já estiver pronto quando criado) </td></tr><tr><td> ③ Obtenha uma credencial de viagem </td><td> Back-end do aplicativo → HappyOyster API aberta </td><td> <code>POST /openapi/v1/worlds/get-travel-credential</code> </td><td> Retorna uma única vez <code>bilhete</code> para entrada-viagem </td></tr><tr><td> ④ Solicite um token de chave de API temporário </td><td> Back-end do aplicativo → HappyOyster API aberta </td><td> <code>POST /api/v1/tokens?expire_in_seconds=1800</code> </td><td> Retorna uma chave de API temporária <code>ficha</code>. Não defina uma expiração muito curta ou o token poderá expirar antes da conclusão da experiência e exigir atualizações frequentes. </td></tr><tr><td> ⑤ Entregue o token de chave de API temporário e a credencial de viagem </td><td> Frontend do aplicativo → backend do aplicativo → frontend do aplicativo </td><td> Solicite uma chave de API temporária e uma credencial de viagem </td><td> Depois que o frontend recebe <code>ficha</code> e <code>bilhete</code>, ele inicia o fluxo do SDK </td></tr><tr><td> ⑥ Entre na experiência </td><td> Interface do aplicativo → HappyOyster SDK </td><td> Consulte "Subfluxo de inicialização do SDK" abaixo </td><td> <code>novo HappyOysterEngine/updateToken</code> → <code>criarViagem</code> → <code>início()</code> </td></tr></tbody></table>

1.3 Subfluxo de interação do SDK (estágio expandido ⑥)

1.3.1 Iniciar uma viagem

travel-start-flow.png

Principais requisitos para pedidos de chamada

<table><colgroup><col style={{ width: "10%" }} /><col style={{ width: "30%" }} /><col style={{ width: "60%" }} /></colgroup><thead><tr><th> Passo </th><th> API </th><th> Descrição </th></tr></thead><tbody><tr><td> 1 </td><td> <code>novo HappyOysterEngine()</code> </td><td> Reutilizar um motor para o mesmo <code>APIHost + modelo</code> </td></tr><tr><td> 2 </td><td> <code>atualizarToken()</code> </td><td> Conclua isso antes <code>início()</code> </td></tr><tr><td> 3 </td><td> <code>criarViagem()</code> </td><td> <code>elemento de vídeo</code> é obrigatório </td></tr><tr><td> 4 </td><td> <code>viajar.em(...)</code> </td><td> Cadastre ouvintes antes <code>início()</code> </td></tr><tr><td> 5 </td><td> <code>aguarde viagem.start()</code> </td><td> Entra <code>correndo</code> depois de um início bem-sucedido </td></tr></tbody></table>

1.3.2 Jogue no modo Aventura

adventure-command-flow.png

1.3.3 Jogar no modo Direção/Atuação

adventure-control-flow.png

Use esta tabela como modelo mental rápido para aventura, direção e atuação:

<table><colgroup><col style={{ width: "15%" }} /><col style={{ width: "20%" }} /><col style={{ width: "25%" }} /><col style={{ width: "40%" }} /></colgroup><thead><tr><th> Modo </th><th> Principais APIs </th><th> Uso típico </th><th> Cuidado com </th></tr></thead><tbody><tr><td> Aventura (<code>1</code>) </td><td> <code>enviarComando()</code> </td><td> Controle em tempo real baseado em teclado, joystick e botão </td><td> Enviar somente depois <code>correndo</code>; <code>maxExperienceTimeSec</code> aplica-se apenas a este modo </td></tr><tr><td> Direção (<code>2</code>) </td><td> <code>enviarInstruct()</code>, <code>pausa()</code>, <code>currículo()</code>, <code>retroceder()</code> </td><td> Direção em linguagem natural e controle de reprodução </td><td> Pausar, retomar e retroceder dependem da capacidade da sessão; verificar <code>viajar.can(ação)</code> primeiro </td></tr><tr><td> Atuando (<code>3</code>) </td><td> <code>enviarInstruct()</code>, <code>pausa()</code>, <code>currículo()</code> </td><td> Experiências orientadas para o desempenho e configuração de proporção </td><td> Pausar e retomar dependem da capacidade da sessão; <code>retroceder()</code> está indisponível e <code>lista de scripts</code> não posso usar <code>enviarInstruct()</code> </td></tr></tbody></table>

1.4 Práticas de back-end de aplicativos (resumo)

No back-end, mantenha as seguintes regras simples e rígidas:

2. Conceitos básicos do SDK

No fluxo descrito em §1, o SDK é responsável apenas por “entrar Viagem → jogar → controlar → finalizar”. Existem dois objetos principais:

<table><colgroup><col style={{ width: "30%" }} /><col style={{ width: "70%" }} /></colgroup><thead><tr><th> Objeto </th><th> Objetivo </th></tr></thead><tbody><tr><td> <code>HappyOysterEngine</code> </td><td> Configura <code>HostAPI</code> e <code>modelo</code>, gerencia o QwenCloud token de chave de API temporário (<code>atualizarToken</code>) e cria sessões de viagem </td></tr><tr><td> <code>Viajar</code> </td><td> Representa uma sessão: <code>começar</code> conecta-se ao RTC; <code>enviarComando</code>, <code>enviarInstruir</code>e os métodos de ciclo de vida controlam a sessão </td></tr></tbody></table>

Estados de sessão (statusChanged):

idle ──start()──► prepare ──video playable──► running ⇄ paused ──end()──► idle

Três restrições (explicado mais tarde):

3. Inicialização e gerenciamento de token

3.1 Instanciação do mecanismo

Obrigatório

Recomendado

Use esta tabela rápida para os campos de configuração comuns:

<table><colgroup><col style={{ width: "20%" }} /><col style={{ width: "10%" }} /><col style={{ width: "70%" }} /></colgroup><thead><tr><th> Campo </th><th> Obrigatório </th><th> Orientação </th></tr></thead><tbody><tr><td> <code>HostAPI</code> </td><td> Sim </td><td> Passe apenas o host vazio, como <code>plataforma aberta.exemplo.com</code>; não inclua <code>https://</code> ou um caminho </td></tr><tr><td> <code>modelo</code> </td><td> Sim </td><td> Obrigatório, sem padrão; mantenha-o alinhado com o modelo backend usado para criar o mundo e emitir seu ticket. É fixo para o Motor e não é inferido <code>modo</code> ou o bilhete </td></tr><tr><td> <code>ficha</code> </td><td> Não </td><td> Você pode passá-lo no construtor ou configurá-lo posteriormente com <code>atualizarToken(token)</code>; atualizar tokens expirados com <code>atualizarToken</code> também </td></tr><tr><td> <code>nível de registro</code> </td><td> Não </td><td> Usar <code>depurar</code> em desenvolvimento e <code>avisar</code> ou <code>nenhum</code> em produção </td></tr><tr><td> <code>streamReadyTimeout</code> </td><td> Não </td><td> Aumente-o se a configuração da rede ou da renderização estiver lenta; forneça um caminho de nova tentativa após o tempo limite </td></tr></tbody></table>

Evite

3.2 Modelo de credencial dupla

Correspondente aos estágios ③–⑤ em §1.2:

<table><colgroup><col style={{ width: "20%" }} /><col style={{ width: "25%" }} /><col style={{ width: "25%" }} /><col style={{ width: "30%" }} /></colgroup><thead><tr><th> Credencial </th><th> Fonte </th><th> Como configurá-lo </th><th> Objetivo </th></tr></thead><tbody><tr><td> QwenCloud token de chave de API temporário </td><td> Chave de curta duração emitida pelo back-end do aplicativo </td><td> <code>sdk.updateToken(token)</code> </td><td> Autentica solicitações de SDK para Open Platform </td></tr><tr><td> Bilhete de viagem </td><td> Credencial de viagem solicitada pelo back-end do aplicativo </td><td> <code>createTravel({ ticket })</code> </td><td> Credencial única para uma sessão de viagem </td></tr></tbody></table>

Obrigatório

Recomendado

Evite

4. Ciclo de vida da viagem

4.1 Criar e iniciar

Obrigatório

Recomendado

Evite

4.2 Durante a execução

4.3 Finalizar e recriar

Obrigatório

Recomendado

Evite

5. Elemento de vídeo e políticas do navegador

Obrigatório

Recomendado

Evite

6. Assinaturas de eventos e tratamento de erros

6.1 Assinaturas de eventos

Obrigatório

Recomendado

<table><colgroup><col style={{ width: "25%" }} /><col style={{ width: "75%" }} /></colgroup><thead><tr><th> Evento </th><th> Objetivo </th></tr></thead><tbody><tr><td> <code>status alterado</code> </td><td> Exibir status, detectar prontidão de vídeo (<code>correndo</code>) e ativar ou desativar botões </td></tr><tr><td> <code>travelInfoReady</code> </td><td> Leia <code>ID de viagem criptografado</code>, modo, primeiro quadro, duração da experiência e proporção de atuação antes da conexão do RTC; <code>viajar.getInfo()</code> retorna os mesmos dados </td></tr><tr><td> <code>primeiroFrameGenerated</code> </td><td> Quando existe um primeiro quadro não vazio, segue-se <code>travelInfoReady</code>; use-o como um espaço reservado durante <code>preparar</code> </td></tr><tr><td> <code>erro</code> (através <code>onError</code>) </td><td> Registro, notificações do sistema e acionamento de atualização de token </td></tr></tbody></table>

6.2 Camadas de erro

Evite

7. Envio de instruções: comando e instrução

7.1 sendCommand (controle em tempo real)

sendCommand() fornece controle em tempo real no modo Aventura (1). Ele envia comandos através do RTC DataChannel e está disponível apenas enquanto a sessão está running.

O SDK envia no máximo um comando aproximadamente a cada 42 ms (cerca de 24 FPS). Se tiver decorrido um ciclo desde o envio anterior, ele envia o novo comando imediatamente. Se sendCommand() é chamado várias vezes no mesmo ciclo, envia apenas o comando completo da última chamada. translation, rotatione interaction são dimensões de controle independentes. Os campos omitidos são enviados como None; o SDK não mescla campos entre chamadas, repete periodicamente o comando anterior ou envia automaticamente None.

Ações pontuais

Ligar sendCommand() uma vez para ações únicas, como pular ou atacar:

await travel.sendCommand({ interaction: "Jump" });

Ações contínuas

Continue ligando sendCommand() enquanto um controle de movimento ou rotação de visualização contínua permanece pressionado. O aplicativo frontend não precisa limitar as chamadas para 42 ms; o SDK reúne chamadas de alta frequência em uma taxa de saída de cerca de 24 FPS. No lançamento, envie explicitamente None para redefinir o controle:

// Keep calling while the control is pressed
void travel.sendCommand({ translation: "Front" });

// Stop moving on release
void travel.sendCommand({ translation: "None" });

Evite

7.2 sendInstruct (Instruções de direção)

Obrigatório

Recomendado

8. Solução de problemas

<table><colgroup><col style={{ width: "25%" }} /><col style={{ width: "35%" }} /><col style={{ width: "40%" }} /></colgroup><thead><tr><th> Sintoma </th><th> Causa comum </th><th> O que fazer </th></tr></thead><tbody><tr><td> A inicialização do SDK falha com um erro de argumento </td><td> <code>HostAPI</code> foi passado como um URL completo, por exemplo com <code>https://</code> ou um caminho </td><td> Passe apenas o host vazio, como <code>plataforma aberta.exemplo.com</code> </td></tr><tr><td> <code>viajar.start()</code> falha porque o ticket é inválido ou já foi usado </td><td> <code>bilhete</code> é de uso único; pode expirar, ser reutilizado ou emitido antes que o mundo esteja pronto </td><td> Solicite um novo <code>bilhete</code> do back-end; certifique-se de que o mundo esteja pronto antes de entrar </td></tr><tr><td> <code>onError</code> relata um erro de autenticação após a execução da sessão </td><td> O QwenCloud chave API temporária <code>ficha</code> expirou </td><td> Peça ao seu back-end um novo token e ligue <code>motor.updateToken(novoToken)</code> </td></tr><tr><td> O player continua carregando ou <code>início()</code> expira </td><td> O fluxo remoto não está pronto, a rede está lenta ou <code>streamReadyTimeout</code> é muito curto </td><td> Forneça uma ação de nova tentativa; aumentar <code>streamReadyTimeout</code> se necessário; verifique os logs de back-end e RTC </td></tr><tr><td> Um segundo <code>criarViagem()</code> a chamada falha porque já existe uma viagem ativa </td><td> A viagem anterior não terminou </td><td> Ligar <code>aguarde viagem.end()</code> e limpe os ouvintes ao sair, tentar novamente ou trocar de sessão </td></tr><tr><td> Controles ou instruções não fazem nada </td><td> A sessão não é <code>correndo</code>, ou a API errada é usada para o modo Aventura, Direção ou Atuação atual </td><td> Disponibilidade do botão Drive de <code>status alterado</code>; ligar <code>viajar.can(ação)</code> antes de ações sensíveis ao modo </td></tr></tbody></table>

Ao depurar, registre três coisas juntas: SDK ErrorCode, atual TravelStatuse logs de solicitação de back-end. Isso geralmente informa se o problema está relacionado à credencial, ao estado, à rede ou à renderização.