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-sdkEl 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).nullfuera de una sesión verificada.ctx.locale- cadenas de idioma resueltas (ctx.locale._langmás tus claves), onull.ctx.skin- colores y URLs de assets resueltos (ctx.skin._theme, siemprelightodark, más tus claves), onull.ctx.config- configuración de juego resuelta, onull.
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:
- Deriva cada elección aleatoria de
ctx.seed. Nunca llames aMath.random()ni leas el reloj de pared para nada que afecte al desenlace. - 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 URLsBlobinline en su lugar;- descargas de CDN externo en tiempo de ejecución -
connect-srclas 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
- El contrato de repetición: convierte tu juego registrado en el artefacto
runque hace el juego reproducible. - El manifiesto caputchin.json: describe el juego, sus presets, y su bundle.
- Publica en el marketplace: etiqueta el repo y sal en vivo.
Véase también
- Referencia del game SDK: cada export, por completo.
- El engine-kit: la forma opcional de obtener determinismo gratis.