Create-Aeronautics:兼容性 + 漏洞修复

Create-Aeronautics:兼容性 + 漏洞修复

一个针对Sable及其相关问题的修复模组

游戏机制

FuckSable

F**kSable

一个专门修 Sable 的模组 因为 Sable 的作者挖坑不填,我来填

Sable 是个让 Minecraft 方块物理化的模组,想法很酷。但 Rust + Java 的缝合架构嘛……你懂的。这个模组的存在就是为了补那些能让服务器血压直接拉满的洞。


前置

  • Sable — 不是可选,毕竟我存在的意义就是擦它的屁股

社区


版本说明

当你发现某个版本标记为 Alpha(例如 v1.0.0-alpha.1),那大概是一个存在已知问题但未被下架的版本。Alpha 版本可能包含未完成的功能、已知的崩溃风险或性能问题,主要用于早期测试和反馈收集。

但请注意:Alpha 也包含 Beta 版本 —— 实际上某些 Beta 反而比 Alpha 更稳定。版本号只是个标签,不代表一切。

详细信息以 GitHub Releases 为准 —— 每个版本的发布说明都会清楚列出已知问题、适用范围和建议。不要只看版本号,看发布说明。

生产环境请优先选择经过充分测试的版本。如果你在 Alpha/Beta 版本中遇到了问题,欢迎到 Discord 反馈,但请理解——测试版就是测试版,崩了别意外。


修复项

默认全开,可通过配置文件单独关闭。依赖其他模组的修复项,缺模组会自动禁用。

Sable 核心修复

修复项 描述
async-save SubLevel 保存操作重定向到异步 I/O 线程,防止保存时服务器冻结。Sable 的 Rust 原生层在主线程做同步 I/O —— 稍微大点的物理结构就能把整个服务器卡死。
panic-guard 调用 Rust 原生代码前加安全检查,防止 Rust 侧 panic 把整个 JVM 带走。是的,Rust panic 会直接弄死 Java 进程,没有任何优雅处理。
write-flush 更新存储文件头之前先刷盘,防止崩溃时数据变垃圾。Sable 的存储格式在异常退出后基本就是不可读的废数据。
plot-holder-guard plot 区块的 holder 不存在时安全跳过方块更新,而不是直接崩溃。竹子长在物理结构旁边?服务器炸。
sublevel-entity-guard 防止 SubLevelInclusiveLevelEntityGetter 遍历异常 AABB 时服务器卡死。EntitySectionStorage 内部数据被并发修改损坏后,实体碰撞检测可能陷入无限循环 —— 一只蝙蝠的碰撞查询就能卡死服务器 60 秒以上。
command-block-sublevel-fix 禁止命令方块(及其变体)被放到物理化结构上。原版只禁止直接物理化命令方块,但你可以先放方块再物理化 —— 显然不是预期行为。
player-position-guard 玩家坐标超出世界边界时自动拉回,防止 SubLevel 物理把玩家扔出世界导致崩溃。Y 轴上限扩展至原版 +1000 格。
light-engine-bounds-guard 防止 SubLevel 区段超出世界高度限制时光照引擎崩溃。
physics-ticket-guard 防止 PhysicsChunkTicketManager 导致 DistanceManager 内部状态损坏(ArrayIndexOutOfBoundsException)。
sublevel-volume-limit 限制单个物理结构最大方块数量(8192,约 16x16x32 体积),防止过大的碰撞体导致 Rapier 原生崩溃。

兼容性修复

