Skip to content

你该写哪一种 ​

启动器有两套完全独立的插件系统。选错了会白写一遍,所以先用三个问题确定。

三个问题 ​

问题一:你是想给启动器增加「能装的东西」,还是增加「界面」?

  • 增加能装的东西(一个新的下载站、一个新的内容来源)→ 内容源
  • 增加界面(一张卡片、一个页面、一个按钮)→ UI 插件

问题二:你有一个现成的网站或 API 要接进来吗?

有,而且它提供的是模组 / 整合包 / 资源包 / 光影 / 数据包 / 世界 / 皮肤中的某几种 → 内容源。

问题三:你要做的事需要读启动器的内部状态吗?(实例列表、当前选中的实例、安装进度、日志)

需要 → UI 插件(内容源拿不到这些)。

对照表 ​

你想做的写哪个为什么
接入「某某模组站」,让它的模组能在启动器里搜到并安装内容源这就是内容源的定义
接入一个地图站,用户能浏览并下载地图内容源世界(world)是内容类型之一
在侧边栏放一张显示「今日一言」的卡片UI 插件需要往界面里塞东西
给启动器加一个「服务器列表」页面UI 插件需要注册路由
主页显示当前实例的游玩时长UI 插件需要读启动器内部状态
让用户能从 CurseForge 装整合包内容源整合包是内容类型之一
局域网联机(把世界广播到局域网)UI 插件需要 lan 权限,且要配界面
换一套主题配色UI 插件需要注入 CSS
把某个站的皮肤接进来内容源皮肤(skin)是内容类型之一

两者的硬性差异 ​

内容源UI 插件
manifest 里的 type没有这个字段(就是内容源)"ui" 或 "sidecar"
安装目录<数据目录>/content-sources/<id>/<数据目录>/plugins/<id>/
索引文件content-sources.jsonplugins.json
提交表单「提交内容源」「提交插件」
入口导出activate(api) 里调 api.register(provider)activate(api) 直接用 api
能力声明capabilities 对象(search/browse/…)permissions 数组(style/slot:…/…)
权限模型只有 hosts 白名单,没有审批逐项声明 + 用户审批
有界面吗没有,一个 DOM 都碰不到有,能放组件、注册页面、注入 CSS

它们不是「同一个系统的两个模式」

两套 manifest 的字段名、目录、加载器、权限模型都不一样。你不能把一个内容源装到 plugins/ 里,反过来也一样。一个仓库通常只做一件事——官方的四个内容源和两个 UI 插件就是六个独立仓库。

可以同时写吗 ​

可以,但要分成两个插件:一个内容源 + 一个 UI 插件,两个仓库、两份 manifest。

它们之间没有直接的通信方式。内容源拿不到 UI 插件的状态,UI 插件也调不了内容源的 provider。它们唯一的交汇点是启动器自己的数据:

  • 内容源可以把东西写进 api.storage,UI 插件读不到(存储是按插件隔离的)。
  • 内容源安装的内容会出现在实例的「内容」页里,UI 插件可以通过 hostapi:instance.list 之类看到实例,但看不到内容源的细节。

所以如果你的想法需要「一个界面 + 一个下载源」紧耦合,通常说明只写 UI 插件就够了——UI 插件自己就能发网络请求(network:<host> 权限)、自己读文件(通过宿主接口),不需要内容源那一层。

内容源那一层存在的意义是:让内容出现在启动器统一的浏览页里,和 Modrinth 的内容混在一起被搜索、筛选、排序。 如果你的内容不需要这个,就别写内容源。

还是拿不准 ​

问自己一个问题:「用户会怎么找到你提供的东西?」

  • 在启动器的浏览页里搜到、点进去、装到某个实例 → 内容源
  • 在启动器的某个界面位置看到、点开、在插件自己的页面里操作 → UI 插件

如果答案是「用户根本不需要看到界面,他只是想搜到并安装」,那就是内容源。


决定了?去第一个内容源或第一个 UI 插件。

Celestial Launcher 基于 Modrinth App 构建,遵循其开源许可。