# Kemas game PlayDrop dengan catalogue.json dan .play

Susun folder hasil build, ZIP, atau .play dengan catalogue.json di root, deklarasikan media listingan dan server, lalu unggah dengan prepare_catalogue_upload.

Source: https://www.playdrop.ai/id/create/learn/packaging

Paket adalah hasil build sebuah game ditambah `catalogue.json` yang menjelaskannya. Paket adalah jalur untuk setiap fitur PlayDrop: beberapa berkas, SDK, gambar dan video listingan, papan peringkat, pencapaian, aset, dan server yang di-hosting. Unggah dari aplikasi AI Anda sebagai folder, ZIP, atau berkas `.play`.

## Susun foldernya

Letakkan `catalogue.json` di root, di samping game hasil build. Jalur di katalog bersifat relatif terhadap berkas itu.

```
catalogue.json
index.html
assets/sprites.png
assets/music.mp3
listing/icon.png
listing/hero-landscape.png
listing/hero-portrait.png
listing/screenshots/landscape-1.png
listing/videos/portrait-1.mp4
achievements/first-win.png
server/index.mjs
```

Berkas `.play` adalah folder ini yang di-zip dengan `catalogue.json` di root arsip, bukan di dalam folder pembungkus, lalu diganti namanya menjadi `.play`. Berkas ini hanya berisi berkas hasil build: tanpa `node_modules`, source map yang tidak ingin Anda publikasikan, atau berkas `.env`. Biarkan proyek sumber tetap di tangan pengguna Anda; PlayDrop tidak bisa mengembalikannya.

## Tulis catalogue.json

```json
{
  "apps": [{
    "name": "moon-jump",
    "displayName": "Moon Jump",
    "subtitle": "Lunar platform hopping against the clock",
    "description": "Jump between lunar platforms, collect oxygen and reach the flag.",
    "controls": "Tap or press Space to jump. Swipe or use arrows to steer.",
    "file": "index.html",
    "version": "1.0.0",
    "visibility": "PRIVATE",
    "authMode": "OPTIONAL",
    "tags": ["game-genre/casual", "game-genre/platformer"],
    "types": ["solo"],
    "controller": { "support": "optional", "type": "simpler" },
    "surfaceTargets": { "desktop": true, "mobileLandscape": true, "mobilePortrait": true },
    "primarySurface": "MOBILE_PORTRAIT",
    "sourceLocale": "en",
    "listing": {
      "icon": "listing/icon.png",
      "heroLandscape": "listing/hero-landscape.png",
      "heroPortrait": "listing/hero-portrait.png",
      "screenshotsLandscape": ["listing/screenshots/landscape-1.png"],
      "videosPortrait": ["listing/videos/portrait-1.mp4"]
    },
    "leaderboards": [{ "key": "best-time", "displayName": "Best time", "description": "Fastest run to the flag",
      "scoreType": "TIME_MS", "sort": "ASC" }],
    "achievements": [{ "key": "first-win", "displayName": "First flag", "description": "Reach the flag once",
      "kind": "STANDARD", "icon": "achievements/first-win.png" }],
    "server": { "entry": "server/index.mjs", "rooms": ["game"] }
  }]
}
```

