Files
agentmc/scaffold/MOD-BLOCKS.md
JianFeeeee bea69b2556 mod 方块渲染(零加载器):注册链路实测打通
关键洞察:方块**渲染**只需要「注册表里有名字」+「资源包有模型」,
与方块行为无关 ⇒ 不必等 Fabric 加载器。

实测(已跑通):
  frozen 置 false + unregisteredIntrusiveHolders 恢复非 null
  注册占位方块 → BLOCK 1003→1004
  BlockState 手动 add 进 BLOCK_STATE_REGISTRY → 24135→24136
  state id = 24135,parseForBlock 可解析(渲染路径通)

★ 两个必须都处理的字段:只解冻 frozen 会抛
  "This registry can't create intrusive holders"
★ BlockState 不会自动注册(state id 会是 0),需手动 IdMapper.add

边界已诚实标注(scaffold/MOD-BLOCKS.md):
  外观 ✅ / state id ✅ / 变体属性 ⚠️ / 行为 ❌(需加载器)
  渲染类型(半透明)⚠️ 未验证 / 方块实体 ⚠️ 未验证
  Iris 光影必须加载器,此路不覆盖
2026-09-25 19:33:42 +08:00

82 lines
3.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/<modid>/blockstates/<block>.json → 变体 → 模型
assets/<modid>/models/block/*.json
assets/<modid>/models/item/*.json
assets/<modid>/textures/block/*.png
```
## 这条路的边界(诚实标注)
| 项 | 状态 |
|---|---|
| 方块**外观**(模型/贴图) | ✅ 资源包机制可覆盖 |
| state id 分配 | ✅ 已实测 |
| 方块**属性**(朝向/开关等 blockstate 变体) | ⚠️ 占位方块只有 defaultBlockState。要支持变体需按 blockstates json 的 variants 逐个注册 state(工作量与 mod 方块数成正比) |
| 方块**行为**(红石/交互/掉落) | ❌ 不适用(渲染不需要,但要 gameplay 就必须加载器) |
| 方块实体(箱子/机器) | ⚠️ 未验证 |
| 渲染**类型**(solid/cutout/translucent) | ⚠️ 未验证 —— 占位方块都是 solid,可能半透明方块显示异常 |
## 与加载器方案的关系
零加载器方案适合**渲染**(我们的用途);一旦需要 gameplay
(红石/交互/mod 逻辑),才必须上 Fabric/Forge。
Iris 光影**必须**加载器(它是 Fabric mod),这条路覆盖不了。