# catalogue.json과 .play로 PlayDrop 게임 패키징하기

catalogue.json을 루트에 둔 빌드 폴더, ZIP 또는 .play를 구성하고, 등록 정보 미디어와 서버를 선언한 뒤 prepare_catalogue_upload로 업로드하세요.

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

패키지는 게임의 빌드 결과물에 이를 설명하는 `catalogue.json`을 더한 것입니다. 여러 파일, SDK, 등록 정보 이미지와 동영상, 순위표, 업적, 에셋, 호스팅 서버 등 PlayDrop의 모든 기능을 쓰는 방식입니다. AI 앱에서 폴더, ZIP 또는 `.play` 파일로 업로드하세요.

## 폴더 구성하기

빌드된 게임 옆, 루트에 `catalogue.json`을 두세요. 카탈로그의 경로는 이 파일을 기준으로 한 상대 경로입니다.

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

`.play` 파일은 이 폴더를 압축하되 `catalogue.json`이 감싸는 폴더 안이 아니라 아카이브 루트에 오도록 한 뒤, 확장자를 `.play`로 바꾼 것입니다. 빌드된 파일만 들어가야 합니다. `node_modules`, 공개하고 싶지 않은 소스 맵, `.env` 파일은 넣지 마세요. 소스 프로젝트는 사용자가 보관하게 하세요. PlayDrop은 소스를 돌려줄 수 없습니다.

## 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`은 게임을 시작하는 페이지의 정확한 경로입니다. 지정하지 않으면 시작 파일은 `index.html`이 아니라 기본값 `<name>.html`이 됩니다.
- `version`은 같은 게임을 업로드할 때마다 새로 정하는 시맨틱 버전입니다.
- `surfaceTargets`는 테스트한 화면을 나열하고, `primarySurface`는 게임이 맞춰 설계된 화면을 지정합니다.
- 등록 정보 미디어, 업적 아이콘, 서버 진입 파일은 카탈로그가 지정합니다. `listing/` 아래에 있더라도 어떤 필드도 참조하지 않는 파일은 등록 정보 미디어가 아니며, 어떤 앱도 지정하지 않은 파일은 게시가 시작되기 전에 거부됩니다.
- `server.entry`는 빌드된 ES 모듈 번들 하나를 가리킵니다. [실시간 게임 서버](https://www.playdrop.ai/ko/create/learn/game-servers)를 참고하세요.

모든 필드와 제한은 [catalogue.json 참조](https://www.playdrop.ai/ko/create/learn/catalogue-json)에 있습니다. 따로 설정할 카탈로그 버전 필드는 없습니다.

## 폴더 업로드하기

새 `requestId`, `expectedCurrentVersion`(새 게임이면 `null`), 완전한 카탈로그 객체, 파일마다 설명자 하나를 넣어 `prepare_catalogue_upload`를 보내세요. 작은 텍스트 파일은 인라인 `text`로 넣을 수 있습니다. 그 밖의 파일은 모두 서명된 업로드입니다. 정확한 바이트로 크기, SHA-256(소문자 16진수 64자), base64 MD5를 로컬에서 계산하세요. 아래는 파일 2개짜리 게임의 완전한 요청입니다. 아이콘의 다이제스트 자리 표시자 두 개를 실제 아이콘 값으로 바꾸세요.

```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" }
  ]
}
```

응답에는 `listing/icon.png`의 업로드 URL과 헤더가 나옵니다. 반환된 모든 헤더와 함께 아이콘의 정확한 바이트를 그곳에 PUT으로 올린 다음, 같은 `requestId`로 `complete_catalogue_upload`를 호출하세요. 업로드 결과의 `url`로 비공개 버전을 열 수 있습니다.

## ZIP 또는 .play 업로드하기

- **파일 시스템이 있는 에이전트**는 아카이브를 로컬에서 풀어 위와 같이 폴더로 업로드합니다.
- **ChatGPT**는 대화에서 확인한 아카이브를 실제 `download_url`, `file_id`와 함께 `archive`로 넘기며, `catalogue`나 `files`는 넣지 않습니다. 아카이브는 압축 상태로 최대 20 MiB입니다.

아카이브는 일반 ZIP 파일이어야 합니다. 암호화, 다중 볼륨, ZIP64, 심볼릭 링크, 중복 경로, 절대 경로, `..`는 허용되지 않습니다. 빈 파일과 중첩된 catalogue.json 파일은 거부됩니다.

## 제한

파일 또는 아카이브 항목 512개(catalogue.json과 디렉터리 포함), 파일당 50 MiB, 압축 해제 기준 200 MiB, 카탈로그 JSON 1 MiB입니다. 호스팅 서버 번들은 최대 2 MiB입니다.

## 패키지 업데이트하기

`list_games`를 읽고, 각 앱의 `name`을 유지하고, `version`을 올리고, `expectedCurrentVersion`을 현재 게시 버전으로 설정하세요. 새 버전에 있어야 할 모든 파일과 필드를 담은 전체 스냅샷을 보내세요. 보관된 버전의 바뀌지 않은 파일은 바이트 대신 `reuse: { gameId, version, path }`로 보낼 수 있습니다. 순위표와 업적 키는 삭제하거나 유형을 바꿀 수 없으니, `"status": "RETIRED"`로 종료하세요.

## 커넥터로 아직 업로드할 수 없는 에셋

PlayDrop은 실행 가능한 3D 모델(`procedural` 하위 카테고리 또는 `CUSTOM` 형식의 `MODEL_3D` 에셋)을 자체 도구에서 실행하지만, API가 그 코드를 검증할 수 없기 때문에 커넥터는 이를 거부합니다. 지원되는 형식으로 미리 굽거나, PlayDrop에 이미 게시된 에셋을 참조하세요. 미리 구운 절차적 이미지와 스프라이트시트는 허용됩니다. [게임 에셋](https://www.playdrop.ai/ko/create/learn/game-assets)을 참고하세요.

## 관련 가이드

- [catalogue.json](https://www.playdrop.ai/ko/create/learn/catalogue-json): PlayDrop 게임이 쓰는 catalogue.json 필드: 식별 정보, 등록 정보, 플레이 방식, 화면, 로그인, 인스턴트, 미리 로드, 미디어, 번역, 점수, 서버.
- [AI 앱에서 게시하기](https://www.playdrop.ai/ko/create/learn/mcp-publishing): AI 에이전트가 MCP로 PlayDrop에 게시하는 방법: 간단한 방식과 패키지 방식, 비공개 테스트, publish_game_version, 업데이트, 제한, 복구.
- [등록 정보 아트와 동영상](https://www.playdrop.ai/ko/create/learn/listing-assets): PlayDrop 게임에 넣을 수 있는 등록 정보 이미지와 동영상의 경로, 형식, 방향, 그리고 피드, 검색, 링크 미리 보기에서 잘 보이는 아트를 만드는 방법을 알아보세요.
- [실시간 게임 서버](https://www.playdrop.ai/ko/create/learn/game-servers): MongoDB 데이터베이스가 있는 호스팅 Colyseus 0.17 서버를 게임에 쓰고, SDK로 브라우저에서 연결하고, 플레이어 2명으로 테스트하고, 제한 사항을 알아보세요.
