---
type: tutorial
title: Construa um jogo de marketplace
summary: "Construa um jogo Caputchin de uma pasta vazia: instale o SDK, registre uma fábrica, renderize a partir do contexto por rodada, registre um trace, e empacote-o para o iframe em sandbox."
---

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](/docs/marketplace-game-development/overview) 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

```bash
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).

```js
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](/docs/marketplace-game-development/game-sdk-reference) 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:

```js
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](#5-record-a-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.

```js
function onWin(traceString) {
  bridge.pass({ trace: traceString });
}
function onAssetFailure(err) {
  bridge.error({ code: "asset-load-failed", message: String(err) });
}
```

## 5. Registre um trace [#5-record-a-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.

```js
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](/docs/marketplace-game-development/replay-contract); 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](/docs/marketplace-game-development/engine-kit) 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:

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

Este é o mesmo caminho `game-src` que um [jogo personalizado](/docs/custom-game-development/self-hosted-game) 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

- [O contrato de replay](/docs/marketplace-game-development/replay-contract): transforme seu jogo registrado no artefato `run` que torna o jogo reproduzível.
- [O manifesto caputchin.json](/docs/marketplace-game-development/the-caputchin-json-manifest): descreva o jogo, seus presets e seu bundle.
- [Publicar no marketplace](/docs/marketplace-game-development/publish-to-the-marketplace): marque o repositório com uma tag e entre no ar.

## Veja também

- [Referência do SDK de jogo](/docs/marketplace-game-development/game-sdk-reference): cada export, por completo.
- [O engine-kit](/docs/marketplace-game-development/engine-kit): a forma opcional de obter determinismo de graça.
