Repository navigation
Add data-driven cube selection for 26.2 为26.2添加数据驱动的模型选取与描边 - #281
Conversation
- 支持按命名空间和方块配置精确模型选取、仅模型描边及原版模式 - 按资源包优先级逐项合并规则,并在模型重载完成时同步应用规则与几何 - 保留动态部件和程序排除接口,分离描边与精确选取入口并限制邻格补扫范围 - 补充资源配置格式、覆盖顺序及 KubeJS 整合包接入说明
🌿 Roseau API Breaking Change Report
|
- 在跳过精确选取前刷新客户端帧缓存,避免仅描边模式冻结动态部件 - 保留嵌套射线调用的缓存保护及超出精确选取距离时的原版行为
Gugle2308
left a comment
There was a problem hiding this comment.
结论:PR #281 已在本次审查执行前被合并(squash c0e0d4c8,与 dev/26.2 tip 一致,合并树与 head 729c3ada 完全相同);本 PR 的补丁与 #280(26.1)逐行相同,我在 26.2 官方 client.jar 上复核了关键语义,未发现本 PR 引入的阻塞缺陷,仍有两条跟进项与 #280 同源。
一、合并态与三条版本线的关系
| 判据 | 结果 |
|---|---|
| state / merged / merged_at | closed / true / 2026-11-14T11:13:30Z(与 #276、#280 同一分钟合入) |
| merge_commit_sha | c0e0d4c87ae5f089ee0401926dac92f5342e946c,与 ls-remote dev/26.2 相同 |
| 合并树 vs head | git diff pr281h pr281b 为空 ⇒ 进主干的就是 729c3ada |
| 规模 | 7 文件 +194/−11,与 API changed_files=7 / additions=194 / deletions=11 一致,payload 里的 diff 覆盖完整 |
| head CI | build / build ✅(total_count: 1) |
| 既有评论 | 仅 Roseau API 报告 1 条(`cube |
| 与 #280 的关系 | 两个 PR 的 +/− 行逐行 diff 为空;核心 5 个 Java 文件在两条线的 head 上逐字节相同,CubeHighlightEvents 仅差 26.2 的提取期 API(gui.hud.isHidden()、getAppropriateLineWidth、submitCustomGeometry),PR 自身那一行相同 |
二、在 26.2 上复核过的关键语义(官方未混淆 client.jar 字节码)
-
包优先级方向仍正确。
MultiPackResourceManager.<init>先List.copyOf(packs)存packs,再按列表顺序 push 进各 namespace 的FallbackResourceManager;getResourceStack从size()-1倒序取、末尾Lists.reverse,listPacks()直接返回同一个packs⇒ 索引升序 = 低→高,CubeSelectionRules.java:43的升序排序 + 顺序merge与 README「从低到高逐项合并」一致。 -
路径参数与 id 前缀仍正确。
checkTrailingDirectoryPath与 26.1 同形:endsWith("/")为真才抛IllegalArgumentException⇒ 传"anvillib"(无尾斜杠)合法;FileToIdConverter.fileToId用path.substring(prefix.length() + 1, len - ext)反推,反证返回的 id 带anvillib/前缀 ⇒CubeSelectionRules.java:37的id.getPath().equals("anvillib/cube_selection.json")能命中任意 namespace 的assets/<ns>/anvillib/cube_selection.json。 -
规则读的是本轮包集。
ReloadableResourceManager.createReload的顺序仍是「close 旧resources→ newMultiPackResourceManager→putfield resources→SimpleReloadInstance.create」,且listPacks()/listResourceStacks()都委托同一个resources字段 ⇒ModifyBakingResult期间两处看到同一批包,packs.indexOf(sourcePackId())的索引与栈顺序同源。 -
逐帧链路成立。
Minecraft.renderFrame(boolean)里pick在GameRenderer.render之前(字节码偏移 403 < 520)⇒ 第 2 个提交的beginFrame前移一定发生在描边提取之前,仅描边模式下FRAME不再从首帧起冻结。 -
Identifier.createUntrusted仍校验。fromNamespaceAndPath走assertValidNamespace/assertValidPath,所以CubeSelectionRules.java:66用一次性Identifier校验 namespace 键的做法在 26.2 依然有效(非法键会让整份文件不生效,与 README 一致)。 -
其余结论沿用 #280: 规则与几何成对发布(
CubeModelEvents.java:22-24/:29)、install先写rules再重算(CubeSelection.java:128-131)、三种模式四个消费点收敛(target仅 MODEL、outlineTargetMODEL+OUTLINE、extendsBlock仅 MODEL、active()=pickingActive)、exclude优先于配置、公开面零增长(record/枚举/两个重载均包私有,与 Roseau「0 breaking」一致)。
三、跟进项
CubePicking.java:38-42(26.2,与 26.1 相同):
context.nearest = block;
context.distance = start.distanceToSqr(block.getLocation());
...
BlockGetter.traverseBlocks(start, block.getLocation(), context, ...);1.21.1 线同名方法 :38-43 多三行:
double distanceToHit = start.distanceToSqr(block.getLocation());
// Sable 等模组可能返回子空间存储坐标,不能从玩家位置跨坐标空间逐格补扫。
if (!(distanceToHit <= start.distanceToSqr(end) + 1.0E-7)) return vanilla;差别不只是变量复用:Entity.pick 的返回值也是被重定向过的点,第三方返回远处/子空间坐标时,26.2 会从 start 到该坐标逐格遍历整段射线,每格再跑 (2r+1)³ 次 getBlockState,且这是每帧一次;context.distance 同时失去「不超过射线长度」的上界。26.2 的 base 同样没有这层守卫(不是本 PR 引入),但三线现在共用同一份实现,建议一次补齐 26.1/26.2 两处,并在三线各留一句注释,避免下次再漂移。
ModelSelections.java:29-30 的 MAX_STATES = 32768 / MAX_GEOMETRY_BYTES = 16 MiB 超限时只做 unsupported++(:50、:55、:125-127),而 CubeSelection.statistics() 全仓无调用者。README 新推的正是 namespace 级配置("namespaces": {"minecraft": "model"} 这类整包开启),撞上上限的表现是「一部分方块仍走原版行为」且没有任何日志。建议在 CubeModelEvents.apply 里当 unsupportedStates > 0 打一条一次性 warn。
四、细节建议
packs.indexOf(...)的-1分支(CubeSelectionRules.java:39):命不中时该源被排成最低优先级(静默降级)。当前sourcePackId()与packId()必然命中,属防御性代码;建议Math.max(0, …)或 debug 日志。load()的异常边界(CubeSelectionRules.java:35、:37在逐文件 try 之外):listPacks()/listResourceStacks()抛RuntimeException时会穿出ModifyBakingResult监听器并让整次模型重载失败,外部资源包引发的问题应退化成EMPTY继续。- 公开重载
install(Snapshot)(CubeSelection.java:133)不重置rules:第三方ModelSelections.bake(models)+install(snapshot)会沿用上一轮包规则,当前自洽但语义隐式,建议在 javadoc 注明。 extendsBlock热路径(CubeSelection.java:193-196):新增的mode()每格要做一次注册表反查 + 4 次 map 查询,而原先是两次查询;extended只在install时按 MODEL 过滤过(命中即必为 MODEL),可把廉价判断前移,只对DYNAMIC分支保留 mode 判定。active()/isEnabled()语义收窄(签名不变、javadoc 已更):active()从「有模型或动态注册」变成「存在 MODEL 模式几何」。下游若用它做渲染/拾取判断需要知会。- README 139–140 行连续两个空行(
### RPC 模块之前,行号 141)。
五、验证边界
未执行 Gradle 构建、runClient 或 JUnit;结论来自:静态读码 + 三点 diff 对账(7 文件,与 API 计数逐项一致)+ 26.2 官方未混淆 client.jar 的字节码核对(listPacks / getResourceStack / checkTrailingDirectoryPath / createReload / Identifier.createUntrusted / Minecraft.renderFrame 内 pick 与 render 的偏移)+ 与 #280、#276 的逐行补丁比对 + GitHub API 与 head CI。观感类结论(描边与动态部件是否跟手、outline 手感、F3+T 热换包)静态无法替代实机,建议按 model / outline / vanilla 三档各测一次,并覆盖「整包设成 model 触发预算」与「装带子空间坐标的重定向类模组」两种边界。
Gugle2308
left a comment
There was a problem hiding this comment.
结论:PR #281 已合并(squash c0e0d4c8,与 dev/26.2 tip 一致,合并树与 head 729c3ada 完全相同);本 PR 的补丁与 #280(26.1)逐行相同,我在 26.2 官方未混淆 client.jar 上复核了关键语义,未发现本 PR 引入的阻塞缺陷。这是同批六条线的最后一个,横向对比已完整:只有 1.21.1 带距离哨兵。
一、合并态与范围
| 判据 | 结果 |
|---|---|
| state / merged / merged_at | closed / true / 2026-11-14T11:13:30Z |
| merge_commit_sha | c0e0d4c87ae5f089ee0401926dac92f5342e946c = dev/26.2 tip |
| 合并树 vs head | 空 diff ⇒ 进主干的就是 729c3ada |
| 规模 | 7 文件 +194/−11,与 API 计数一致,diff 覆盖完整 |
| head CI | build / build ✅ |
| 既有评论 | 仅 Roseau API 报告(`cube |
| 与 #280 的关系 | 两 PR 的 +/− 行逐行 diff 为空;核心 5 个 Java 文件在两条线 head 上逐字节相同,CubeHighlightEvents 仅差 26.2 的提取期 API |
二、已验证的关键语义(26.2 官方未混淆 client.jar,直接按内部名反汇编)
- 包优先级方向正确。
MultiPackResourceManager.<init>先List.copyOf(packs)存入packs,再按列表顺序 push 进各 namespace 的FallbackResourceManager;getResourceStack从size()-1倒序取、末尾Lists.reverse,listPacks()返回同一个packs⇒ 索引升序 = 低→高,CubeSelectionRules.java:43的升序排序 + 顺序merge与 README 一致。 - 目录参数合法。
checkTrailingDirectoryPath只在路径endsWith("/")时抛 ⇒ 传"anvillib"正确;六条线行为一致,可写进文档(路径不带尾斜杠)。 - 规则取本轮包集。
ReloadableResourceManager.createReload顺序为 close 旧 → new →putfield→ 派发;listPacks()与listResourceStacks()委托同一个resources字段。 - 逐帧链路成立。
Minecraft.renderFrame(boolean)内pick(偏移 403)早于GameRenderer.render(偏移 520)⇒beginFrame在描边提取之前清帧缓存,仅描边模式不再冻结动态部件。 Identifier.createUntrusted仍校验(assertValidNamespace/assertValidPath)⇒CubeSelectionRules.java:66的 namespace 键校验有效。- 其余结论与同批 #276–#280 相同:规则与几何成对发布、
install先写rules再重算、三种模式四消费点收敛、exclude优先、整份文件校验后替换、公开面零增长。
三、六线对比终版:距离哨兵只在 1.21.1
| 版本线 | PR | distanceToHit 上界 |
|---|---|---|
| 1.21.1 | #276 | ✅ 有 |
| 1.21.4 | #277 | ❌ 无 |
| 1.21.8 | #278 | ❌ 无 |
| 1.21.11 | #279 | ❌ 无 |
| 26.1 | #280 | ❌ 无 |
| 26.2 | #281(本 PR) | ❌ 无(CubePicking.java:38-39 直接赋值) |
1.21.1 的守卫处理「Entity.pick 返回被第三方模组重定向到远处/子空间坐标」(注释点名 Sable):先在 start.distanceToSqr(end) + 1e-7 处兜底返回原版结果。其余五条线会把该坐标直接喂给 BlockGetter.traverseBlocks(start, block.getLocation(), …) 逐格遍历整段射线(每格再跑 (2r+1)³ 次 getBlockState),且每帧一次;context.distance 同时失去「不超过射线长度」的上界。
六条线已全部合并,建议按「一次改五处」处理(26.1 / 26.2 / 1.21.11 的 CubePicking.java 逐字节相同,1.21.4 与 1.21.8 只差 API 名),并在各线对应位置留注释固化触发场景,避免将来被当成冗余清掉。
四、其他建议
⚠️ 预算截断静默(六线通病):MAX_STATES = 32768/MAX_GEOMETRY_BYTES = 16 MiB超限只unsupported++,而CubeSelection.statistics()全仓无调用者;namespace 级配置最容易撞上,表现为「一部分方块仍走原版行为」且无日志。建议在CubeModelEvents.apply里unsupportedStates > 0时打一条一次性 warn。- 💡 README 139–140 行连续两个空行(
### RPC 模块在 141 行)。 - 💡
packs.indexOf(...)的-1(CubeSelectionRules.java:39):命不中会被排成最低优先级(静默降级),可Math.max(0, …)或 debug 日志。 - 💡
load()的异常边界(:35、:37在逐文件 try 之外):listPacks()/listResourceStacks()抛RuntimeException会穿出监听器、让整次模型重载失败,外部资源包引发的问题应退化成EMPTY。 - 💡 公开重载
install(Snapshot)(CubeSelection.java:133)不重置rules,第三方重装快照会沿用上一轮包规则,建议 javadoc 注明。 - 💡
extendsBlock热路径(CubeSelection.java:193-196):mode()每格多一次注册表反查 + 4 次 map 查询,而extended命中即必为 MODEL,可把廉价判断前移。 - 💡
active()/isEnabled()语义收窄(签名不变、javadoc 已更),下游若做渲染/拾取判断需知会。
五、验证边界
未执行 Gradle 构建、runClient 或 JUnit;结论来自静态读码 + 三点 diff 对账(7 文件,与 API 计数逐项一致)+ 26.2 官方未混淆 client.jar 字节码核对(listPacks / getResourceStack / checkTrailingDirectoryPath / createReload / Identifier.createUntrusted / renderFrame 内 pick 与 render 的偏移)+ 与同批 #276–#280 的逐文件比对 + head CI。观感类结论(描边跟手度、outline 手感、F3+T 热换包)静态无法替代实机,建议按 model / outline / vanilla 三档各测一次,并覆盖「整包设成 model 触发预算」与「装带子空间坐标的重定向类模组」两种边界。
feat(cube): 支持资源包配置模型选取与描边
fix(cube): 保持仅描边模式的动态几何逐帧更新