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-sdkO 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).nullfora de uma sessão verificada.ctx.locale- strings de idioma resolvidas (ctx.locale._langmais suas chaves), ounull.ctx.skin- cores e URLs de ativo resolvidos (ctx.skin._theme, semprelightoudark, mais suas chaves), ounull.ctx.config- configuração de jogabilidade resolvida, ounull.
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:
- Derive cada escolha aleatória de
ctx.seed. Nunca chameMath.random()nem leia o relógio para nada que afete o resultado. - 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 URLsBlobinline em vez disso;- buscas em CDN externo em tempo de execução -
connect-srcas 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
- O contrato de replay: transforme seu jogo registrado no artefato
runque torna o jogo reproduzível. - O manifesto caputchin.json: descreva o jogo, seus presets e seu bundle.
- Publicar no marketplace: marque o repositório com uma tag e entre no ar.
Veja também
- Referência do SDK de jogo: cada export, por completo.
- O engine-kit: a forma opcional de obter determinismo de graça.