Caputchin
应用市场游戏开发

caputchin.json 清单

你仓库根部的 caputchin.json 是一个应用市场游戏的作者加索引器的真相来源。索引器在服务端读它,以得知这个游戏的身份、它的包住在哪里、它提供哪些预设、以及如何回放它。它 绝不 在浏览器里读;SDK 不在运行时加载它。

这一页是那个字段参考。构建见 构建一个应用市场游戏;发布见 发布到应用市场。确切的类型由 SDK 作为 GameManifest 导出(见 SDK 参考)。

一个最小的单一游戏

{
  "terms_accepted": true,
  "license": "MIT",
  "marketplace": {
    "name": "Leaf Memory",
    "description": "Match pairs of tropical leaves before the timer runs out.",
    "preview": "preview.png"
  },
  "npm": "@your-org/leaf-memory",
  "entry": "dist/leaf-memory.js"
}

顶层字段

字段必需用途
terms_accepted索引时必须是字面量 true,以确认你接受 应用市场提交条款。任何其他值(或缺失)都把这个清单从索引里剔除。仅自托管的清单可以省略它。
license索引时一个涵盖你代码和捆绑资源的 SPDX 标识符或表达式。必须求值为一个被批准的标识符(见 发布错误参考)。
marketplace索引时它的存在就是那个“索引我”的信号。缺失意味着一个应用市场忽略的有效 自托管 游戏。见 marketplace 块
npmentry索引器解析成一个 jsDelivr URL 的 npm 包坐标。
entrynpm指向构建出的包的仓库相对路径。一个可运行的游戏必须有 entrynpm 或两者。
games集合时一个集合包装器的子清单路径。和 entry / npm 互斥。见 集合
run可选一个专用的 headless 回放工件。见 run 工件
preferred可选宿主 可以 遵从的呈现提示。见 preferred 块
locales / skins / configurations可选iframe 里 SDK 消费的预设块。见 预设块

没有 version 字段:索引器自己把包钉到一个不可变的 ref(已发布的 npm 版本或解析出的提交 SHA)。

marketplace 块

字段用途
name卡片标题。回退到仓库名(单一 / 包装器)或 leaf-dir 名(集合子项)。
description卡片副标题和详情页正文。
preview图像路径(相对于仓库根)或绝对 URL。大约 600x315,主体居中。
version给详情页用的仅显示版本字符串。可选。
support作者声明的兼容性标志(responsivetouchaccessibleaudio、……),平台绝不核实;作为筛选器和卡片图标浮现。
author可选的 { name?, url?, email? }name / url 作为详情页上的一行作者署名渲染;email 绝不显示,是 发布失败邮件选择加入。这三个子字段相互独立。

这个块完全可选。省略 name / url,详情页就只是不显示作者署名(页面别处显示的 GitHub 所有者是一个独立的、始终存在的身份,不是这个署名的回退)。省略 email,你就收不到发布失败通知。只设你想要的那些子字段。

分发指针

索引器把每个游戏的包钉到一个不可变的 ref,好让存储的完整性哈希保持有效,并在每次运行时重新解析(每日 cron 加手动的“发布或更新”):

  • entry + npm 解析为 cdn.jsdelivr.net/npm/<npm>@<resolved-version>/<entry>
  • entry 解析为 cdn.jsdelivr.net/gh/<owner>/<repo>@<commit-sha>/<entry>
  • npm 解析为被钉版本上的包默认入口。

没有用户侧的版本固定;要冻结一个构建,用户通过组件的 game-src自定义游戏)自托管它。

run 工件

默认地,回放自检跑你实时的 entry 包。当你的游戏很大、或基于框架或 WASM 时,改为交付一个专用的精简 headless run 工件:

{
  "run": {
    "entry": "dist/run.js",
    "modules": [
      { "name": "sim.wasm", "type": "wasm", "path": "dist/sim.wasm" }
    ]
  }
}
字段含义
run.entry指向 headless run 包的仓库相对路径(回放契约run 导出)。
run.modulesrun 按 name 导入的模块条目的可选数组。

索引器强制的约束

这些在索引时被校验;一处违反会让发布以一个 manifest-error 失败(见 发布错误参考)。

run.entry

  • 必须是 JavaScript:basename 必须匹配 [a-zA-Z0-9_-]+.js。run 工件始终是 JS(WASM 作为一个模块交付,见下文)。
  • 必须是一个干净的仓库相对路径:没有前导斜杠、没有 .. 穿越、没有空白、没有 ? / #、没有 scheme:// 前缀。

