琳琅运行时是安装在 Minecraft 服务器中的插件,插件名为 LinlangRuntimeBukkit。它提供 Linlang 的具体功能,并负责管理这些功能的生命周期。
服务器只需要安装一个运行时。每个使用 Linlang 的插件会从这个运行时取得一个独立的 Linlang 对象,这个对象称为 Facade,也就是当前插件的服务入口。
可以把两者理解为:
插件只依赖 linlang-api 编写代码,不直接依赖 Runtime、Core 或 Bukkit 实现类。
运行时启动后,会向 Bukkit ServicesManager 注册一个 Linlang 服务。使用方插件调用 Lin.init(this) 或 Lin.setup(this, options) 时,API 找到这个服务,并要求运行时为当前 JavaPlugin 创建 Facade。
flowchart LR
Server["Bukkit / Paper"] --> Runtime["LinlangRuntimeBukkit"]
Runtime --> Services["ServicesManager"]
Plugin["使用 Linlang 的插件"] -->|"Lin.setup(this)"| Services
Services --> Runtime
Runtime --> Facade["当前插件的 Linlang Facade"]
Facade --> Modules["文件、命令、消息、界面、审计"]
在代码内部:
BukkitLoader 负责启动和关闭运行时插件。RuntimeCore 保存服务器级共享资源,并创建、登记和关闭 Facade。FacadeCore 管理某个插件的服务与生命周期。BukkitPlatformAdapter 把 Core 的请求转换为 Bukkit API 操作。Linlang 向开发者提供唯一入口 Lin。
不同插件共享 Runtime 的实现代码,但各自保存业务数据。
| 运行时共享 | 每个插件独立 | ||
|---|---|---|---|
| Bukkit 平台适配器 | 配置服务与语言服务 | ||
| 运行时事件总线 | 命令服务与消息服务 | ||
| 日志与审计 Provider | 全局语言与消息前缀 | ||
| 内建消息和问题代码 | Facade 事件总线 | ||
| 服务创建与关闭逻辑 | 文件目录与审计租户 |
运行时根据传入的 JavaPlugin 判断资源属于哪个插件。插件 A 的配置文件不会写入插件 B 的目录,插件 A 的命令、语言和日志设置也不会修改插件 B 的 Facade。
界面核心和数据服务由 Runtime 按插件所有者管理,同样不会在不同插件之间混用。
服务器管理员需要将 linlang-runtime-bukkit Jar 放入服务器的 plugins 目录。插件项目只以 provided 方式依赖相同版本的 linlang-api,并在 plugin.yml 中声明:
depend:
- LinlangRuntimeBukkit
这样 Bukkit 会先启用运行时,再启用依赖 Linlang 的插件。依赖与开发环境配置请见:依赖引入 ⇱。
插件在 onEnable() 中初始化,在 onDisable() 中关闭:
public final class MyPlugin extends JavaPlugin {
private Linlang lin;
@Override
public void onEnable() {
lin = Lin.setup(this, new LinOptions()
.pluginLogger(true)
.totalLocale("zh_CN"));
}
@Override
public void onDisable() {
if (lin != null) {
lin.close();
lin = null;
}
}
}
初始化后,从同一个 lin 获取服务:
lin.linFile();
lin.linCommand();
lin.linMessenger();
lin.linView();
lin.linAudit();
Lin.init(this) 使用默认设置。Lin.setup(this, options) 会在初始化时同时应用语言、前缀和日志方式。更多初始化写法请见:初始化 ⇱和设置项 ⇱。
每个插件启用周期只应初始化一次。不要在命令或事件中重复调用 Lin.init(this),否则会为同一个插件创建额外的 Facade。
Lin.find() 返回共享运行时入口,不是插件自己的 Facade。普通插件不应修改或关闭它。
运行时会在启动和插件初始化时检查版本兼容性。兼容规则、插件依赖版本的声明方式以及后续更新查询入口,请见:版本兼容性 ⇱。
从 2.6.0.0 开始,日常设置和重载都保留 Facade 及其服务实例。切换语言不再清空命令注册,也不需要重新绑定文件对象。
| 操作 | 实际行为 | ||
|---|---|---|---|
lin.reload() | 刷新审计配置、配置和语言;成功后执行插件回调,再刷新 GUI 定义 | ||
lin.settings().apply() | 原地应用设置,切换语言时加载目标语言,不重读无关配置 | ||
lin.restart() | 重建文件、命令、消息与 GUI 服务,执行插件的重建回调 | ||
lin.close() | 关闭门面并释放资源 |
Bukkit 中应在主线程调用这些操作。递归重载、重载期间关闭,以及使用已关闭的 Facade 都会被拒绝。
业务缓存不会自动从配置对象重新计算。插件可以通过具名 onReload 回调更新缓存;完整顺序、失败处理和旧版迁移见重载机制 ⇱。
lin.close() 会注销当前 Facade,关闭命令交互和消息资源,停止 Facade 事件总线,并释放当前插件的界面、数据和审计资源。
服务器关闭时,Runtime 会先从 Bukkit 注销共享服务,阻止继续创建 Facade;随后关闭仍然存在的 Facade、运行时事件总线和数据资源,最后刷新审计文件队列。
Runtime 会清理未关闭的残留 Facade,但插件仍应在自己的 onDisable() 中主动调用 lin.close()。
运行时提供 /linlang 管理命令,权限为 linlangruntimebukkit.admin,默认授予 OP。
| 命令 | 作用 | ||
|---|---|---|---|
/linlang help | 查看运行时命令帮助。 | ||
/linlang info | 查看 API 与 Runtime 版本。 | ||
/linlang plugins | 查看已经创建 Facade 的插件。 | ||
/linlang reload <插件名称> | 只软重载指定插件的 Facade。 | ||
/linlang reload-all | 软重载 Runtime 和全部 Facade。 | ||
/linlang restart <插件名称> | 重建指定插件的门面服务;插件须先注册重建回调。 | ||
/linlang restart-all | 逐个重建已注册插件的门面服务,汇总成功数量。 | ||
/linlang problems | 查看全部内建问题代码。 | ||
/linlang problem <代码> | 查询问题代码的含义。 |
运行时命令的前缀、执行结果、列表标题、字段名称、命令说明和参数名称位于:
plugins/LinlangRuntimeBukkit/linlang/runtime/command/zh_CN.yml
plugins/LinlangRuntimeBukkit/linlang/runtime/command/en_GB.yml
您可以自行添加更多语言。
运行时按照自身的全局语言选择文件。单插件重载使用 reload-plugin 下的提示,全部重载使用 reload-all,旧的 reload 提示不再使用,避免升级后仍显示旧的全部重载文案。执行 /linlang reload-all 后,修改过的提示消息、命令帮助说明、参数名称和问题目录说明会通过语言引用立即生效,prefix 也会重新接入命令服务与消息服务。问题代码、组件标识和文档路径保持稳定;问题含义和处理建议由问题目录语言包提供,语言包不可用时使用 Runtime 内建文本。
restart 和 restart-all 会替换文件、命令、消息与 GUI 服务,但不调用插件的 onEnable()。插件必须通过 onRebuild 恢复文件绑定、命令和 GUI 注册;未提供回调时拒绝重建。旧 GUI 会话关闭,数据库和审计资源保留。
更新 Linlang 后应重新启动服务器以重新加载,不应使用任意形式的热重载。
讨论