用 catalogue.json 和 .play 打包 PlayDrop 游戏
整理一个根目录带有 catalogue.json 的已构建游戏文件夹、ZIP 或 .play,声明商店页面媒体和服务器,并用 prepare_catalogue_upload 上传。
包是游戏的构建产物,加上一个描述它的 catalogue.json。所有 PlayDrop 功能都通过包来实现:多个文件、SDK、商店页面图片和视频、排行榜、成就、素材以及托管服务器。你可以从 AI 应用以文件夹、ZIP 或 .play 文件的形式上传它。
整理文件夹
把 catalogue.json 放在根目录,与已构建的游戏放在一起。catalogue 中的路径都相对于它。
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 文件就是把这个文件夹压缩成 ZIP(catalogue.json 位于压缩包根目录,而不是在外层文件夹中),再重命名为 .play。它只包含构建产物:不要包含 node_modules、你不想公开的源映射文件或 .env 文件。源代码项目由你的用户保管;PlayDrop 无法返回它。
编写 catalogue.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是启动游戏的页面的确切路径。没有它时,入口默认为<name>.html,而不是index.html。- 同一款游戏每次上传,
version都要使用新的语义化版本号。 surfaceTargets列出你测试过的显示形态;primarySurface是游戏设计时针对的显示形态。- 商店页面媒体、成就图标和服务器入口都由 catalogue 认领:
listing/下没有被任何字段引用的文件不是商店页面媒体,而没有被任何应用认领的文件会在发布任何内容之前被拒绝。 server.entry指向一个已构建的 ES 模块包;请参阅实时游戏服务器。
所有字段及其限制都在 catalogue.json 参考中。不需要设置 catalogue 版本字段。
上传文件夹
用新的 requestId、expectedCurrentVersion(新游戏为 null)、完整的 catalogue 对象和每个文件一个描述符发送 prepare_catalogue_upload。小的文本文件可以是内联 text。其他所有文件都使用签名上传:根据确切的字节在本地计算它的大小、SHA-256(64 个小写十六进制字符)和 base64 编码的 MD5。下面这个两文件游戏的请求是完整的;把图标上的两个摘要占位符替换成图标的真实值即可。
{
"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 到该 URL,并带上返回的所有请求头,然后用同一个 requestId 调用 complete_catalogue_upload。回执中的 url 会打开这个私密版本。
上传 ZIP 或 .play
- 有文件系统的 Agent 先在本地解压压缩包,再像上面那样以文件夹形式上传。
- ChatGPT 把它从对话中解析出的压缩包作为
archive传入,附上真实的download_url和file_id,不传catalogue或files。压缩包压缩后最大 20 MiB。
压缩包必须是普通的 ZIP 文件:不能加密、分卷、使用 ZIP64、包含符号链接、重复路径、绝对路径或 ..。空文件和嵌套的 catalogue.json 文件会被拒绝。
限制
512 个文件或压缩包条目(包括 catalogue.json 和目录),每个文件 50 MiB,解压后 200 MiB,catalogue JSON 最大 1 MiB。托管服务器包最大 2 MiB。
更新包
读取 list_games,保留每个应用的 name,提高它的 version,并把 expectedCurrentVersion 设为当前已发布的版本。发送完整快照:新版本应包含的每个文件和每个字段。保留版本中未改动的文件可以用 reuse: { gameId, version, path } 发送,而不必发送它们的字节。排行榜和成就的键不能删除,也不能更改类型;请用 "status": "RETIRED" 停用。
连接器暂时无法上传的素材
PlayDrop 会在自己的工具中运行可执行的 3D 模型(带有 procedural 子类别或 CUSTOM 格式的 MODEL_3D 素材),但连接器会拒绝它们,因为 API 无法验证它们的代码。请将它们烘焙为受支持的格式,或引用 PlayDrop 上已发布的素材。已烘焙的程序化图片和精灵图可以上传。请参阅游戏素材。