diff --git a/scaffold/MOD-BLOCKS.md b/scaffold/MOD-BLOCKS.md new file mode 100644 index 0000000..89bc177 --- /dev/null +++ b/scaffold/MOD-BLOCKS.md @@ -0,0 +1,81 @@ +# mod 方块渲染(零加载器方案) + +## 问题 + +附录 D 已证明:**1.20.1 不在网络上下发方块注册表**, +mod 方块的 state id 只在 mod 被加载时才注册进客户端。 + +⇒ 走 mod 加载器(Fabric/Forge)是一个方案,但成本高(加载器 + 重映射整个游戏)。 + +## 关键洞察 + +**方块渲染只需要「注册表里有这个名字」+「资源包里有它的模型/贴图」**, +与方块的行为无关(行为是游戏逻辑,渲染不看)。 + +所以零加载器也能让 mod 方块正确显示: + +1. 注册**同名占位方块**进客户端注册表(拿到 state id) +2. mod jar 当**资源包**加载(拿到 blockstates/models/textures) + +## 实测(2026-09-25,已跑通) + +`Registry.register` 与 `Block` 构造是 public,但注册表在 +`BuiltInRegistries.bootStrap()` 里被 `freeze()`。绕过需要两步: + +```java +// ★ 1. 解冻:frozen 是 MappedRegistry 的 private boolean +// ★ 2. 恢复 unregisteredIntrusiveHolders —— freeze() 会把它置 null, +// 而注册路径需要它(否则抛 "This registry can't create intrusive holders") +// —— 这两个字段必须**都**处理,只解冻 frozen 不够(实测) +Field frozen = ...getDeclaredField("frozen"); frozen.setBoolean(BLOCK, false); +Field holders = ...getDeclaredField("unregisteredIntrusiveHolders"); +if (holders.get(BLOCK) == null) holders.set(BLOCK, new IdentityHashMap<>()); + +// 3. 注册占位方块(外观由资源包提供,行为对渲染无关) +Block b = new Block(BlockBehaviour.Properties.copy(Blocks.STONE)); +Registry.register(BuiltInRegistries.BLOCK, + new ResourceLocation("createbigcannons", "cast_iron_autocannon_breech"), b); + +// ★ 4. BlockState **不会**自动进 BLOCK_STATE_REGISTRY(实测 state id 会是 0) +// 需手动 add —— IdMapper.add(T) 是 public +Block.BLOCK_STATE_REGISTRY.add(b.defaultBlockState()); +``` + +实测结果: + +``` +注册前 BLOCK = 1003, BLOCK_STATE = 24135 +注册后 BLOCK = 1004, BLOCK_STATE = 24136 +state id = 24135 ← 正确分配 +反查 = createbigcannons:cast_iron_autocannon_breech +parseForBlock("createbigcannons:...") 成功 → id 24135 ← 渲染路径可解析 +``` + +## 外观怎么来 + +mod jar 里通常自带 `pack.mcmeta`(实测 createbigcannons 有), +可直接作为资源包加入 `resourcePackRepository`: + +``` +assets//blockstates/.json → 变体 → 模型 +assets//models/block/*.json +assets//models/item/*.json +assets//textures/block/*.png +``` + +## 这条路的边界(诚实标注) + +| 项 | 状态 | +|---|---| +| 方块**外观**(模型/贴图) | ✅ 资源包机制可覆盖 | +| state id 分配 | ✅ 已实测 | +| 方块**属性**(朝向/开关等 blockstate 变体) | ⚠️ 占位方块只有 defaultBlockState。要支持变体需按 blockstates json 的 variants 逐个注册 state(工作量与 mod 方块数成正比) | +| 方块**行为**(红石/交互/掉落) | ❌ 不适用(渲染不需要,但要 gameplay 就必须加载器) | +| 方块实体(箱子/机器) | ⚠️ 未验证 | +| 渲染**类型**(solid/cutout/translucent) | ⚠️ 未验证 —— 占位方块都是 solid,可能半透明方块显示异常 | + +## 与加载器方案的关系 + +零加载器方案适合**渲染**(我们的用途);一旦需要 gameplay +(红石/交互/mod 逻辑),才必须上 Fabric/Forge。 +Iris 光影**必须**加载器(它是 Fabric mod),这条路覆盖不了。