修复项 依赖 描述
carryon-compat CarryOn 修 CarryOn 把玩家放到物理化 sub-level 时传送到错误维度并崩溃。Sable 的射线检测返回局部坐标,CarryOn 当全局坐标用,玩家直接被扔进虚空。
typewriter-server-fix Simulated 修 Simulated 打字机在专用服务器上崩溃。服务端代码里出现了 net.minecraft.client.gui.screens.Screen —— 这玩意儿在专用服务器上根本不存在。客户端/服务端不分,离谱。
aeronautics-server-fix Aeronautics 修航空学 SteamVentBlockEntity 在专用服务器上崩溃。又是客户端类引用,这次是 Minecraft.getInstance()。同一个坑踩两次,真有你的。
aeronautics-slime-bearfix Aeronautics 修粘液球粘连结构时可以粘连旋转轴承导致其分离并产生穿模效应。默认关闭 —— 因为很多玩法基于这个 bug。
physics-staff-drag-clipfix Simulated 修物理手杖高速拖动物理结构时穿模和飞出世界边界。在 updatePose 中钳制 SubLevel 位置到世界边界内,超出则清零速度。Y 轴上限扩展至原版 +1000 格。默认开启。
cna-path-optimization Create New Age 优化 CNA 电网寻路性能。原实现用 ArrayList.contains() 做 BFS visited 检查 —— O(n) 查找,大电网上直接吃掉 18% 的 tick 时间。换 HashSet,O(1)。
cna-insert-optimization Create New Age 优化 CNA 能量插入逻辑,减少冗余寻路和消费者满时的提前退出。
ctt-concurrent-fix Create ThreadedTrains 修 CTT 和机械动力的并发问题。CTT 把火车 tick 移到工作线程,但只把 manageEntities 调度回主线程,updateContraptionAnchors 还在工作线程跑 —— 主线程读到不一致状态,EntitySectionStorage 里的 AVL 树被搞坏,轻则 NPE,重则整个服务器死循环卡死。
effortless-particle-fix Effortless, Sable 修 Effortless 对着 Sable 物理结构操作时客户端崩溃。Sable 射线检测返回 Plot 存储区域的远端坐标,Effortless 用该坐标生成粒子时客户端未加载该区块导致崩溃。修复方式:跳过未加载区块的粒子生成。

性能优化

普通优化默认启用,安全无副作用。激进优化默认禁用,效果显著但可能带来行为异常。

普通优化

修复项 描述
spatial-index-query queryIntersecting 从线性扫描改为基于区块的空间索引查询,O(n)→O(1)。
bbox-object-reuse 热路径 BoundingBox3d 对象通过 ThreadLocal 池复用,减少 GC 压力。
inblock-state-cache 缓存实体所在方块状态查询结果,避免每 tick 重复遍历。
level-accelerator-cache 启用 LevelAccelerator 的 Long2ObjectMap 区块缓存(原版硬编码关闭),提升碰撞检测区块查找命中率。
collision-aabb-prefilter SAT 碰撞检测前添加 AABB 预筛,跳过不相交方块的 15 轴 SAT 计算。
collision-skip-air 碰撞检测中空气方块直接返回空 VoxelShape,避免无意义的碰撞形状计算。
fastutil-sublevel-maps SubLevel 内部 HashMap 替换为 fastutil 原始类型 Map,减少 Map.Entry 对象分配。
physics-traversal-merge 物理管线 tick 中多次 SubLevel 遍历合并为单次遍历,减少迭代开销。
network-sync-batch 网络同步标记优化(原版已有批处理机制,此为补充标记)。

激进优化

修复项 风险 描述
dynamic-collision-substep 高速实体穿模 服务器 TPS 低时动态降低碰撞子步数。
skip-far-entity-collision 远处实体穿模 跳过超出阈值的远距离实体碰撞检测。
collision-volume-threshold 大型结构边缘碰撞失效 降低小体积方块的碰撞检测精度。
dynamic-physics-substep 物理精度下降 TPS 低时动态降低物理模拟子步数。
reduced-network-sync 客户端位置不同步 降低 SubLevel 状态网络同步频率。
skip-noncritical-collision 掉落物/经验球穿模 跳过非关键实体(掉落物等)的碰撞检测。

Create 模组优化

修复项 描述
create-kinetic-cache 缓存 KineticNetwork 应力计算结果,脏标记机制避免重复计算。
create-contraption-collision-radius 缩减装置实体碰撞检测半径,减少远距离碰撞计算。

调试命令

命令 作用
/fucksable 查看所有修复项状态
/fucksable <修复项> on/off 启用/禁用某个修复项
/fucksable all on/off 启用/禁用全部修复项
/fucksable default 恢复所有修复项为默认配置
/fucksable fstemp <方块ID> 遍历已加载区块查找指定方块(包含物理结构上的)—— 其实没啥用,你看命令名也猜得到吧?
/fucksable fs2temp on <坐标> 监控指定位置的方块更新,显示完整调用链(点击可复制)—— 和上面那个一样,临时的,没啥大用
/fucksable fs2temp off 停止监控

