Skip to content

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

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

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

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
font ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
space-select ✅ Compatible 0
util ✅ 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 bf11fa3 into Anvil-Dev:dev/1.21.4 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 #277 已在本次审查执行前被合并(squash bf11fa3c,与 dev/1.21.4 tip 一致,合并树与 head 94dc032c 完全相同);这是同一改动的第 4 个版本线(1.21.1 / 1.21.4 / 26.1 / 26.2),我用 1.21.4 官方混淆 jar + client_mappings 逐条复核了关键语义,未发现本 PR 引入的阻塞缺陷;唯一实质差异是缺少 1.21.1 线已有的距离哨兵,属同 family 内的移植缺口。

一、合并态与跨线关系

判据 结果
state / merged / merged_at closed / true / 2026-11-14T11:12:52Z
merge_commit_sha bf11fa3cb80329a9c0ec60f2ad62d6271112ec37,与 ls-remote dev/1.21.4 相同
合并树 vs head git diff pr277h pr277b 为空 ⇒ 进主干的就是 94dc032c
规模 7 文件 +193/−11,与 API 计数一致(比 26.x 的 +194 少 1 行,来自 ModelSelections 的 1.21.4 API 差异 BlockModelShaper.stateToModelLocation,非逻辑差异)
head CI build / build ✅(total_count: 1)
既有评论 仅 Roseau API 报告 1 条,无内容级审查
跨线一致性 CubeSelectionRules.java 在 1.21.4 / 26.1 / 26.2 三条线上逐字节相同;与 1.21.1(#276)的差异仅 ResourceLocation↔Identifier 换名;CubePicking 的差异见下文 ⚠️1

二、在 1.21.4 上核对过的语义(混淆 jar + 官方 mappings)

  1. 包优先级方向正确。 MultiPackResourceManager(obf aui)的 listPacks() 返回构造器里 List.copyOf 存下的同一个列表;FallbackResourceManager(obf aug)的 getResourceStack 从 size()-1 倒序取、末尾 Lists.reverse ⇒ 索引升序 = 优先级低→高,CubeSelectionRules.java:43 的升序排序 + 顺序 merge 与 README「从低到高逐项合并」一致。

  2. 1.21.4 也有 checkTrailingDirectoryPath,方向与 26.x 相同。 aui.a(Ljava/lang/String;)V 的字节码是「endsWith("/") 为真才抛 IllegalArgumentException」⇒ 传 "anvillib"(无尾斜杠)合法;若写成 "anvillib/" 会直接抛。这一点在本线是新增信息(1.21.4 已带该校验,与 1.21.1 的宽松行为不同),值得写进库文档,避免下游按旧习惯传尾斜杠。

  3. 规则读的是本轮包集。 ReloadableResourceManager(obf aum)的 createReload 顺序为「close 旧 resources → new aui → putfield aum.c → 派发监听器」⇒ ModifyBakingResult 期间 listPacks() 与 listResourceStacks() 看到同一批新包,packs.indexOf(sourcePackId()) 与栈顺序同源。

  4. 逐帧链路成立。 GameRenderer.pick(float)(glq.a(F)V)在全 jar 只有两个调用方:Minecraft.tick()(flk.t)与 GameRenderer.renderLevel(glq.a(Lfla;)V)⇒ 每帧一次,第 2 个提交的 beginFrame 前移一定早于描边绘制,仅描边模式不再冻结动态部件。

  5. ResourceLocation.fromNamespaceAndPath 仍校验。 其构造沿用 d → e/f(namespace / path 校验)与 1.21.1 同构,所以 CubeSelectionRules.java:66 用一次性 ResourceLocation 校验 namespace 键在 1.21.4 有效;非法键仍会让整份文件不生效,与 README 一致。

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

三、跟进项

⚠️ 1. 1.21.4 缺 1.21.1 已有的距离哨兵(同 family 内的移植缺口)

CubePicking.java:38-42(本 PR):

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 同时失去「不超过射线长度」的上界。两条 1.21.x 线行为不一致这件事本身就是证据——26.1/26.2 同样没有这段守卫,建议在 26.1、26.2、1.21.4 三处一次补齐(1.21.1 的注释可直接沿用)。

⚠️ 2. 预算截断完全静默(四条线通病)

ModelSelections.java 的 MAX_STATES = 32768 / MAX_GEOMETRY_BYTES = 16 MiB 超限时只做 unsupported++,而 CubeSelection.statistics() 全仓无调用者。README 新推的正是 namespace 级配置("namespaces": {"minecraft": "model"} 这类整包开启),撞上上限的表现是「一部分方块仍走原版行为」且没有任何日志。建议在 CubeModelEvents.apply 里当 unsupportedStates > 0 打一条一次性 warn。

四、细节建议

  • README 62–63 行连续两个空行(### Config 模块 在 64 行)。
  • 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.4 官方混淆 client.jar 与 client_mappings 反汇编(obf:aui=MultiPackResourceManager、aug=FallbackResourceManager、aum=ReloadableResourceManager、akv=ResourceLocation、glq=GameRenderer、flk=Minecraft)+ 与 1.21.1/26.1/26.2 三个孪生 PR 的逐行比对 + head CI。26.x 侧此前已用未混淆 jar 核对,本线为混淆 jar 反汇编,结论未依赖源码猜测。观感类结论(描边与动态部件是否跟手、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 #277 已合并(squash bf11fa3c,与 dev/1.21.4 tip 一致,合并树与 head 94dc032c 完全相同),评论区此前只有 Roseau 的 API 报告;本次补做内容级复核,未发现本 PR 引入的阻塞缺陷,唯一实质跟进项是六条版本线里只有 1.21.1 带 CubePicking 的距离哨兵。

一、合并态与范围

判据 结果
state / merged / merged_at closed / true / 2026-11-14T11:12:52Z
merge_commit_sha bf11fa3cb80329a9c0ec60f2ad62d6271112ec37 = dev/1.21.4 tip
合并树 vs head 空 diff ⇒ 进主干的就是 94dc032c
规模 7 文件 +193/−11,与 API 计数一致(比 26.x 的 +194 少 1 行,来自 ModelSelections 的 1.21.4 API 差异,非逻辑差异)
head CI build / build ✅
既有评论 仅 Roseau API 报告(`cube

二、已验证的关键语义(1.21.4 官方混淆 client.jar + client_mappings 反汇编)

  1. 包优先级方向正确。 MultiPackResourceManager(obf aui)的 listPacks() 返回构造器 List.copyOf 存下的同一列表,构造器按列表顺序 push 进 FallbackResourceManager(obf aug);aug.getResourceStack 从 size()-1 倒序取、末尾 Lists.reverse ⇒ 索引升序 = 低→高,CubeSelectionRules.java:43 的升序排序 + 顺序 merge 与 README 一致。
  2. 目录参数合法。 1.21.4 也有 checkTrailingDirectoryPath(aui.a(String)V),只在路径 endsWith("/") 时抛 ⇒ 传 "anvillib" 正确;该行为在六条线上完全一致(可写进文档:路径不带尾斜杠)。
  3. 路径过滤跨 namespace 命中。 FileToIdConverter.fileToId 用 prefix.length() + 1 反推,反证 listResourceStacks 返回的 id 带 anvillib/ 前缀 ⇒ :37 的 equals(PATH) 命中任意 namespace 下的同名文件。
  4. 规则取本轮包集。 ReloadableResourceManager(aum)的 createReload 顺序为 close 旧 → new aui → putfield aum.c → 派发。
  5. 逐帧链路成立。 GameRenderer.pick(float)(glq.a(F)V)在全 jar 只有两个调用方:Minecraft.tick()(flk.t)与 GameRenderer.renderLevel(glq.a(Lfta;)V)⇒ 每帧一次,beginFrame 前移早于描边提取。
  6. ResourceLocation.fromNamespaceAndPath 仍校验。 akv.a(String,String) → akv.d → e/f 两级校验 ⇒ CubeSelectionRules.java:66 的 namespace 键校验在 1.21.4 有效。
  7. 其余结论与同批 #276/#278/#279/#280/#281 相同:规则与几何成对发布、install 先写 rules 再重算、三种模式四消费点收敛、exclude 优先、整份文件校验后替换、公开面零增长。

三、唯一实质跟进项:六线对比,距离哨兵只在 1.21.1

版本线 PR distanceToHit 上界
1.21.1 #276 ✅ 有
1.21.4 #277(本 PR) ❌ 无(CubePicking.java:38-39 直接赋值)
1.21.8 #278 ❌ 无
1.21.11 #279 ❌ 无
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 62–63 行连续两个空行(### Config 模块 在 64 行)。
  • 💡 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.4 官方混淆 client.jar 与 client_mappings 反汇编(aui/aug/aum/akv/glq/flk)+ 与同批 #276 / #278 / #279 / #280 / #281 的逐文件比对 + 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