Caputchin
Desenvolvimento de jogo de marketplace

Construa um jogo de marketplace

Ao final deste tutorial você terá um jogo Caputchin completo e jogável em um bundle JavaScript, pronto para tornar reproduzível e publicar. Esta é a versão chata, faça-cada-passo; leia Publique um jogo no marketplace primeiro para onde ela se encaixa.

Você precisa de Node e de um bundler (esbuild, rollup, vite ou webpack, qualquer um deles). Você não precisa de uma conta Caputchin para construir, só para publicar.

1. Instale o SDK

npm install @caputchin/game-sdk

O SDK é minúsculo: um ajudante register mais tipos TypeScript. Ele não empacota o runtime do widget, então seu jogo continua pequeno.

2. Registre uma fábrica de jogo

Um jogo é uma única chamada a register com uma função fábrica. O widget invoca sua fábrica dentro do iframe em sandbox; essa chamada é ela mesma o sinal de início (não há um evento de início separado para esperar).

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;
});

Você não passa o manifesto para register; o servidor lê o caputchin.json no momento da indexação e envia os presets resolvidos para a sua fábrica como ctx. Veja a referência do SDK para a superfície completa.

3. Renderize a partir do contexto por rodada

O terceiro argumento da fábrica, ctx, carrega tudo o que muda por visitante:

  • ctx.seed - a semente por rodada. Derive toda aleatoriedade dela (veja o passo 5). null fora de uma sessão verificada.
  • ctx.locale - strings de idioma resolvidas (ctx.locale._lang mais suas chaves), ou null.
  • ctx.skin - cores e URLs de ativo resolvidos (ctx.skin._theme, sempre light ou dark, mais suas chaves), ou null.
  • ctx.config - configuração de jogabilidade resolvida, ou null.

Leia suas próprias chaves destes; você nunca resolve presets você mesmo. Cada um é null quando seu manifesto não declara um bloco correspondente, então sempre recaia em um padrão embutido:

function startGame(container, bridge, ctx) {
  const title = ctx.locale?.title ?? "Tap the targets";
  const accent = ctx.skin?.accent_color ?? "#2da44e";
  const targetCount = ctx.config?.target_count ?? 5;
  // ...render with these...
}

Se o seu layout precisa de um tamanho explícito, chame bridge.setSize(width, height) uma vez depois do seu primeiro paint.

4. Vitória, derrota e erros

Seu jogo decide quando o jogador vence e diz ao anfitrião pela ponte:

  • Numa vitória, chame bridge.pass({ trace }) com o trace da rodada (passo 5).
  • Numa derrota ou abandono, chame nada; o silêncio é o sinal de falha.
  • Se o próprio jogo quebra (um ativo falhou, uma exceção), chame bridge.error({ code, message }). Isso é para falha interna do jogo, não um jogador perdendo.
function onWin(traceString) {
  bridge.pass({ trace: traceString });
}
function onAssetFailure(err) {
  bridge.error({ code: "asset-load-failed", message: String(err) });
}

5. Registre um trace

Este é o passo que torna o jogo reproduzível. À medida que o jogador age, registre as entradas que conduzem o resultado (em qual alvo ele acertou, em ordem, com timing se importa) e serialize esse registro para uma string ou array de bytes: esse é o seu trace. Combinado com a semente, o trace precisa deixar sua lógica reproduzir o resultado exato, porque o servidor o reexecuta.

Duas regras tornam isso possível:

  1. Derive cada escolha aleatória de ctx.seed. Nunca chame Math.random() nem leia o relógio para nada que afete o resultado.
  2. Registre o suficiente para reexecutar. O trace mais a semente é a entrada completa para a lógica do seu jogo.
function makeRng(seed) {
  // seed is ctx.seed; feed it so the server reproduces the same sequence.
  let s = hashSeed(seed);
  return () => (s = (s * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff;
}

O formato do seed, do trace e do veredito são definidos por o contrato de replay; essa página cobre transformar este jogo registrado no artefato run headless que o servidor reexecuta. Se escrever lógica determinística à mão soa trabalhoso, o kit de engine o faz por você, opcionalmente.

6. Empacote para a sandbox

O widget carrega exatamente uma URL de script, então tudo precisa estar nesse único arquivo. Configure seu bundler 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 do iframe de origem opaca, de 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.

7. Teste-o localmente

Não há harness especial. Embuta o widget em uma página HTML estática, aponte o game-src para a saída do seu bundle local, e jogue-o em um navegador:

<caputchin-game sitekey="cpt_pub_..." game-src="http://localhost:8080/game.js"></caputchin-game>

Este é o mesmo caminho game-src que um jogo personalizado usa; para o marketplace você vai, em vez disso, publicar o repositório para que a plataforma fixe o bundle por você.

Próximos passos

Veja também

Nesta página