Repository navigation
Add data-driven cube selection for 1.21.4 为1.21.4添加数据驱动的模型选取与描边 - #277
Conversation
- 支持按命名空间和方块配置精确模型选取、仅模型描边及原版模式 - 按资源包优先级逐项合并规则,并在模型重载完成时同步应用规则与几何 - 保留动态部件和程序排除接口,分离描边与精确选取入口并限制邻格补扫范围 - 补充资源配置格式、覆盖顺序及 KubeJS 整合包接入说明
- 在跳过精确选取前刷新客户端帧缓存,避免仅描边模式冻结动态部件 - 保留嵌套射线调用的缓存保护及超出精确选取距离时的原版行为
🌿 Roseau API Breaking Change Report
|
Gugle2308
left a comment
There was a problem hiding this comment.
结论: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.21.4 上核对过的语义(混淆 jar + 官方 mappings)
-
包优先级方向正确。
MultiPackResourceManager(obfaui)的listPacks()返回构造器里List.copyOf存下的同一个列表;FallbackResourceManager(obfaug)的getResourceStack从size()-1倒序取、末尾Lists.reverse⇒ 索引升序 = 优先级低→高,CubeSelectionRules.java:43的升序排序 + 顺序merge与 README「从低到高逐项合并」一致。 -
1.21.4 也有
checkTrailingDirectoryPath,方向与 26.x 相同。aui.a(Ljava/lang/String;)V的字节码是「endsWith("/")为真才抛IllegalArgumentException」⇒ 传"anvillib"(无尾斜杠)合法;若写成"anvillib/"会直接抛。这一点在本线是新增信息(1.21.4 已带该校验,与 1.21.1 的宽松行为不同),值得写进库文档,避免下游按旧习惯传尾斜杠。 -
规则读的是本轮包集。
ReloadableResourceManager(obfaum)的createReload顺序为「close 旧resources→new aui→putfield aum.c→ 派发监听器」⇒ModifyBakingResult期间listPacks()与listResourceStacks()看到同一批新包,packs.indexOf(sourcePackId())与栈顺序同源。 -
逐帧链路成立。
GameRenderer.pick(float)(glq.a(F)V)在全 jar 只有两个调用方:Minecraft.tick()(flk.t)与GameRenderer.renderLevel(glq.a(Lfla;)V)⇒ 每帧一次,第 2 个提交的beginFrame前移一定早于描边绘制,仅描边模式不再冻结动态部件。 -
ResourceLocation.fromNamespaceAndPath仍校验。 其构造沿用d → e/f(namespace / path 校验)与 1.21.1 同构,所以CubeSelectionRules.java:66用一次性ResourceLocation校验 namespace 键在 1.21.4 有效;非法键仍会让整份文件不生效,与 README 一致。 -
其余结论与 #276 相同:规则与几何成对发布(
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):
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 的注释可直接沿用)。
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
left a comment
There was a problem hiding this comment.
结论: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 反汇编)
- 包优先级方向正确。
MultiPackResourceManager(obfaui)的listPacks()返回构造器List.copyOf存下的同一列表,构造器按列表顺序 push 进FallbackResourceManager(obfaug);aug.getResourceStack从size()-1倒序取、末尾Lists.reverse⇒ 索引升序 = 低→高,CubeSelectionRules.java:43的升序排序 + 顺序merge与 README 一致。 - 目录参数合法。 1.21.4 也有
checkTrailingDirectoryPath(aui.a(String)V),只在路径endsWith("/")时抛 ⇒ 传"anvillib"正确;该行为在六条线上完全一致(可写进文档:路径不带尾斜杠)。 - 路径过滤跨 namespace 命中。
FileToIdConverter.fileToId用prefix.length() + 1反推,反证listResourceStacks返回的 id 带anvillib/前缀 ⇒:37的equals(PATH)命中任意 namespace 下的同名文件。 - 规则取本轮包集。
ReloadableResourceManager(aum)的createReload顺序为 close 旧 → newaui→putfield aum.c→ 派发。 - 逐帧链路成立。
GameRenderer.pick(float)(glq.a(F)V)在全 jar 只有两个调用方:Minecraft.tick()(flk.t)与GameRenderer.renderLevel(glq.a(Lfta;)V)⇒ 每帧一次,beginFrame前移早于描边提取。 ResourceLocation.fromNamespaceAndPath仍校验。akv.a(String,String)→akv.d→e/f两级校验 ⇒CubeSelectionRules.java:66的 namespace 键校验在 1.21.4 有效。- 其余结论与同批 #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 触发预算」与「装带子空间坐标的重定向类模组」两种边界。
feat(cube): 支持资源包配置模型选取与描边
fix(cube): 保持仅描边模式的动态几何逐帧更新