Skip to content

Add data-driven cube selection for 26.2 为26.2添加数据驱动的模型选取与描边 - #281

Merged
PigeonNian merged 2 commits into
Anvil-Dev:dev/26.2from
WhereisFff:codex/cube-data-26.2
Nov 14, 2026
Merged

PigeonNian merged 2 commits into
Anvil-Dev:dev/26.2from
WhereisFff:codex/cube-data-26.2

Conversation

@WhereisFff

@WhereisFff WhereisFff commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

feat(cube): 支持资源包配置模型选取与描边

  • 支持按命名空间和方块配置精确模型选取、仅模型描边及原版模式
  • 按资源包优先级逐项合并规则,并在模型重载完成时同步应用规则与几何
  • 保留动态部件和程序排除接口,分离描边与精确选取入口并限制邻格补扫范围
  • 补充资源配置格式、覆盖顺序及 KubeJS 整合包接入说明

fix(cube): 保持仅描边模式的动态几何逐帧更新

  • 在跳过精确选取前刷新客户端帧缓存,避免仅描边模式冻结动态部件
  • 保留嵌套射线调用的缓存保护及超出精确选取距离时的原版行为

- 支持按命名空间和方块配置精确模型选取、仅模型描边及原版模式
- 按资源包优先级逐项合并规则,并在模型重载完成时同步应用规则与几何
- 保留动态部件和程序排除接口,分离描边与精确选取入口并限制邻格补扫范围
- 补充资源配置格式、覆盖顺序及 KubeJS 整合包接入说明
@Gugle2308

Gugle2308 commented Nov 14, 2026 •

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
cube ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering ✅ Compatible 0
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
math ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
ui ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

Full CSVs: see the Artifacts section of this workflow run.

- 在跳过精确选取前刷新客户端帧缓存,避免仅描边模式冻结动态部件
- 保留嵌套射线调用的缓存保护及超出精确选取距离时的原版行为
@PigeonNian
PigeonNian merged commit c0e0d4c into Anvil-Dev:dev/26.2 Nov 14, 2026
1 check passed

@Gugle2308 Gugle2308 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论: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 字节码)

  1. 包优先级方向仍正确。 MultiPackResourceManager.<init> 先 List.copyOf(packs) 存 packs,再按列表顺序 push 进各 namespace 的 FallbackResourceManager;getResourceStack 从 size()-1 倒序取、末尾 Lists.reverse,listPacks() 直接返回同一个 packs ⇒ 索引升序 = 低→高,CubeSelectionRules.java:43 的升序排序 + 顺序 merge 与 README「从低到高逐项合并」一致。

  2. 路径参数与 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。

  3. 规则读的是本轮包集。 ReloadableResourceManager.createReload 的顺序仍是「close 旧 resources → new MultiPackResourceManager → putfield resources → SimpleReloadInstance.create」,且 listPacks() / listResourceStacks() 都委托同一个 resources 字段 ⇒ ModifyBakingResult 期间两处看到同一批包,packs.indexOf(sourcePackId()) 的索引与栈顺序同源。

  4. 逐帧链路成立。 Minecraft.renderFrame(boolean) 里 pick 在 GameRenderer.render 之前(字节码偏移 403 < 520)⇒ 第 2 个提交的 beginFrame 前移一定发生在描边提取之前,仅描边模式下 FRAME 不再从首帧起冻结。

  5. Identifier.createUntrusted 仍校验。 fromNamespaceAndPath 走 assertValidNamespace / assertValidPath,所以 CubeSelectionRules.java:66 用一次性 Identifier 校验 namespace 键的做法在 26.2 依然有效(非法键会让整份文件不生效,与 README 一致)。

  6. 其余结论沿用 #280: 规则与几何成对发布(CubeModelEvents.java:22-24 / :29)、install 先写 rules 再重算(CubeSelection.java:128-131)、三种模式四个消费点收敛(target 仅 MODEL、outlineTarget MODEL+OUTLINE、extendsBlock 仅 MODEL、active()=pickingActive)、exclude 优先于配置、公开面零增长(record/枚举/两个重载均包私有,与 Roseau「0 breaking」一致)。

三、跟进项

⚠️ 1. 26.x 两条线都缺 1.21.1 已有的距离哨兵(三线唯一语义差异)

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 两处,并在三线各留一句注释,避免下次再漂移。

⚠️ 2. 预算截断完全静默(与 #280 同源)

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 Gugle2308 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论: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,直接按内部名反汇编)

  1. 包优先级方向正确。 MultiPackResourceManager.<init> 先 List.copyOf(packs) 存入 packs,再按列表顺序 push 进各 namespace 的 FallbackResourceManager;getResourceStack 从 size()-1 倒序取、末尾 Lists.reverse,listPacks() 返回同一个 packs ⇒ 索引升序 = 低→高,CubeSelectionRules.java:43 的升序排序 + 顺序 merge 与 README 一致。
  2. 目录参数合法。 checkTrailingDirectoryPath 只在路径 endsWith("/") 时抛 ⇒ 传 "anvillib" 正确;六条线行为一致,可写进文档(路径不带尾斜杠)。
  3. 规则取本轮包集。 ReloadableResourceManager.createReload 顺序为 close 旧 → new → putfield → 派发;listPacks() 与 listResourceStacks() 委托同一个 resources 字段。
  4. 逐帧链路成立。 Minecraft.renderFrame(boolean) 内 pick(偏移 403)早于 GameRenderer.render(偏移 520)⇒ beginFrame 在描边提取之前清帧缓存,仅描边模式不再冻结动态部件。
  5. Identifier.createUntrusted 仍校验(assertValidNamespace / assertValidPath)⇒ CubeSelectionRules.java:66 的 namespace 键校验有效。
  6. 其余结论与同批 #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 触发预算」与「装带子空间坐标的重定向类模组」两种边界。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants