---
type: tutorial
title: Bangun game marketplace
summary: "Bangun game Caputchin dari folder kosong: pasang SDK, daftarkan factory, render dari konteks per-ronde, rekam trace, dan bundel ia untuk iframe ber-sandbox."
---

Di akhir tutorial ini kamu akan punya game Caputchin lengkap yang bisa dimainkan dalam satu bundel JavaScript, siap dibuat bisa-diputar-ulang dan diterbitkan. Ini versi membosankan, lakukan-tiap-langkah; baca [Kirim game ke marketplace](/docs/marketplace-game-development/overview) dulu untuk di mana ia cocok.

Kamu butuh Node dan sebuah bundler (esbuild, rollup, vite, atau webpack, salah satu). Kamu tak butuh akun Caputchin untuk membangun, hanya untuk menerbitkan.

## 1. Pasang SDK

```bash
npm install @caputchin/game-sdk
```

SDK mungil: sebuah helper `register` plus tipe TypeScript. Ia tak membundel runtime widget, jadi game-mu tetap kecil.

## 2. Daftarkan sebuah game factory

Sebuah game adalah satu panggilan ke `register` dengan **fungsi factory**. Widget memanggil factory-mu di dalam iframe ber-sandbox; panggilan itu sendiri adalah sinyal mulai (tak ada event mulai terpisah untuk ditunggu).

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

Kamu **tak** melewatkan manifest ke `register`; server membaca `caputchin.json` saat-indeks dan mengirim preset terselesaikan ke factory-mu sebagai `ctx`. Lihat [referensi SDK](/docs/marketplace-game-development/game-sdk-reference) untuk permukaan penuhnya.

## 3. Render dari konteks per-ronde

Argumen ketiga factory, `ctx`, membawa semua yang berubah per pengunjung:

- `ctx.seed` - seed per-ronde. **Turunkan semua keacakan dari ini** (lihat langkah 5). `null` di luar sesi terverifikasi.
- `ctx.locale` - string bahasa terselesaikan (`ctx.locale._lang` plus kunci-mu), atau `null`.
- `ctx.skin` - warna dan URL aset terselesaikan (`ctx.skin._theme`, selalu `light` atau `dark`, plus kunci-mu), atau `null`.
- `ctx.config` - konfigurasi gameplay terselesaikan, atau `null`.

Baca kunci-mu sendiri dari ini; kamu tak pernah menyelesaikan preset sendiri. Masing-masing `null` saat manifest-mu mendeklarasikan tanpa blok cocok, jadi selalu jatuh ke default bawaan:

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

Jika tata letakmu butuh ukuran eksplisit, panggil `bridge.setSize(width, height)` sekali setelah cat pertamamu.

## 4. Menang, kalah, dan galat

Game-mu memutuskan kapan pemain menang dan memberi tahu host lewat jembatan:

- Saat menang, panggil `bridge.pass({ trace })` dengan [trace](#5-record-a-trace) ronde (langkah 5).
- Saat kalah atau ditinggalkan, panggil **tak ada**; keheningan adalah sinyal kegagalan.
- Jika game itu sendiri rusak (aset gagal, eksepsi), panggil `bridge.error({ code, message })`. Itu untuk kegagalan internal-game, bukan pemain kalah.

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

## 5. Rekam sebuah trace [#5-record-a-trace]

Inilah langkah yang membuat game **bisa-diputar-ulang**. Selagi pemain bertindak, rekam input yang mengemudikan hasil (target mana yang mereka kenai, dalam urutan, dengan waktu jika penting) dan serialisasikan catatan itu ke string atau array byte: itu adalah **trace**-mu. Digabung dengan seed, trace harus membiarkan logikamu mereproduksi hasil persis, karena server menjalankannya-ulang.

Dua aturan membuat itu mungkin:

1. **Turunkan setiap pilihan acak dari `ctx.seed`.** Jangan pernah panggil `Math.random()` atau membaca jam dinding untuk apa pun yang memengaruhi hasil.
2. **Rekam cukup untuk memutar-ulang.** Trace plus seed adalah input lengkap ke logika game-mu.

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

Bentuk `seed`, trace, dan vonis didefinisikan oleh [kontrak putar-ulang](/docs/marketplace-game-development/replay-contract); halaman itu mencakup mengubah permainan terekam ini menjadi artefak `run` headless yang dijalankan-ulang server. Jika menulis-tangan logika deterministik terdengar rewel, [kit engine](/docs/marketplace-game-development/engine-kit) melakukannya untukmu, opsional.

## 6. Bundel untuk sandbox

Widget memuat persis satu URL skrip, jadi semua harus ada di satu berkas itu. Konfigurasi bundler-mu untuk satu keluaran mandiri:

- inline aset sebagai data URL (sprite, suara, font);
- nonaktifkan pemisahan kode;
- jika kamu memakai WASM, sematkan ia sebagai base64 dan instansiasi dari byte.

Pola yang **tidak** berfungsi di dalam iframe ber-origin-opaque, CSP-ketat:

- `fetch('./sprite.png')` relatif-path - tak ada path untuk diambil;
- `import('./chunk.js')` dinamis - URL kedua diblokir;
- `new Worker('./worker.js')` - munculkan worker dari URL `Blob` inline sebagai gantinya;
- pengambilan CDN eksternal saat runtime - `connect-src` memblokirnya.

## 7. Uji ia secara lokal

Tak ada harness khusus. Sematkan widget di halaman HTML statis, arahkan `game-src` ke keluaran bundel lokalmu, dan mainkan di peramban:

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

Ini adalah jalur `game-src` yang sama yang dipakai [game kustom](/docs/custom-game-development/self-hosted-game); untuk marketplace kamu akan menerbitkan repo sebagai gantinya agar platform memin bundel untukmu.

## Langkah berikutnya

- [Kontrak putar-ulang](/docs/marketplace-game-development/replay-contract): ubah permainan terekam-mu menjadi artefak `run` yang membuat game bisa-diputar-ulang.
- [Manifest caputchin.json](/docs/marketplace-game-development/the-caputchin-json-manifest): deskripsikan game, preset, dan bundel-nya.
- [Terbitkan ke marketplace](/docs/marketplace-game-development/publish-to-the-marketplace): tag repo dan tayang.

## Lihat juga

- [Referensi game SDK](/docs/marketplace-game-development/game-sdk-reference): setiap ekspor, secara penuh.
- [engine-kit](/docs/marketplace-game-development/engine-kit): cara opsional mendapat determinisme gratis.
