09-anti-patterns · diff
git:20260819.ca99162 to git:20260827.527ef8b
1 added, 1 removed. Audit A to A.
# 09 — 反模式库
> 适用版本:Fabric 1.14.4
---
## 约束
### 核心原则
- 本文件列出 Fabric 开发中最常见的错误模式和正确方案
- 遇到问题时,先查阅本文件
- 错误通常源于 Forge 习惯迁移或对 Fabric 机制不熟悉
---
## 1. 注册系统反模式
### ❌ 在 onInitialize() 外注册
```java
// ❌ 错误
public class ExampleMod implements ModInitializer {
private static final Item MY_ITEM = new Item(new Item.Settings());
// 类加载时注册,但应在 onInitialize() 中
static {
Registry.register(Registry.ITEM, new Identifier(MOD_ID, "my_item"), MY_ITEM);
}
}
// ✅ 正确
public class ExampleMod implements ModInitializer {
private static final Item MY_ITEM =
Registry.register(Registry.ITEM, new Identifier(MOD_ID, "my_item"), new Item(new Item.Settings()));
@Override
public void onInitialize() {
// 静态初始化已在类加载时完成,但最佳实践是在此处初始化
}
}
```
### ❌ BlockItem 与 Block 注册名不一致
```java
// ❌ 错误
Registry.register(Registry.BLOCK, new Identifier(MOD_ID, "my_block"), myBlock);
Registry.register(Registry.ITEM, new Identifier(MOD_ID, "my_block_item"), // 不同名!
new BlockItem(myBlock, new Item.Settings()));
// ✅ 正确:使用完全相同的 Identifier
Registry.register(Registry.BLOCK, new Identifier(MOD_ID, "my_block"), myBlock);
Registry.register(Registry.ITEM, new Identifier(MOD_ID, "my_block"), // 同名!
new BlockItem(myBlock, new Item.Settings()));
```
---
## 2. Mixin 反模式
### ❌ 忘记在 fabric.mixins.json 中声明 mixin
```java
// ❌ 错误:Mixin 类已写好,但忘记在配置文件中声明
// src/main/resources/examplemod.mixins.json
{
"package": "com.example.examplemod.mixin",
"client": ["MyMixin"], // MyMixin 类在 common 包?
"mixins": []
}
// ✅ 正确:确认包名和声明一致
// mixin 在 client 包中
{
"package": "com.example.examplemod.mixin",
"client": ["client.MyMixin"],
"mixins": []
}
```
### ❌ 在 Mixin 中使用 @Shadow 引用不存在的成员
```java
// ❌ 错误:Minecraft 类没有 `session` 字段
@Shadow private static Object session;
// ✅ 正确:使用正确的字段名(参考 Yarn 映射)
@Shadow private static Minecraft instance;
```
### ❌ 在 Mixin 中 new 实例
```java
// ❌ 错误:Mixin 是在运行时字节码注入,禁止直接实例化
@Inject(at = @At("HEAD"), method = "tick")
private void onTick(CallbackInfo ci) {
MyClass obj = new MyClass(); // 禁止!
}
// ✅ 正确:Mixin 仅修改现有逻辑,不创建新对象
```
---
## 3. Yarn Mappings 反模式
### ❌ 混用 Yarn 和 MCP 映射
```java
// ❌ 错误:Forge 项目迁移时使用 MCP 方法名
EntityPlayerMP player; // MCP 风格(Forge)
player.sendChatMessage("hello");
// ✅ 正确:使用 Yarn 映射
ServerPlayerEntity player; // Yarn 风格(Fabric)
player.sendMessage(new LiteralText("hello"));
```
### ❌ 误解 class_XXXXX 命名
```java
// ❌ 错误:class_XXXXX 是 Yarn 未解析的混淆类,不是有效 API
MyClass.class_12345 obj = new MyClass.class_12345(); // 错误!
// ✅ 正确:这是正常现象,不应主动使用未解析的类
// 如果必须使用,通过 Mixin Access Widener 访问
```
---
## 4. fabric.mod.json 反模式
### ❌ schemaVersion 错误
```json
// ❌ 错误:schemaVersion 必须为 1(1.14.4)
{
"schemaVersion": 0, // 错误!Fabric 不支持 schemaVersion 0
...
}
// ✅ 正确
{
"schemaVersion": 1,
...
}
```
### ❌ entrypoints 字段名错误
```json
// ❌ 错误:使用 Forge 的 entrypoint 格式
{
"init": "com.example.ExampleMod" // 错误!
}
// ✅ 正确
{
"entrypoints": {
"main": ["com.example.ExampleMod"]
}
}
```
### ❌ depends 中缺少必需依赖
```json
// ❌ 错误:缺少 fabricloader 依赖
{
"depends": {
"minecraft": ">=1.14.4"
}
}
// ✅ 正确:必须包含 fabricloader
{
"depends": {
- "fabricloader": ">=0.3.2",
+ "fabricloader": ">=0.3.7.111",
"minecraft": ">=1.14.4",
"java": ">=8"
}
}
```
---
## 5. Loom / Gradle 反模式
### ❌ Loom 版本使用错误
```groovy
// ❌ 错误:Loom 0.4-SNAPSHOT 是 1.14.4 的正确版本
plugins {
id 'fabric-loom' version '1.4-SNAPSHOT' // 错误!这是 1.20.1+ 的版本
}
// ✅ 正确(1.14.4)
plugins {
id 'fabric-loom' version '0.4-SNAPSHOT'
}
```
### ❌ 忘记运行 ./gradlew clean loom
```bash
# ❌ 错误:Mappings 变更后直接 build
./gradlew build # 可能不生效
# ✅ 正确:Mappings 变更后先 clean 再 loom
./gradlew clean
./gradlew loom
```
### ❌ 混用 modImplementation 和 modApi
```groovy
// ❌ 错误:依赖传递性混淆
dependencies {
modImplementation("net.fabricmc.fabric-api:fabric-api:0.28.5+1.14") // 不传递依赖
}
// ✅ 正确:API 需要传递,实现不需要
dependencies {
modApi("net.fabricmc.fabric-api:fabric-api:0.28.5+1.14") // 传递依赖
modImplementation("com.example:third-party-mod:1.0.0") // 不传递
}
```
---
## 6. 客户端/服务端反模式
### ❌ 在 onInitialize() 中引用 MinecraftClient
```java
// ❌ 错误:MinecraftClient 仅存在于客户端
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
Minecraft.getInstance(); // 服务端崩溃!
}
}
// ✅ 正确:客户端逻辑放在 ClientModInitializer
public class ExampleModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
Minecraft.getInstance(); // 正确
}
}
```
### ❌ 在 Mixin 中引用客户端类但配置为服务端
```java
// ❌ 错误:Mixin 配置错误
// examplemod.mixins.json
{
"mixins": ["MyMixin"], // 在 mixins(服务端)中
"client": []
}
// MyMixin.java 引用了仅客户端的类
@Mixin(WorldRenderer.class) // WorldRenderer 是仅客户端的类!
public class MyMixin { ... }
```
---
## 7. Registry API 反模式(1.14.4 特有)
### ❌ 使用 Registries 而非 Registry
```java
// ❌ 错误:1.14.4 中不存在 Registries 类
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "my_item"), myItem);
// ✅ 正确:使用 Registry 静态字段
Registry.register(Registry.ITEM, new Identifier(MOD_ID, "my_item"), myItem);
```
### ❌ Block.Properties 使用错误
```java
// ❌ 错误:AbstractBlock.Settings 是 1.17+ 的类;Mojmap Block.Properties 也不是 Yarn 1.14 名
new Block(AbstractBlock.Settings.create(Material.STONE));
new Block(Block.Properties.create(Material.STONE));
// ✅ 正确(Yarn 1.14.4)
new Block(Block.Settings.of(Material.STONE));
```
### ❌ EntityType MobCategory 使用错误
```java
// ❌ 错误:MobCategory 是 1.17+ 的类
EntityType.Builder.of(Entity::new, MobCategory.CREATURE);
// ✅ 正确(1.14.4)
EntityType.Builder.create(Entity::new, EntityCategory.CREATURE);
```
---
## 8. 常见崩溃诊断
| 崩溃信息 | 原因 | 解决方案 |
|---------|------|---------|
| `NullPointerException` at `Registry.register` | 注册在 `onInitialize()` 外执行 | 将注册移入 `onInitialize()` |
| `Mixin did not apply` | mixin 包名不匹配或 `fabric.mixins.json` 配置错误 | 检查 package 和配置 |
| `Could not find net.fabricmc:yarn` | yarn 版本号格式错误 | 格式:`1.14.4+build.18` |
| `FabricLoader not found` | 依赖缺失 | 检查 `fabricloader` 在 depends 中 |
| `No such registry` | Registry 类型错误 | 确认 `Registry.XXX` 正确(不是 `Registries.XXX`) |
| `duplicate id` | 注册 ID 重复 | 检查 mod ID 是否冲突 |