Repository navigation
Add data-driven cube selection for 1.21.1 为1.21.1添加数据驱动的模型选取与描边 - #276
Conversation
- 支持按命名空间和方块配置精确模型选取、仅模型描边及原版模式 - 按资源包优先级逐项合并规则,并在模型重载完成时同步应用规则与几何 - 保留动态部件和程序排除接口,分离描边与精确选取入口并限制邻格补扫范围 - 补充资源配置格式、覆盖顺序及 KubeJS 整合包接入说明
- 在跳过精确选取前刷新客户端帧缓存,避免仅描边模式冻结动态部件 - 保留嵌套射线调用的缓存保护及超出精确选取距离时的原版行为
🌿 Roseau API Breaking Change Report
|
* feat(cube): 接入资源包驱动的模型选取与描边 - 将方块命名空间启用和选取排除名单迁移至统一资源配置,保留现有方块的默认模式 - 使整机与动态模型描边遵守统一模式及运行时排除,保留预览几何和方块实体部件排除配置 - 补充资源包覆盖顺序、旧配置迁移和 KubeJS 接入说明 - 配套 Anvil-Dev/AnvilLib#276 的资源规则,依赖发布前使用本地构建联调 * style(cube): 统一模型描边条件的缩进 * build(deps): 更新砧库模型选取依赖 - 将砧库及关联模块更新至 2.0.0+snapshot.682,使用已发布的资源包模型选取与描边功能 - 同步最低依赖版本与配置文档,完成铁砧工艺的数据驱动模型接入
Gugle2308
left a comment
There was a problem hiding this comment.
结论:PR #276 已在合并后被审查(squash 95c30b13,与 dev/1.21.1 tip 一致,合并树与 head 076385c9 完全相同);本次补做内容级复核,未发现本 PR 引入的阻塞缺陷;横向对比五条版本线后可以看到,本线是唯一带有距离哨兵的线,建议保留,并把这段守卫补到其余四条线。
一、合并态与范围
| 判据 | 结果 |
|---|---|
| state / merged / merged_at | closed / true / 2026-11-14T11:12:42Z |
| merge_commit_sha | 95c30b13e01f1e5d03507bf3f7fcfdef32ddf261,与 ls-remote dev/1.21.1 相同 |
| 合并树与 head 的对比 | git diff pr276h2 pr276m 的输出为空,说明进主干的正是 076385c9 |
| 规模 | 7 个文件 +193/−11,与 API 的 changed_files=7 / additions=193 / deletions=11 一致,diff 覆盖完整 |
| head CI | build / build ✅(total_count: 1;仓库只有这一项检查) |
| 既有评论 | 仅 Roseau API 报告 1 条(`cube |
二、已在 1.21.1 上核对过的语义
-
资源包优先级方向正确。
MultiPackResourceManager(obfatx)的listPacks()返回构造器里List.copyOf存下的同一个列表,构造器按列表顺序把包 push 进各 namespace 的FallbackResourceManager(obfatv);atv.getResourceStack从size()-1倒序取、末尾Lists.reverse,因此按索引升序排列就等于按优先级从低到高排列;CubeSelectionRules.java:43按优先级升序排序后再顺序merge,这与 README「从低到高逐项合并」一致。 -
目录参数校验存在且方向正确。 1.21.1 也有
checkTrailingDirectoryPath(atx.a(String)V),字节码含义是只有endsWith("/")为真时才抛IllegalArgumentException;因此传入"anvillib"(无尾斜杠)是合法的,传入"anvillib/"才会抛异常。这一点在 1.21.1、1.21.4、1.21.11、26.1、26.2 五条线上完全一致,可以放心写进库文档。 -
路径过滤能命中任意 namespace。
FileToIdConverter.fileToId用path.substring(prefix.length() + 1, len - ext)反推,反证listResourceStacks返回的 id 带anvillib/前缀,所以CubeSelectionRules.java:37的id.getPath().equals("anvillib/cube_selection.json")能命中assets/<任意 ns>/anvillib/cube_selection.json。 -
规则读的是本轮包集。
ReloadableResourceManager(obfaub)的createReload顺序为:close 旧resources,然后new atx,再执行putfield,最后派发;因此ModifyBakingResult期间listPacks()与listResourceStacks()看到的是同一批新包。 -
逐帧链路成立。
GameRenderer.pick(float)(ges.b(F)V)的调用方只有Minecraft.tick()(fgo.t)与GameRenderer.renderLevel(ges.a(Lfgf;)V),因此每帧一次;第 2 个提交的beginFrame前移一定早于描边提取,仅描边模式不再冻结动态部件。 -
其余结论: 规则与几何成对发布(
CubeModelEvents.java:22-24、:29)、install先写rules再重算(CubeSelection.java:128-131)、三种模式在四个消费点收敛(target仅 MODEL、outlineTargetMODEL+OUTLINE、extendsBlock仅 MODEL、active()=pickingActive)、exclude优先于配置、公开面零增长(与 Roseau「0 breaking」一致)、README 新增 12 条陈述逐条对得上代码。
三、五线横向对比(本线独有:距离哨兵)
本批 #276 / #277 / #279 / #280 / #281 是同一改动的五条线,我逐条比对过 head 实现:
| 线 | PR | distanceToHit 上界守卫 |
说明 |
|---|---|---|---|
| 1.21.1 | #276(本 PR) | ✅ 有(CubePicking.java:38-43) |
唯一带守卫的线 |
| 1.21.4 | #277 | ❌ 无 | CubePicking.java:38-39 直接赋值 |
| 1.21.11 | #279 | ❌ 无 | 同 26.x 实现,逐字节相同 |
| 26.1 | #280 | ❌ 无 | 同上 |
| 26.2 | #281 | ❌ 无 | 同上 |
本线这段守卫做的事情是:当 Entity.pick 的返回被第三方模组重定向到远处或子空间坐标时(注释点名的 Sable 场景),先在 start.distanceToSqr(end) + 1e-7 处兜底返回原版结果。其余四条线缺这层兜底,因此会把这个坐标直接交给 BlockGetter.traverseBlocks(start, block.getLocation(), …) 逐格遍历整段射线,每格还要跑 (2r+1)³ 次 getBlockState,并且每帧一次;同时 context.distance 也失去了「不超过射线长度」的上界。
建议二选一,避免将来在本线被当成冗余代码清掉:把这三行补到其余四条线(注释可直接沿用),或者在五条线的对应位置都加注释,说明它依赖的模组场景与触发条件。
四、其他跟进
ModelSelections.java 的 MAX_STATES = 32768 / MAX_GEOMETRY_BYTES = 16 MiB 超限时只做 unsupported++,而 CubeSelection.statistics() 全仓无调用者。README 新推的正是 namespace 级配置("namespaces": {"minecraft": "model"} 这类整包开启),撞上上限的表现是「一部分方块仍走原版行为」且没有任何日志。建议在 CubeModelEvents.apply 里当 unsupportedStates > 0 打一条一次性 warn。
细节建议:
- README 68–69 行连续两个空行(
### Config 模块在 70 行)。 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.1 官方混淆 client.jar 与 client_mappings 反汇编(obf:atx=MultiPackResourceManager、atv=FallbackResourceManager、aub=ReloadableResourceManager、ges=GameRenderer、fgo=Minecraft)+ 与同批 #277 / #279 / #280 / #281 的逐文件比对 + head CI。观感类结论(描边与动态部件是否跟手、outline 手感、F3+T 热换包)静态无法替代实机,建议按 model / outline / vanilla 三档各测一次,并覆盖「整包设成 model 触发预算」与「装带子空间坐标的重定向类模组」两种边界。
Gugle2308
left a comment
There was a problem hiding this comment.
结论:PR #276 已合并(squash 95c30b13,与 dev/1.21.1 tip 一致,合并树与 head 076385c9 完全相同),评论区此前只有 Roseau 的 API 报告、没有内容级审查;本次补做复核,未发现本 PR 引入的阻塞缺陷,结论只有一条实质跟进项:六条版本线里只有本线带 CubePicking 的距离哨兵。
一、合并态与范围
| 判据 | 结果 |
|---|---|
| state / merged / merged_at | closed / true / 2026-11-14T11:12:42Z |
| merge_commit_sha | 95c30b13e01f1e5d03507bf3f7fcfdef32ddf261 = dev/1.21.1 tip |
| 合并树 vs head | 空 diff ⇒ 进主干的就是 076385c9(含第 2 个提交) |
| 规模 | 7 文件 +193/−11,与 API changed_files=7 / additions=193 / deletions=11 一致,diff 覆盖完整 |
| head CI | build / build ✅ |
| 既有评论 | 仅 Roseau API 报告(`cube |
二、已验证的关键语义(1.21.1 官方混淆 client.jar + client_mappings 反汇编)
- 包优先级方向正确。
MultiPackResourceManager(obfatx)的listPacks()返回构造器List.copyOf存下的同一列表;FallbackResourceManager(obfatv)的getResourceStack从size()-1倒序取、末尾Lists.reverse⇒ 索引升序 = 低→高,CubeSelectionRules.java:43的升序排序 + 顺序merge与 README 一致。 - 目录参数合法。 1.21.1 也有
checkTrailingDirectoryPath,只在路径endsWith("/")时抛 ⇒ 传"anvillib"正确;该行为在六条线上完全一致(可写进文档:路径不带尾斜杠)。 - 路径过滤跨 namespace 命中。
FileToIdConverter.fileToId用prefix.length() + 1反推,反证listResourceStacks返回的 id 带anvillib/前缀 ⇒:37的equals(PATH)命中assets/<任意 ns>/anvillib/cube_selection.json。 - 规则取本轮包集。
ReloadableResourceManager(aub)的createReload顺序为 close 旧 → new →putfield→ 派发。 - 逐帧链路成立。
GameRenderer.pick(float)(ges.b(F)V)的调用方只有Minecraft.tick()与GameRenderer.renderLevel⇒ 每帧一次,beginFrame前移早于描边提取。 - 发布原子性 / 容错:
CubeModelEvents.java:22-24/:29成对暂存与发布;install先写rules再按新规则重算(CubeSelection.java:128-131);merge全量校验后才返回新 record,异常时保留低优先级规则(:56-77);exclude在mode()首行短路(:111-114)。 - 公开面零增长:
CubeSelectionRules与Mode包私有、两个install/isEnabled/mode重载包私有,与 Roseau「0 breaking」一致;README 新增 12 条陈述逐条对得上代码。
三、唯一实质跟进项:六条线里只有本线有距离哨兵
| 版本线 | PR | distanceToHit 上界 |
|---|---|---|
| 1.21.1 | #276(本 PR) | ✅ 有(CubePicking.java:38-43) |
| 1.21.4 | #277 | ❌ 无 |
| 1.21.8 | #278 | ❌ 无 |
| 1.21.11 | #279 | ❌ 无 |
| 26.1 | #280 | ❌ 无 |
| 26.2 | #281 | ❌ 无 |
本线代码:
double distanceToHit = start.distanceToSqr(block.getLocation());
// Sable 等模组可能返回子空间存储坐标,不能从玩家位置跨坐标空间逐格补扫。
if (!(distanceToHit <= start.distanceToSqr(end) + 1.0E-7)) return vanilla;其余五条线会把这个坐标直接喂给 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。- 💡
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 已更),下游若用它做渲染/拾取判断需知会。 - 💡 README 68–69 行连续两个空行(
### Config 模块在 70 行)。
五、验证边界
未执行 Gradle 构建、runClient 或 JUnit;结论来自静态读码 + 三点 diff 对账(7 文件,与 API 计数逐项一致)+ 1.21.1 官方混淆 client.jar 与 client_mappings 反汇编 + 与同批 #277 / #278 / #279 / #280 / #281 的逐文件比对 + head CI。观感类结论(描边跟手度、outline 手感、F3+T 热换包)静态无法替代实机,建议按 model / outline / vanilla 三档各测一次,并覆盖「整包设成 model 触发预算」与「装带子空间坐标的重定向类模组」两种边界。
feat(cube): 支持资源包配置模型选取与描边
fix(cube): 保持仅描边模式的动态几何逐帧更新