---
type: tutorial
title: ابنِ لعبة المتجر
summary: "ابنِ لعبة Caputchin من مجلّد فارغ: ثبّت الـ SDK، وسجّل مصنعًا، واعرض من سياق كل جولة، وسجّل أثرًا، واحزمها لإطار iframe المعزول."
---

بنهاية هذا الدرس سيكون لديك لعبة Caputchin كاملة قابلة للّعب في حزمة JavaScript واحدة، جاهزة لجعلها قابلة لإعادة التشغيل ونشرها. هذه النسخة المملّة، افعل-كل-خطوة؛ اقرأ [اشحن لعبة إلى المتجر](/docs/marketplace-game-development/overview) أولًا لمكانها.

تحتاج إلى Node ومُجمِّع (esbuild، أو rollup، أو vite، أو webpack، أيٌّ منها). لا تحتاج إلى حساب Caputchin للبناء، بل للنشر فقط.

## 1. ثبّت الـ SDK

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

الـ SDK ضئيل: مساعد `register` إضافةً إلى أنواع TypeScript. لا يحزم زمن تشغيل الأداة، فتبقى لعبتك صغيرة.

## 2. سجّل مصنع لعبة

اللعبة نداء واحد لـ `register` بـ **دالة مصنع**. تستدعي الأداة مصنعك داخل إطار iframe المعزول؛ ذلك النداء هو نفسه إشارة البدء (لا حدث بدء منفصل لتنتظره).

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

**لا** تمرّر البيان إلى `register`؛ يقرأ الخادم `caputchin.json` وقت الفهرسة ويشحن التهيئات المحلولة إلى مصنعك كـ `ctx`. انظر [مرجع الـ SDK](/docs/marketplace-game-development/game-sdk-reference) للسطح الكامل.

## 3. اعرض من سياق كل جولة

وسيط المصنع الثالث، `ctx`، يحمل كل ما يتغيّر لكل زائر:

- `ctx.seed` - بذرة كل جولة. **اشتقّ كل العشوائية من هذه** (انظر الخطوة 5). `null` خارج جلسة متحقَّق منها.
- `ctx.locale` - نصوص اللغة المحلولة (`ctx.locale._lang` إضافةً إلى مفاتيحك)، أو `null`.
- `ctx.skin` - الألوان وروابط الأصول المحلولة (`ctx.skin._theme`، دائمًا `light` أو `dark`، إضافةً إلى مفاتيحك)، أو `null`.
- `ctx.config` - إعداد اللعب المحلول، أو `null`.

اقرأ مفاتيحك الخاصة منها؛ لا تحلّ تهيئات بنفسك أبدًا. كلٌّ `null` حين لا يعلن بيانك كتلةً مطابقةً، فارتدّ دائمًا إلى افتراضي مدمج:

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

إن احتاج تخطيطك حجمًا صريحًا، ناد `bridge.setSize(width, height)` مرة بعد أول رسم لك.

## 4. الفوز والخسارة والأخطاء

تقرّر لعبتك متى يفوز اللاعب وتخبر المضيف عبر الجسر:

- عند الفوز، ناد `bridge.pass({ trace })` بـ [أثر](#5-record-a-trace) الجولة (الخطوة 5).
- عند الخسارة أو الهجر، ناد **لا شيء**؛ الصمت إشارة الفشل.
- إن انكسرت اللعبة نفسها (فشل أصل، أو استثناء)، ناد `bridge.error({ code, message })`. ذلك لإخفاق داخلي للّعبة، لا لخسارة لاعب.

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

## 5. سجّل أثرًا [#5-record-a-trace]

هذه الخطوة التي تجعل اللعبة **قابلة لإعادة التشغيل**. أثناء تصرّف اللاعب، سجّل المُدخَلات التي تقود الحصيلة (أيّ هدف أصاب، بالترتيب، بالتوقيت إن كان يهمّ) وسلسِل ذلك السجلّ إلى نصّ أو مصفوفة بايتات: ذلك **أثرك**. مدموجًا مع البذرة، يجب أن يتيح الأثر لمنطقك إعادة إنتاج النتيجة بالضبط، لأن الخادم يعيد تشغيله.

قاعدتان تجعلان ذلك ممكنًا:

1. **اشتقّ كل خيار عشوائي من `ctx.seed`.** لا تنادِ `Math.random()` أبدًا ولا تقرأ ساعة الحائط لأي شيء يؤثّر في الحصيلة.
2. **سجّل ما يكفي لإعادة التشغيل.** الأثر إضافةً إلى البذرة هو المُدخَل الكامل لمنطق لعبتك.

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

شكل `seed`، والأثر، والحُكم معرَّف بـ [عقد إعادة التشغيل](/docs/marketplace-game-development/replay-contract)؛ تلك الصفحة تغطّي تحويل هذا اللعب المسجَّل إلى أداة `run` بلا واجهة التي يعيد الخادم تشغيلها. إن بدت كتابة منطق حتمي يدويًّا متعبة، فإن [عدّة المحرّك](/docs/marketplace-game-development/engine-kit) تفعلها عنك، اختياريًّا.

## 6. احزم للعزل

تحمّل الأداة رابط سكربت واحد بالضبط، فيجب أن يكون كل شيء في ذلك الملفّ الواحد. أعدّ مُجمِّعك لمخرَج واحد مكتفٍ بذاته:

- ضمّن الأصول كروابط data (الصور النقطية، والأصوات، والخطوط)؛
- عطّل تقسيم الكود؛
- إن استخدمت WASM، ضمّنه كـ base64 وأنشئه من بايتات.

أنماط **لا** تعمل داخل إطار iframe بأصل معتم بـ CSP صارم:

- `fetch('./sprite.png')` نسبيّ المسار - لا مسار للجلب منه؛
- `import('./chunk.js')` ديناميكي - الرابط الثاني محجوب؛
- `new Worker('./worker.js')` - ولّد العمّال من روابط `Blob` مضمَّنة بدلًا؛
- جلب CDN خارجي وقت التشغيل - `connect-src` يحجبه.

## 7. اختبرها محليًّا

لا هيكل خاصّ. ضمّن الأداة في صفحة HTML ساكنة، ووجّه `game-src` إلى مخرَج حزمتك المحلّي، والعبها في متصفّح:

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

هذا مسار `game-src` نفسه الذي تستخدمه [لعبة مخصّصة](/docs/custom-game-development/self-hosted-game)؛ وللمتجر ستنشر المستودع بدلًا كي تثبّت المنصّة الحزمة عنك.

## الخطوات التالية

- [عقد إعادة التشغيل](/docs/marketplace-game-development/replay-contract): حوّل لعبك المسجَّل إلى أداة `run` التي تجعل اللعبة قابلة لإعادة التشغيل.
- [بيان caputchin.json](/docs/marketplace-game-development/the-caputchin-json-manifest): صِف اللعبة، وتهيئاتها، وحزمتها.
- [انشر إلى المتجر](/docs/marketplace-game-development/publish-to-the-marketplace): وسِم المستودع وانطلق.

## انظر أيضًا

- [مرجع SDK اللعبة](/docs/marketplace-game-development/game-sdk-reference): كل تصدير، كاملًا.
- [عدّة engine-kit](/docs/marketplace-game-development/engine-kit): الطريقة الاختيارية للحصول على الحتمية مجانًا.