每个 run.modules[] 条目{ name, type, path }

  • name 必须匹配 [a-zA-Z0-9_-]+.(wasm|js):一个模块要么是 JS 要么是 WASM,别无其他。name 是 entry 所用的那个导入说明符。
  • type 必须和扩展名一致:一个 .wasm 名用 wasm,一个 .js 名用 js
  • name 不能是回放宿主内部使用的一个 保留 名(entry.jsartifact.js)。
  • name 在数组内必须 唯一(不重复)。
  • path 必须是一个干净的仓库相对路径(和 run.entry 同样的规则)。
  • 至多 16 个模块。

省略 run 以直接回放实时的 entry。这个工件必须导出什么,见 回放契约

preferred 块

可选的 preferred 块携带呈现提示。每个键都是建议性的:宿主 可以 遵从它,而它绝不覆盖一个明确的嵌入属性。

{
  "preferred": { "width": 360, "height": 480, "layout": "modal" }
}

一个应该默认横跨它容器的响应式游戏,可以在任一轴上声明 "full"

{
  "preferred": { "width": "full", "height": 480 }
}
字段含义
width / height一个像素占位,或字面量 "full"。当嵌入把 width / height 留空时组件应用它(一个明确的嵌入值,包括 full,会改为获胜)。一个像素值把 iframe 调到那个数;"full" 把那个轴拉伸到填满父级,和一个嵌入 width="full" 同样的效果。省略以回退到组件的内置默认占位。
layout组件围绕游戏构建的那个外壳:inlinemodalfullscreen。仅当嵌入把 layout 留空时使用(它的默认 auto)。解析顺序:嵌入的 layout 属性、然后这个 preferred layout、然后 inline

这些提示只对平台在服务端解析的游戏(应用市场游戏,或一个不带站点密钥给出的游戏 id)被遵从。一个平台在挂载前读不到的自托管 game-src 包,把占位和 layout 提示都忽略。

预设块

localesskinsconfigurations 各声明一个可选的 schema(逐键的类型和文档)加上 presets(那些命名的选项库)。组件对着这些解析访客的选择,并把扁平化的结果作为 ctx 交给你的游戏。完整的字段类型目录是那份共享的 自定义模式参考;同样的类型驱动一个 自定义游戏的仪表盘模式

一个 skins.schema 字段可以是一个 color、一个资源(image / audio / video),或一个标量(booleannumberrangelist),用和 configurations 同样的约束形式({ "type": "range", "min": 0, "max": 24 }["dots","stripes"],等等)。标量皮肤值在 ctx.skin 里解析为它们的带类型值(一个 number 是一个真正的数),恰好像一个配置值;颜色和资源值解析为字符串。

一个皮肤预设的 _theme 声明它在哪种模式下工作:lightdarkany(省略它意味着 any)。一个 lightdark 预设只在那种模式里显示;一个 any 预设在任一背景上都好看且对两者都有资格。每种模式有一个默认。一个标了 _default: true 的预设是它有资格的那个/那些模式的默认,所以一个 any 默认覆盖两者;当几个有资格的预设为一种模式都被标为默认时,声明顺序里的第一个赢得它。把一个特定模式的预设列在一个 any 的之上,以给那种模式一个专属皮肤,而让那个 any 预设落到另一种。

.caputchin/ 拆分文件

预设块(尤其是完整的 locale 集)让 caputchin.json 变长。把任何一个维度移进 .caputchin/ 文件夹下它自己的文件里,让清单保持短:

caputchin.json
.caputchin/locales.json
.caputchin/skins.json
.caputchin/configurations.json

每个文件的顶层对象 就是 那个维度块({ schema?, presets })。三者都可选。优先级是整维度替换、caputchin.json 获胜:如果一个维度同时内联和作为文件声明,就用内联块、忽略文件(发布会警告,好让你知道它是死的)。把每个维度恰好放在一个地方。

集合

一个仓库可以交付好几个游戏。一个集合包装器声明子路径而非 entry / npm

{
  "marketplace": { "name": "Caputchin Core Pack", "description": "The official pack." },
  "games": ["./packages/leaf-memory", "./packages/dino-runner"]
}

每个路径指向一个持有它自己 caputchin.json 的子目录。子项 id 是 owner/repo/<leaf-dir>。省略包装器的 marketplace 块,以索引这些子项而不带一个集合页面。

另见

本页内容