国际化 (i18n)

FuckSable 支持自定义语言包。所有可打印信息(命令反馈、日志等)都可以通过语言文件覆盖。

使用方法

/fucksablelang <语言文件名>
  • <语言文件名>: 语言文件的名称(不含路径,不含扩展名)

语言文件位于 .minecraft/config/fucksable/lang/(服务端则为 config/fucksable/lang/)。

默认语言包

文件名 语言
zh.yml 简体中文
en.yml 英文

创建自定义语言包

  1. 进入 config/fucksable/lang/ 目录
  2. 复制 en.yml 或 zh.yml 作为模板
  3. 重命名为你想要的语言文件名(例如 ja.yml、es.yml、fr.yml)
  4. 翻译 YAML 文件中的值(键名不要改)
  5. 在游戏中执行 /fucksablelang <文件名> 切换

注意事项

  • FuckSable 的可打印信息不是很多,所有文本加起来也就几十行
  • 你完全可以直接丢给 GPT 去翻译,几秒钟搞定
  • 切换语言后立即生效,不需要重启服务器
  • 如果语言文件格式错误(无效 YAML),会自动回退到默认语言(英文)

示例:自定义语言文件结构

# zh.yml - 简体中文示例

command:
  fucksable:
    title: "=== FuckSable 修复状态 ==="
    fix:
      enabled: "✓ %s 已启用"
      disabled: "✗ %s 已禁用"
      missing_dep: "⚠ %s 已禁用 (缺少依赖: %s)"
    invalid_fix: "无效的修复项: %s"
    set:
      enabled: "已将 %s 设置为 %s"
    lang:
      switched: "语言已切换至: %s"
      file_not_found: "语言文件不存在: %s"
      invalid_yml: "语言文件格式错误,已回退至默认语言"
# en.yml - 英文示例

command:
  fucksable:
    title: "=== FuckSable Fix Status ==="
    fix:
      enabled: "✓ %s is enabled"
      disabled: "✗ %s is disabled"
      missing_dep: "⚠ %s is disabled (missing dependency: %s)"
    invalid_fix: "Invalid fix: %s"
    set:
      enabled: "Set %s to %s"
    lang:
      switched: "Language switched to: %s"
      file_not_found: "Language file not found: %s"
      invalid_yml: "Invalid YAML format, falling back to default language"

为什么是 All-in-One?

Q: 为什么不写单独的模组逐个修复,而是全部打包在一起?

A: 因为我讨厌为了修复几个问题还要到处找补丁、一个个安装、反复排查冲突。

FuckSable 是全部打包:

  • 一个模组,一次安装,所有修复同时生效
  • 每一个修复都在真实服务端环境测试过,不是纸上谈兵
  • 配置文件可以单独开关,不需要就不开
  • 不用记这个 bug 归哪个模组管,一个命令全看见

简单说就是:省事。


吐槽

Sable 的 Rust + Java 架构本身不是问题。问题在于两边几乎都没有错误处理:

  • Rust 侧 panic → 直接带走 JVM
  • Java 侧对 Rust 返回值校验 ≈ 不存在
  • 服务端引用客户端类?这不是边缘 case,是专用服务器一启动就崩的程度

依赖管理也是一坨屎:API 类散落在各种包路径下 —— SubLevelContainerHolder 在 mixinterface.plot 而不是 api.sublevel,JOMLConversion 在 companion.math。你永远猜不到下一个类在哪个包里。

但话说回来,方块物理化这个想法确实很酷。只是希望作者能多花点时间在稳定性上,而不是只追求功能。


附加说明

自动更新走的是中国 IP,而且没有 SSL 保护。

默认是关闭的(除非你自己改了配置)。不要开。需要更新就去 Modrinth 下,防止供应链攻击。

"我暂时没想把它开源,但你们大可信任安全性 —— 毕竟 Modrinth 审核过了对吧?"

现在开源了。


许可证

MIT License - 详见 LICENSE 文件。