重载生命周期
基本信息
| 属性 | 值 |
|---|---|
| 入口 | MineTweakerImplementationAPI.reload()(MineTweakerImplementationAPI.java:634-701) |
| 触发点 1 | 服务器启动 onServerStart(MineTweakerImplementationAPI.java:610-613) |
| 触发点 2 | /minetweaker 重载指令 |
| 触发点 3 | 客户端建连 onClientAboutToConnect(MineTweakerMod.java:181-188) |
| 生命周期钩子 | 3 个(onRollbackEvent / onReloadEvent / onPostReload) |
| 钩子可从脚本调用 | 否 —— MineTweakerImplementationAPI 全文 0 个 ZenScript 注解 |
| 脚本分发 | platform.distributeScripts(...),受 sendScriptsOnReloading 控制 |
五个阶段
reload() 的执行顺序(MineTweakerImplementationAPI.java:634-701):
| # | 动作 | 源码行 |
|---|---|---|
| 1 | blockInfoPlayers.clear() —— 清空方块信息监听者缓存 |
:635 |
| 2 | logger.clear() + events.clear() —— 清日志与全部事件订阅 |
:637-638 |
| 3 | 若 server != null,重新挂上内置的登录/登出监听 LISTEN_LOGIN / LISTEN_LOGOUT |
:640-643 |
| 4 | MineTweakerAPI.tweaker.rollback() —— 逆序撤销上次全部动作 |
:645 |
| 5 | 发布 ONROLLBACK |
:646 |
| 6 | 若 server != null 且未注册过 minetweaker 指令,则注册 /minetweaker(别名 mt / crafttweaker / ct,OP 限定) |
:648-688 |
| 7 | 发布 ONRELOAD |
:691 |
| 8 | MineTweakerAPI.tweaker.load() —— 重新加载脚本 |
:693 |
| 9 | 若 server != null,platform.distributeScripts(tweaker.getScriptData()) |
:695-697 |
| 10 | 发布 ONPOSTRELOAD |
:699 |
三个钩子的语义:
| 钩子 | 注册方法 | 发布行 | 典型用途 |
|---|---|---|---|
ONROLLBACK |
onRollbackEvent(:593-594) |
:646 |
释放脚本占用的外部资源 |
ONRELOAD |
onReloadEvent(:597-598) |
:691 |
读取即将生效的新状态 |
ONPOSTRELOAD |
onPostReload(:601-602) |
:699 |
依赖配方已就绪的收尾工作 |
三个钩子共用同一个空载体类 ReloadEvent(MineTweakerImplementationAPI.java:730-731),签名均为 (IEventHandler<ReloadEvent>) → IEventHandle。三个 EventList 字段声明在 :60-62。
限制:钩子仅 Java 可见
MineTweakerImplementationAPI.java 全文 @ZenMethod / @ZenClass 出现次数为 0(grep -nE '@ZenMethod|@ZenClass' src/main/java/minetweaker/MineTweakerImplementationAPI.java 无输出)。因此脚本里没有 events.onReload(...) 这类调用;这三个钩子只能由 Java 侧模组注册。同理 ReloadEvent 自身也没有 @ZenClass,即便拿到实例脚本也无类型可用。
步骤 3 的内置监听
LISTEN_LOGIN / LISTEN_LOGOUT 是 1.7.10 侧的固定监听器(MineTweakerImplementationAPI.java:641-642),用于把脚本数据推给玩家。它们在每次 reload() 后被重新挂上,因为上一步 events.clear() 已清空所有订阅。
步骤 6 的分发与配置
distributeScripts 实现(mc1710/MCPlatformFunctions.java:32-36):
public void distributeScripts(byte[] data) {
if (MineTweakerConfig.sendScriptsOnReloading)
MineTweakerMod.NETWORK.sendToAll(new MineTweakerLoadScriptsPacket(data));
}
sendScriptsOnReloading 默认 true(MineTweakerConfig.java:31-35)。关闭后 /minetweaker 重载不会把新脚本推给客户端,客户端将继续用登录时收到的旧脚本。
客户端侧接收由 MineTweakerLoadScriptsHandler 处理,额外要求 MineTweakerAPI.server == null(即当前不在单机/集成服里)且 handleLoadScripts 为 true(network/MineTweakerLoadScriptsHandler.java:24),然后换成 ScriptProviderMemory 并 reload()。
登录时的首次分发
与重载分发独立:FMLEventHandler.onPlayerLoggedIn 在玩家是 EntityPlayerMP 且 sendLoadScripts 为 true 时单发一个 MineTweakerLoadScriptsPacket(FMLEventHandler.java:41-51)。sendLoadScripts 默认 true(MineTweakerConfig.java:29-30)。
antiStuck 与重复加载
MineTweakerMod 用 loaded 标志 + antiStuck 配置决定重连时是否跳过重载(MineTweakerMod.java:174-178、:185-187):
| 场景 | loaded == false |
loaded == true |
|---|---|---|
antiStuck == false(配置默认) |
onServerStart(srv) → 完整重载 |
完整重载 |
antiStuck == true |
onServerStart(srv) → 完整重载 |
只赋值 MineTweakerAPI.server = srv,跳过重载 |
客户端侧同理:antiStuck 为 true 且已加载过时,onClientAboutToConnect 直接 return(MineTweakerMod.java:185)。
注意一处不一致:
MineTweakerConfig.java:12的字段初始值把antiStuck写成true,但load()里config.getBoolean("antiStuck", "core", false, ...)的默认参数是false(MineTweakerConfig.java:42-46)。由于load()在预初始化期必定执行并覆写全部 6 个字段(MineTweakerMod.java:134-135),字段初始值true是死代码,实际默认行为由 cfg 默认参数false决定。配置文件首次生成时写入的是false。