Repository navigation
Add data-driven cube selection for 1.21.11 为1.21.11添加数据驱动的模型选取与描边 - #279
Conversation
- 支持按命名空间和方块配置精确模型选取、仅模型描边及原版模式 - 按资源包优先级逐项合并规则,并在模型重载完成时同步应用规则与几何 - 保留动态部件和程序排除接口,分离描边与精确选取入口并限制邻格补扫范围 - 补充资源配置格式、覆盖顺序及 KubeJS 整合包接入说明
🌿 Roseau API Breaking Change Report
|
- 在跳过精确选取前刷新客户端帧缓存,避免仅描边模式冻结动态部件 - 保留嵌套射线调用的缓存保护及超出精确选取距离时的原版行为
Gugle2308
left a comment
There was a problem hiding this comment.
结论:PR #279 已在本次审查执行前被合并(squash e7e8207e,与 dev/1.21.11 tip 一致,合并树与 head 61e5ecc4 完全相同);这是同一改动的第 5 条线(1.21.1 / 1.21.4 / 1.21.11 / 26.1 / 26.2),我在 1.21.11 官方混淆 jar + client_mappings 上逐条复核了关键语义,未发现本 PR 引入的阻塞缺陷;同时把五条线的差异矩阵补全了——只有 1.21.1 线有距离哨兵,其余四条都没有。
一、合并态与五线关系
| 判据 | 结果 |
|---|---|
| state / merged / merged_at | closed / true / 2026-11-14T11:13:13Z |
| merge_commit_sha | e7e8207e30259ba6f29e04f44d4ebfdc8a6b3b5c,与 ls-remote dev/1.21.11 相同 |
| 合并树 vs head | git diff pr279h pr279b 为空 ⇒ 进主干的就是 61e5ecc4 |
| 规模 | 7 文件 +194/−11,与 API 计数一致;payload 里的 diff 覆盖完整 |
| head CI | build / build ✅(total_count: 1) |
| 既有评论 | 仅 Roseau API 报告 1 条,无内容级审查 |
| 与其他线的关系 | CubeSelection.java / CubeSelectionRules.java / CubePicking.java / CubeModelEvents.java 在 1.21.11 与 26.1 上逐字节相同;CubeHighlightEvents 仅差行宽取值 API,ModelSelections 仅差包移动 |
二、在 1.21.11 上核对过的语义(混淆 jar + 官方 mappings)
-
包优先级方向正确。
MultiPackResourceManager(obfbas)的listPacks()返回构造器里List.copyOf存下的同一个列表(bas.d),构造器按列表顺序把包push进FallbackResourceManager(obfbaq);baq的getResourceStack从size()-1倒序取、末尾Lists.reverse⇒ 索引升序 = 低→高,CubeSelectionRules.java:43的升序排序 + 顺序merge与 README「从低到高逐项合并」一致。 -
目录参数校验存在且方向相同。
bas.a(Ljava/lang/String;)V(checkTrailingDirectoryPath)的字节码是「endsWith("/")为真才抛IllegalArgumentException」⇒ 传"anvillib"(无尾斜杠)合法。1.21.11 已带该校验,与 1.21.4 一致,比 1.21.1 更严格——下游若按旧习惯传"anvillib/"会直接抛,值得在库文档里写一句。 -
规则读的是本轮包集。
ReloadableResourceManager(obfbaw)的createReload顺序为「close 旧resources→new bas→putfield baw.c→ 派发」⇒ModifyBakingResult期间listPacks()与listResourceStacks()看到同一批新包,packs.indexOf(sourcePackId())与栈顺序同源。 -
逐帧链路成立(本线走
LocalPlayer钩子)。 调用链为GameRenderer.pick(float)(hob.a(F)V,调用方只有Minecraft.tick()与GameRenderer.renderLevel)→LocalPlayer.raycastHitResult(float, Entity)(全 jar 唯一调用方就是前者)→LocalPlayer.pick(Entity,D,D,F)→ 被重定向的Entity.pick⇒CubePicking.pick每帧一次,第 2 个提交的beginFrame一定早于描边提取。 -
Identifier.fromNamespaceAndPath仍校验。amo.a(String,String)→amo.d→e/f两级校验与 1.21.4/26.x 同构,所以CubeSelectionRules.java:66用一次性Identifier校验 namespace 键在本线有效;非法键仍让整份文件不生效,与 README 一致。 -
其余结论与 #280/#281 相同:规则与几何成对发布(
CubeModelEvents.java:22-24/:29)、install先写rules再重算(CubeSelection.java:128-131)、三种模式四个消费点收敛(target仅 MODEL、outlineTargetMODEL+OUTLINE、extendsBlock仅 MODEL、active()=pickingActive)、exclude优先于配置、公开面零增长(与 Roseau「0 breaking」一致)。
三、跟进项
CubePicking.java:38-42(本 PR,与 1.21.4 / 26.1 / 26.2 相同):
context.nearest = block;
context.distance = start.distanceToSqr(block.getLocation());1.21.1 线同名方法 :38-43 多三行:
double distanceToHit = start.distanceToSqr(block.getLocation());
// Sable 等模组可能返回子空间存储坐标,不能从玩家位置跨坐标空间逐格补扫。
if (!(distanceToHit <= start.distanceToSqr(end) + 1.0E-7)) return vanilla;影响不只是变量复用:Entity.pick 的返回值本身也是被重定向过的点,第三方返回远处/子空间坐标时,本线会从 start 到该坐标逐格遍历整段射线,每格再跑 (2r+1)³ 次 getBlockState,且每帧一次;context.distance 同时失去「不超过射线长度」的上界。既然五条线里只有 dev/1.21.1 有这段守卫,建议明确取舍:要么把它补到其余四条线(注释可直接沿用),要么从 1.21.1 回退——当前状态是同一份库在不同 MC 版本上行为不一致,最难排查。
ModelSelections.java 的 MAX_STATES = 32768 / MAX_GEOMETRY_BYTES = 16 MiB 超限时只做 unsupported++,而 CubeSelection.statistics() 全仓无调用者。README 新推的正是 namespace 级配置("namespaces": {"minecraft": "model"} 这类整包开启),撞上上限的表现是「一部分方块仍走原版行为」且没有任何日志。建议在 CubeModelEvents.apply 里当 unsupportedStates > 0 打一条一次性 warn。
四、细节建议
- README 69–70 行连续两个空行(
### Config 模块在 71 行)。 packs.indexOf(...)的-1分支(CubeSelectionRules.java:39):命不中时该源被排成最低优先级(静默降级),建议Math.max(0, …)或 debug 日志。load()的异常边界(CubeSelectionRules.java:35、:37在逐文件 try 之外):listPacks()/listResourceStacks()抛RuntimeException会穿出ModifyBakingResult监听器并让整次模型重载失败,外部资源包引发的问题应退化成EMPTY。- 公开重载
install(Snapshot)(CubeSelection.java:133)不重置rules,第三方重装快照时会沿用上一轮包规则,建议 javadoc 注明。 extendsBlock热路径(CubeSelection.java:193-196):新增的mode()每格做一次注册表反查 + 4 次 map 查询,而extended只在install时按 MODEL 过滤过(命中即必为 MODEL),可把廉价判断前移。active()/isEnabled()语义收窄(签名不变、javadoc 已更),下游若用它做渲染/拾取判断需要知会。
五、验证边界
未执行 Gradle 构建、runClient 或 JUnit;结论来自:静态读码 + 三点 diff 对账(7 文件,与 API 计数逐项一致)+ 1.21.11 官方混淆 client.jar 与 client_mappings 反汇编(obf:bas=MultiPackResourceManager、baq=FallbackResourceManager、baw=ReloadableResourceManager、amo=Identifier、hob=GameRenderer、gfj=Minecraft、hnh=LocalPlayer)+ 与其余四个孪生 PR 的逐文件比对 + head CI。观感类结论(描边与动态部件是否跟手、outline 手感、F3+T 热换包)静态无法替代实机,建议按 model / outline / vanilla 三档各测一次,并覆盖「整包设成 model 触发预算」与「装带子空间坐标的重定向类模组」两种边界。
Gugle2308
left a comment
There was a problem hiding this comment.
结论:PR #279 已合并(squash e7e8207e,与 dev/1.21.11 tip 一致,合并树与 head 61e5ecc4 完全相同),此前评论区只有 Roseau 的 API 报告;本次补做内容级复核,未发现本 PR 引入的阻塞缺陷,唯一实质跟进项是六条版本线里只有 1.21.1 带 CubePicking 的距离哨兵。
一、合并态与范围
| 判据 | 结果 |
|---|---|
| state / merged / merged_at | closed / true / 2026-11-14T11:13:13Z |
| merge_commit_sha | e7e8207e30259ba6f29e04f44d4ebfdc8a6b3b5c = dev/1.21.11 tip |
| 合并树 vs head | 空 diff ⇒ 进主干的就是 61e5ecc4 |
| 规模 | 7 文件 +194/−11,与 API 计数一致,diff 覆盖完整 |
| head CI | build / build ✅ |
| 既有评论 | 仅 Roseau API 报告(`cube |
二、已验证的关键语义(1.21.11 官方混淆 client.jar + client_mappings 反汇编)
- 包优先级方向正确。
MultiPackResourceManager(obfbas)的listPacks()返回构造器List.copyOf存下的同一列表(bas.d),构造器按列表顺序 push 进FallbackResourceManager(obfbaq);baq.getResourceStack从size()-1倒序取、末尾Lists.reverse⇒ 索引升序 = 低→高,CubeSelectionRules.java:43的升序排序 + 顺序merge与 README 一致。 - 目录参数合法。
bas.a(String)V(checkTrailingDirectoryPath)只在路径endsWith("/")时抛 ⇒ 传"anvillib"正确;六条线行为一致,可写进文档(路径不带尾斜杠)。 - 规则取本轮包集。
ReloadableResourceManager(baw)的createReload顺序为 close 旧 →new bas→putfield baw.c→ 派发,因此listPacks()与listResourceStacks()同一批包。 - 逐帧链路成立(本线走
LocalPlayer钩子)。 链为GameRenderer.pick(float)(hob.a(F)V,调用方只有Minecraft.tick()与GameRenderer.renderLevel=hob.b(Lgez;)V)→LocalPlayer.raycastHitResult(全 jar 唯一调用方是前者)→LocalPlayer.pick(Entity,D,D,F)→ 被重定向的Entity.pick⇒CubePicking.pick每帧一次,beginFrame早于描边提取。 Identifier.fromNamespaceAndPath仍校验。amo.a(String,String)→amo.d→e/f两级校验 ⇒CubeSelectionRules.java:66的 namespace 键校验在本线有效。- 其余结论与同批 #276/#277/#278/#280/#281 相同:规则与几何成对发布、
install先写rules再重算、三种模式四消费点收敛、exclude优先、整份文件校验后替换、公开面零增长。
三、唯一实质跟进项:六线对比,距离哨兵只在 1.21.1
| 版本线 | PR | distanceToHit 上界 |
|---|---|---|
| 1.21.1 | #276 | ✅ 有 |
| 1.21.4 | #277 | ❌ 无 |
| 1.21.8 | #278 | ❌ 无 |
| 1.21.11 | #279(本 PR) | ❌ 无(CubePicking.java:38-39 直接赋值) |
| 26.1 | #280 | ❌ 无 |
| 26.2 | #281 | ❌ 无 |
1.21.1 的守卫处理「Entity.pick 返回被第三方模组重定向到远处/子空间坐标」(注释点名 Sable):先在 start.distanceToSqr(end) + 1e-7 处兜底返回原版结果。其余五条线会把该坐标直接喂给 BlockGetter.traverseBlocks(start, block.getLocation(), …) 逐格遍历整段射线(每格再跑 (2r+1)³ 次 getBlockState),且每帧一次;context.distance 同时失去「不超过射线长度」的上界。建议补到其余五条线,或在各线对应位置加注释固化其触发场景。
四、其他建议
⚠️ 预算截断静默(六线通病):MAX_STATES = 32768/MAX_GEOMETRY_BYTES = 16 MiB超限只unsupported++,而CubeSelection.statistics()全仓无调用者;namespace 级配置最容易撞上,表现为「一部分方块仍走原版行为」且无日志。建议在CubeModelEvents.apply里unsupportedStates > 0时打一条一次性 warn。- 💡 README 69–70 行连续两个空行(
### Config 模块在 71 行)。 - 💡
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 计数逐项一致)+ 1.21.11 官方混淆 client.jar 与 client_mappings 反汇编(bas/baq/baw/amo/hob/gfj/hnh)+ 与同批 #276 / #277 / #278 / #280 / #281 的逐文件比对 + head CI。观感类结论(描边跟手度、outline 手感、F3+T 热换包)静态无法替代实机,建议按 model / outline / vanilla 三档各测一次,并覆盖「整包设成 model 触发预算」与「装带子空间坐标的重定向类模组」两种边界。
feat(cube): 支持资源包配置模型选取与描边
fix(cube): 保持仅描边模式的动态几何逐帧更新