热重载与调试
Katton 的开发体验围绕“修改、重载、验证”设计。把脚本放进启用的脚本包,编辑 .kt 或 .java 文件,然后无需重启游戏或服务器即可重载。
重载方式
通常来说,我们使用 /katton reload 进行重载。
在 Fabric 和 NeoForge 上,它会重载服务端脚本;单人游戏中还会调度客户端脚本重载。在 Paper 上,它只重载服务端脚本包。
此外,还有以下几种触发重载的方式:
| 触发方式 | 效果 |
|---|---|
/katton reload | 重载 Katton 脚本并显示进度条。 |
/reload | 重载数据包;Katton 会接入服务端脚本重载。 |
F3 + T | 只重载 Minecraft 客户端资源;不会调用 Katton 脚本。 |
| 脚本包界面的 Reload 按钮 | 在 Fabric/NeoForge 客户端从界面触发重载。 |
| IDE 桥部署 | Katton IDEA 插件暂存快照并通过同一条串行路径重载,见 IDE 开发连接。 |
编译与进度
执行 /katton reload 时,Katton 0.5.1 在工作线程上准备世界包编译。客户端首次编译全局包时,也不占用渲染线程。首次编译期间,服务端重载进度会显示编译阶段。客户端激活时会复用准备好的编译产物。
如果脚本源码和依赖库未改变,Katton 可以复用磁盘中的编译缓存。稳定的宿主类路径顺序使缓存键在不同启动之间保持一致。仅修改清单不会强制重新编译 Kotlin;修改源码或包内库则会使缓存失效。阶段耗时日志分别记录编译、类路径索引、激活和资源重载。激活顺序见脚本加载生命周期。
重载时会清理什么
重载时,Katton 会在再次执行入口前清理脚本持有的状态。只会清理被替换的组件:没有变化的独立脚本包会继续运行。
| 状态 | 重载行为 |
|---|---|
| 事件监听器 | 清理后由脚本重新注册。 |
| 托管原生监听器 | 按脚本包作用域清理。 |
| 注入钩子 | 在 Fabric/NeoForge 上重置。 |
| 可重载注册表 | 清理所有权;对象软保留以避免 holder 崩溃。 |
| 数据包修改 | 清理后在 datapack apply 阶段重新应用。 |
| 客户端渲染回调 | 客户端重载时清理。 |
| 脚本的游戏阶段数据包接收器 | 在 Fabric/NeoForge 上随所属包清理并重新注册。见游戏阶段网络通信。 |
| Folia 调度任务 | 先分离;候选提交成功后,被跳过的单次任务会重新调度。 |
依赖事务
重载并不是所有脚本包“一刀切”。Katton 会解析 packDependencies 依赖图,计算每次变更受影响的消费者,并把工作分组成互相独立的事务:
- 只重载消费者时,未变化的依赖实例及其共享状态会保留。
- 重载被依赖包时,会在同一个事务中替换其消费者。
- 互不相关的组件各自提交,因此某个组件的失败不会撤销另一个已经提交的组件。
- 纯资源变更会保留已编译代码和运行状态,不会重放入口。
生命周期与重放
执行重载并不表示所有入口都会再次运行。全局包永不重放;世界包遵循注解的 replay;多人 SERVER_CACHE 包在服务器修订激活后始终全部重放。上下文中的 reason 为 HOT_RELOAD,cause 则指出是命令、数据包重载、服务器脚本包同步还是部署触发了本次调用。
完整阶段矩阵和上下文类型见脚本加载生命周期。
重载结果与回滚
候选失败不会拖垮正在运行的脚本。编译错误、依赖错误和资源失败会恢复受影响组件的受管回调与资源值,但不会重放旧入口;已经提交的独立组件保持活动。
回滚只覆盖 Katton 受管状态。任意副作用——世界修改、文件写入,以及通过第三方 API 修改的静态字段——不会被撤销。重载失败后,可用 /katton errors 查看记录的诊断。