- `file` adalah jalur persis halaman yang memulai game. Tanpa field ini, halaman masuk default-nya adalah `<name>.html`, bukan `index.html`.
- `version` adalah versi semantik baru untuk setiap unggahan game yang sama.
- `surfaceTargets` mencantumkan permukaan yang sudah Anda uji; `primarySurface` adalah permukaan yang menjadi rancangan game.
- Media listingan, ikon pencapaian, dan halaman masuk server diklaim oleh katalog: berkas di dalam `listing/` yang tidak dirujuk field mana pun bukan media listingan, dan berkas yang tidak diklaim aplikasi mana pun ditolak sebelum apa pun diterbitkan.
- `server.entry` menunjuk ke satu bundel modul ES hasil build; lihat [server game realtime](https://www.playdrop.ai/id/create/learn/game-servers).

Setiap field dan batasnya ada di [referensi catalogue.json](https://www.playdrop.ai/id/create/learn/catalogue-json). Tidak ada field versi katalog yang perlu ditetapkan.

## Unggah folder

Kirim `prepare_catalogue_upload` dengan `requestId` baru, `expectedCurrentVersion` (`null` untuk game baru), objek katalog lengkap, dan satu deskriptor per berkas. Berkas teks kecil bisa berupa `text` langsung. Setiap berkas lainnya adalah unggahan bertanda tangan: hitung ukuran, SHA-256 (64 karakter heksadesimal huruf kecil), dan MD5 base64-nya secara lokal dari byte yang persis. Permintaan untuk game dua berkas ini sudah lengkap; ganti dua placeholder digest pada ikon dengan nilai ikon yang sebenarnya.

```json
{
  "requestId": "moon-jump-1.0.0",
  "expectedCurrentVersion": { "moon-jump": null },
  "catalogue": {
    "apps": [{
      "name": "moon-jump",
      "displayName": "Moon Jump",
      "file": "index.html",
      "version": "1.0.0",
      "visibility": "PRIVATE",
      "authMode": "OPTIONAL",
      "tags": ["game-genre/casual"],
      "types": ["solo"],
      "controller": { "support": "off" },
      "surfaceTargets": { "desktop": true, "mobileLandscape": false, "mobilePortrait": true },
      "listing": { "icon": "listing/icon.png" }
    }]
  },
  "files": [
    { "path": "index.html",
      "text": "<!doctype html><html><head><meta charset=\"utf-8\"><title>Moon Jump</title></head><body><canvas id=\"game\"></canvas><script>/* built game */</script></body></html>" },
    { "path": "listing/icon.png", "contentType": "image/png", "sizeBytes": 50211,
      "sha256": "REPLACE_WITH_ICON_SHA256_HEX", "md5Base64": "REPLACE_WITH_ICON_MD5_BASE64" }
  ]
}
```

Respons mencantumkan URL unggahan dan header untuk `listing/icon.png`. Lakukan PUT byte ikon yang persis ke sana dengan semua header yang dikembalikan, lalu panggil `complete_catalogue_upload` dengan `requestId` yang sama. `url` di tanda terima membuka versi privatnya.

## Unggah ZIP atau .play

- **Agen yang memiliki sistem berkas** mengekstrak arsip secara lokal dan mengunggahnya sebagai folder, seperti di atas.
- **ChatGPT** memberikan arsip yang diambilnya dari percakapan sebagai `archive`, dengan `download_url` dan `file_id` aslinya, tanpa `catalogue` atau `files`. Arsip boleh berukuran hingga 20 MiB terkompresi.

Arsip harus berupa berkas ZIP biasa: tanpa enkripsi, beberapa volume, ZIP64, symlink, jalur duplikat, jalur absolut, atau `..`. Berkas kosong dan berkas catalogue.json bersarang ditolak.

## Batas

512 berkas atau entri arsip (termasuk catalogue.json dan direktori), 50 MiB per berkas, 200 MiB setelah diekstrak, dan 1 MiB JSON katalog. Bundel server yang di-hosting berukuran maksimal 2 MiB.

## Perbarui paket

Baca `list_games`, pertahankan `name` setiap aplikasi, naikkan `version`-nya, dan tetapkan `expectedCurrentVersion` ke versi terbitan saat ini. Kirim snapshot lengkap: setiap berkas dan field yang seharusnya dimiliki versi baru. Berkas yang tidak berubah dari versi yang dipertahankan bisa dikirim dengan `reuse: { gameId, version, path }` alih-alih byte-nya. Kunci papan peringkat dan pencapaian tidak bisa dihapus atau diubah tipenya; pensiunkan dengan `"status": "RETIRED"`.

## Aset yang belum bisa diunggah konektor

PlayDrop menjalankan model 3D yang dapat dieksekusi (aset `MODEL_3D` dengan subkategori `procedural` atau format `CUSTOM`) di alatnya sendiri, tetapi konektor menolaknya karena API tidak bisa memvalidasi kodenya. Bake model tersebut ke format yang didukung, atau rujuk aset yang sudah diterbitkan di PlayDrop. Gambar prosedural dan spritesheet yang sudah di-bake diterima. Lihat [aset game](https://www.playdrop.ai/id/create/learn/game-assets).

## Panduan terkait

- [catalogue.json](https://www.playdrop.ai/id/create/learn/catalogue-json): Field catalogue.json yang dipakai game PlayDrop: identitas, listingan, mode bermain, permukaan, login, Instan, pramuat, media, terjemahan, skor, dan server.
- [Terbitkan dari aplikasi AI Anda](https://www.playdrop.ai/id/create/learn/mcp-publishing): Cara agen AI menerbitkan ke PlayDrop lewat MCP: jalur sederhana dan jalur paket, pengujian privat, publish_game_version, pembaruan, batas, dan pemulihan.
- [Gambar dan video listingan](https://www.playdrop.ai/id/create/learn/listing-assets): Gambar dan video listingan game PlayDrop: jalur, format, dan orientasinya, serta cara membuat gambar yang efektif di feed, pencarian, dan pratinjau tautan.
- [Server game realtime](https://www.playdrop.ai/id/create/learn/game-servers): Jalankan server Colyseus 0.17 yang di-hosting dengan MongoDB untuk game Anda, hubungkan dari browser lewat SDK, uji dengan dua pemain, dan pahami batasnya.
