NarraLeaf

清单

manifest.json 中的每一个字段——入口、完整的 contributes 接口,以及为什么大多数权限是派生出来的、而不是手写的

manifest.json 是 Studio 唯一会在不执行你的代码的情况下读取的文件。它声明插件提供了什么,而安装提示中显示的一切都由它计算得出

{
  "manifestVersion": 2,
  "id": "yourname.hello",
  "name": "Hello",
  "version": "1.0.0",
  "publisher": "Your Name",
  "description": "A starter plugin.",
  "entries": { "studio": "main.js", "runtime": "runtime.js" },
  "contributes": {
    "blueprintNodes": ["yourname.hello.log"],
    "runtimeCapabilities": ["store"]
  },
  "permissions": []
}

顶层字段

字段必填说明
manifestVersion始终为 2。版本 1 会被拒绝
id带命名空间:publisher.plugin-name,全小写,至少一个点
name显示名称
version语义化版本(1.0.0),可带预发布或构建后缀
publisher显示在插件列表中
description一行简介,安装时显示
entries{ studio?, runtime? }——相对于包的 ESM 路径。至少一个;两个文件都必须存在
contributes插件提供的一切。省略某个键等同于空列表
permissions只包含作者声明的提权能力——见下文

入口路径不能是绝对路径,也不能包含 ...、空字节、?#

contributes

contributes 是插件能做什么的唯一真相来源。Studio 无需运行插件代码就能据此校验一个项目,安装时的权限集合也由它派生而来

类型声明了什么
blueprintNodesstring[]本插件提供的蓝图节点类型
widgetsstring[]本插件提供的控件元素类型
localesobject[]Studio 语言包——插件新增或补全的语言
runtimeDatastring[]随游戏一并发布的插件存储命名空间
runtimeCapabilitiesstring[]runtime 入口可以使用的能力域
sidecarsobject[]随作者游戏一同发布的原生子进程
buildDependenciesobject[]构建时抓取并缓存的外部二进制文件
testsstring[]本插件提供的测试类型,供 Run > Test 使用
buildConfigobject[]本插件往构建对话框里加的构建配置字段
externalLinksstring[]runtime 入口可以在玩家浏览器里打开的地址模式
networkstring[]runtime 入口可以向其请求字节的主机模式

每一个节点类型、控件类型、存储命名空间、sidecar id 和构建时依赖 id 都必须以你的插件 id 为前缀。contributes 下出现未知的键会被拒绝,而不是被忽略

blueprintNodes 与 widgets

注册一个你没有声明的类型会在加载时抛错,两个入口都是如此。正是这份声明,让 Studio 能在构建之前就告诉作者:项目里用到的某个节点没有 runtime 提供方——而不是发布一个带着无效节点的游戏

runtimeData

插件存储位于项目的编辑器目录下,而那个目录永远不会被打包。如果你的 runtime 入口需要作者在 Studio 中编写的数据——一份目录表、一张查找表——就把这些命名空间列在这里,并用 app.game.data.readJson(namespace) 读取

{ "contributes": { "runtimeData": ["yourname.hello.catalog"] } }

这个列表是一份显式白名单,这样纯编辑器的插件状态就不会意外泄漏进发布的游戏。readJson同步的——数据随包一起分发——当命名空间未被声明、项目从未写入过,或游戏早于该数据被发布时,它返回 null。请优雅降级;不要假定作者数据一定存在

runtimeCapabilities

十个能力域,每一个对应 app.game 的一个命名空间。未声明的域在对象上根本不存在,而不是一个会抛错的方法。声明了它却没有 entries.runtime 是清单错误

{ "contributes": { "runtimeCapabilities": ["store", "events", "state.read"] } }

store · events · state.read · state.write · saves.read · saves.write · ui.overlay · assets · locale

完整模型、每个能力具体授予什么,以及各环境如何进一步收窄它:Runtime API

sidecars 与 buildDependencies

两个重量级声明。sidecar 是随作者构建的游戏一起发布的原生子进程;构建时依赖 是 Studio 在构建时下载、校验并缓存的外部二进制文件。两者都以 <platform>-<arch> 为键,也都必须提供 sha256

locales

一个 Studio 语言包——插件新增的语言,或它为内置语言补上的缺口。参见 创建第一个插件。只包含 contributes.locales、没有任何入口代码的清单也是一个有效的插件

权限是派生的

安装权限分为两类,这个划分正是重点所在

作者声明的——由你写在 permissions[] 里。它们是与 contributes 无关的 Studio 提权控制项:

{
  "permissions": [
    { "kind": "filesystem", "path": "/absolute/path", "mode": "readwrite", "recursive": true },
    { "kind": "api", "capability": "bash.execute" }
  ]
}
类别形状
filesystem{ path, mode: "read" | "write" | "readwrite", recursive }——一个真实的路径字符串
api{ capability }——目前插件只能使用 bash.execute

这些只影响 studio 入口。授权按 pluginId@version 记录,所以提升版本号就需要作者重新批准一次

派生的——由 Studio 从 contributes 计算得出,绝不由你手写:

类别派生自
runtimecontributes.runtimeCapabilities 中的每一项
sidecar每个 contributes.sidecars 条目,以及它所发布的平台键
buildDependency每个 contributes.buildDependencies 条目,以及它下载所用的主机名
externalLinkcontributes.externalLinks,用作者自己写的模式原样列出 —— 不改写,好让提示与清单是同一份文件
networkcontributes.network。与 externalLink 是两个问题,永远不合并:打开一个页面只是把地址交给浏览器、什么都不会回来,而这个是取字节,取回来的东西会在游戏里跑

手写 runtimesidecarbuildDependencyexternalLinknetwork 权限是清单错误——插件会安装失败,并提示 "permission kind … is derived from contributes and must not be declared by hand."。在 contributes 中把能力声明一次,权限自然随之而来

这正是安装提示能保持诚实的原因:一项能力只在一个地方声明,因此提示列出的内容与插件真正能触及的内容不可能漂移开来。它也让更新的行为变得正常——新增一项能力会扩大派生出的权限集,于是自动重新向作者索取批准;而没有扩大任何范围的版本则沿用已有的授权

校验失败

症状检查
安装被拒绝manifest.json 不是合法 JSON,或 id / version / entries 未通过校验
"requires a runtime entry"声明了 runtimeCapabilitiessidecars,却没有 entries.runtime
"must not be declared by hand"把某个派生类别的权限写进了 permissions[]
"Unknown plugin runtime capability"runtimeCapabilities 里有拼写错误。这个列表是封闭的
加载时注册抛错某个节点或控件类型不在 contributes 中,或没有以插件 id 为前缀
预览报 "Plugin validation failed"项目用到的某个节点或控件没有 runtime 提供方——插件被禁用、缺失、没有 entries.runtime,或该类型未被声明
安装时摘要不匹配某个 sidecar 文件的字节与它的 sha256 不符

不派生任何权限的两种贡献

testsbuildConfig 是唯两种不会产生安装权限的贡献,且两者都是裁定而不是漏掉

其余每一种带代码的贡献,在插件装载那一刻就拿到了一项能力。而测试只有在作者打开 Run > Test、在对话框里挑中它、再按下 Start 时才会跑;安装时根本没有一项“环境里一直在”的能力需要同意,而测试跑起来之后能碰什么,由它自己的 requires 单独把关。而构建配置字段只是一个留给作者填的空,填了也不会让插件多碰到任何东西。

本页目录