Caputchin
Desenvolvimento de jogo personalizado

Construa um jogo auto-hospedado

Ao final deste tutorial você terá um jogo Caputchin de verdade: um bundle JavaScript que roda no iframe em sandbox do widget, fala com o anfitrião pelo SDK de jogo, e reporta um trace que o servidor pode reexecutar. Diferente do modo manual, um jogo auto-hospedado pode proteger uma chave de site, porque sua rodada é reproduzível. Este tutorial cobre construir e hospedar o jogo; replay e proteção cobre o artefato que de fato liga o portão.

Leia rode seu próprio jogo para onde isto se encaixa. Você precisa de um lugar para hospedar um arquivo JavaScript estático (qualquer CDN ou host estático) e do widget na sua página.

O formato de um jogo auto-hospedado

Um jogo auto-hospedado é um bundle JavaScript autossuficiente que:

  1. Importa a função register do @caputchin/game-sdk e registra uma única fábrica de jogo.
  2. Renderiza no contêiner que o widget dá à fábrica, usando o idioma, skin e configuração resolvidos passados junto.
  3. Joga de forma determinística sob a semente por rodada, registrando o jogo como um trace.
  4. Numa vitória, chama bridge.pass({ trace }) com esse registro.

O widget carrega seu bundle no seu iframe e invoca sua fábrica; a fábrica sendo chamada é o sinal de início (não há um evento de início separado para esperar).

1. Registre uma fábrica de jogo

Instale o SDK e chame register uma vez com sua fábrica. Você não passa o manifesto; o servidor resolve os presets e os entrega à fábrica.

import { register } from "@caputchin/game-sdk";

register((container, bridge, ctx) => {
  // container — a DOM element inside the sandboxed iframe; render into it.
  // bridge    — push-only channel to the host (pass / error / setSize).
  // ctx       — the per-round context (seed + resolved locale/skin/config).

  const cleanup = startGame(container, bridge, ctx);

  // Return an optional cleanup function; the widget calls it on unmount.
  return cleanup;
});
npm install @caputchin/game-sdk

2. Leia o contexto por rodada

O terceiro argumento da fábrica, ctx, carrega tudo o que seu jogo precisa para este visitante:

  • ctx.seed: a semente de replay por rodada. Derive toda aleatoriedade dela. Null quando o jogo roda fora de uma sessão verificada.
  • ctx.locale: o objeto de idioma resolvido: ctx.locale._lang mais suas chaves de string traduzidas.
  • ctx.skin: o objeto de skin resolvido: ctx.skin._theme (sempre light ou dark) mais suas chaves de cor e ativo.
  • ctx.config: a configuração de jogabilidade resolvida (ou null).

Estes são os presets resolvidos para este visitante, produzidos pela Caputchin a partir do seu esquema e presets definidos no painel. Você lê suas próprias chaves deles; você não resolve nada você mesmo.

Se o seu layout precisa de um tamanho explícito, diga ao anfitrião uma vez depois do seu primeiro paint:

bridge.setSize(360, 480);

3. Torne o jogo determinístico

Esta é a regra que torna um jogo capaz de proteger: o jogo precisa ser determinístico dada a semente e as entradas do jogador. Não chame Math.random() nem leia o relógio para nada que afete o resultado; derive cada escolha aleatória de ctx.seed. O servidor reexecuta sua lógica sob a mesma semente e o mesmo trace registrado e precisa chegar ao mesmo veredito.

// A tiny seeded PRNG; feed it ctx.seed so the server reproduces the run.
function makeRng(seed) {
  let s = hashToInt(seed);
  return () => (s = (s * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff;
}

Registre as entradas que conduzem o resultado (em quais alvos o jogador acertou, em ordem, com timing se importa) à medida que você joga. Serialize esse registro para uma string: essa string é o seu trace.

4. Aprove com o trace

Quando o visitante vence, entregue o trace ao anfitrião. pass aceita um objeto com uma única string trace:

function onWin(traceString) {
  bridge.pass({ trace: traceString });
}

O trace é opaco para a plataforma: é qualquer string que só o seu jogo define, de modo que sua lógica, combinada com a semente, reproduza o resultado. O jogo não reporta uma pontuação aqui; a pontuação, se houver, é seu próprio assunto dentro do iframe.

Mais dois fatos sobre a ponte:

  • O primeiro pass resgata a rodada; uma rodada falha ou abandonada é sinalizada simplesmente não chamando pass (não há método fail nesta ponte).
  • bridge.error({ code, message }) é para uma falha interna do jogo (um ativo falhou em carregar, uma exceção), não uma derrota do jogador. Ele expõe um evento error ao anfitrião.

Depois que o anfitrião resgata a aprovação, ele libera o token do widget, e seu backend verifica esse token como de costume.

5. Empacote para a sandbox

O widget carrega exatamente uma URL de script, então tudo precisa estar nesse único arquivo. Configure seu bundler (esbuild, rollup, vite, webpack; qualquer um deles) para uma saída autossuficiente:

  • inline os ativos como data URLs (sprites, sons, fontes);
  • desabilite o code splitting;
  • se você usa WASM, embuta-o como base64 e instancie a partir dos bytes.

Padrões que não funcionam dentro da sandbox, porque o iframe é de origem opaca com uma CSP estrita:

  • fetch('./sprite.png') relativo ao caminho, não há caminho de onde buscar;
  • import('./chunk.js') dinâmico, a segunda URL é bloqueada;
  • new Worker('./worker.js'), gere workers de URLs Blob inline em vez disso;
  • buscas em CDN externo em tempo de execução, connect-src as bloqueia.

6. Hospede o bundle e aponte o widget para ele

Sirva o arquivo construído do seu próprio host estático por https (http de loopback é permitido para dev local), depois aponte o widget para ele:

<caputchin-game
  sitekey="cpt_pub_..."
  game-src="https://cdn.example.com/my-game/game.js"
></caputchin-game>

O widget carrega seu bundle no seu iframe em sandbox, invoca sua fábrica com o contexto, e espera pelo seu pass. Neste ponto o jogo roda e verifica, mas ainda não tem permissão de proteger uma chave.

7. Torne-o capaz de proteger

Para usar seu jogo auto-hospedado como um portão de verificação, mais duas coisas são necessárias, ambas cobertas a seguir:

  1. Registre-o como um jogo personalizado no painel (um id que você escolhe) e defina seu esquema e presets de campos para que o contexto carregue locale/skin/config reais.
  2. Envie um artefato de replay: uma build headless da lógica do seu jogo que o servidor roda para rederivar o veredito da semente e do trace. Veja replay e proteção.

Até que o artefato de replay seja enviado e passe na sua autoverificação, o jogo personalizado aparece como Não reproduzível e não pode proteger.

Veja também

Nesta página