Caputchin
Desarrollo de juegos del marketplace

Construye un juego del marketplace

Al final de este tutorial tendrás un juego de Caputchin completo y jugable en un bundle de JavaScript, listo para hacerse reproducible y publicar. Esta es la versión aburrida de haz-cada-paso; lee Publica un juego en el marketplace primero para saber dónde encaja.

Necesitas Node y un bundler (esbuild, rollup, vite o webpack, cualquiera de ellos). No necesitas una cuenta de Caputchin para construir, solo para publicar.

1. Instala el SDK

npm install @caputchin/game-sdk

El SDK es diminuto: un helper register más tipos de TypeScript. No empaqueta el runtime del widget, así que tu juego se queda pequeño.

2. Registra una factory de juego

Un juego es una sola llamada a register con una función factory. El widget invoca tu factory dentro del iframe en sandbox; esa llamada es en sí la señal de inicio (no hay un evento de inicio separado que 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;
});

No pasas el manifiesto a register; el servidor lee caputchin.json en tiempo de indexación y envía los presets resueltos a tu factory como ctx. Mira la referencia del SDK para la superficie completa.

3. Renderiza desde el contexto por ronda

El tercer argumento de la factory, ctx, lleva todo lo que cambia por visitante:

  • ctx.seed - la semilla por ronda. Deriva toda la aleatoriedad de esto (mira el paso 5). null fuera de una sesión verificada.
  • ctx.locale - cadenas de idioma resueltas (ctx.locale._lang más tus claves), o null.
  • ctx.skin - colores y URLs de assets resueltos (ctx.skin._theme, siempre light o dark, más tus claves), o null.
  • ctx.config - configuración de juego resuelta, o null.

Lee tus propias claves de estos; nunca resuelves presets tú mismo. Cada uno es null cuando tu manifiesto no declara ningún bloque coincidente, así que recae siempre en un valor por defecto integrado:

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...
}

Si tu layout necesita un tamaño explícito, llama a bridge.setSize(width, height) una vez tras tu primer pintado.

4. Ganar, perder y errores

Tu juego decide cuándo gana el jugador y se lo dice al anfitrión a través del bridge:

  • Al ganar, llama a bridge.pass({ trace }) con la traza de la ronda (paso 5).
  • En una derrota o abandono, llama a nada; el silencio es la señal de fallo.
  • Si el propio juego se rompe (un asset falló, una excepción), llama a bridge.error({ code, message }). Eso es para un fallo interno del juego, no para que un jugador pierda.
function onWin(traceString) {
  bridge.pass({ trace: traceString });
}
function onAssetFailure(err) {
  bridge.error({ code: "asset-load-failed", message: String(err) });
}

5. Registra una traza

Este es el paso que hace el juego reproducible. Mientras el jugador actúa, registra las entradas que conducen el desenlace (qué objetivo golpeó, en orden, con temporización si importa) y serializa ese registro a una cadena o un array de bytes: esa es tu traza. Combinada con la semilla, la traza debe dejar que tu lógica reproduzca el resultado exacto, porque el servidor la vuelve a correr.

Dos reglas lo hacen posible:

  1. Deriva cada elección aleatoria de ctx.seed. Nunca llames a Math.random() ni leas el reloj de pared para nada que afecte al desenlace.
  2. Registra lo suficiente para repetir. La traza más la semilla es la entrada completa a la lógica de tu juego.
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;
}

La forma de seed, la traza, y el veredicto las define el contrato de repetición; esa página cubre convertir este juego registrado en el artefacto run headless que el servidor repite. Si escribir a mano lógica determinista suena engorroso, el engine kit lo hace por ti, opcionalmente.

6. Empaqueta para el sandbox

El widget carga exactamente una URL de script, así que todo debe estar en ese único archivo. Configura tu bundler para una sola salida autocontenida:

  • inlinea los assets como data URLs (sprites, sonidos, fuentes);
  • desactiva el code splitting;
  • si usas WASM, embébelo como base64 e instáncialo desde bytes.

Patrones que no funcionan dentro del iframe de origen opaco y CSP estricta:

  • fetch('./sprite.png') relativo a la ruta - no hay ruta desde la que descargar;
  • import('./chunk.js') dinámico - la segunda URL está bloqueada;
  • new Worker('./worker.js') - lanza workers desde URLs Blob inline en su lugar;
  • descargas de CDN externo en tiempo de ejecución - connect-src las bloquea.

7. Pruébalo en local

No hay harness especial. Incrusta el widget en una página HTML estática, apunta game-src a la salida de tu bundle local, y juégalo en un navegador:

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

Este es el mismo camino game-src que usa un juego a medida; para el marketplace en su lugar publicarás el repo para que la plataforma fije el bundle por ti.

Siguientes pasos

Véase también

En esta página