05-events · git:20260711.586c86f · 2026-07-11 · sha256 6074d2690e549a68

05-events git:20260711.586c86fA

Immutable. This exact content is served forever at /api/v1/blob/6074d2690e549a68.

# 05 — 事件系统

> 适用版本:Fabric 1.14.4

---

## 约束

### 核心原则

- Fabric 1.14.4 的事件系统主要通过 **Fabric API 的 `fabric-event-api`** 提供
- 每个事件类型有自己的回调接口(如 `PlayerTickEvents.END`)
- 回调在 `onInitialize()` 中注册
- Fabric 事件系统是**静态单例**模式

### 与 Forge 事件系统的区别

| Forge | Fabric |
|-------|--------|
| `@SubscribeEvent` 注解 | `EventCallback` 注册或 `@EventHandler` 注解 |
| `MinecraftForge.EVENT_BUS` | 各事件类型的静态字段 |
| `EventPriority` | 事件回调通常按注册顺序执行 |
| `CanceledEvent` | 事件不自动传播取消 |

---

## Decision Flow

### Decision: 选择事件类型

```
IF 处理玩家每 tick 逻辑
  → PlayerTickEvents.END / START

IF 处理实体每 tick 逻辑
  → EntityTickEvents

IF 处理方块放置/破坏
  → BlockEvents.BLOCK_BREAK / BLOCK_PLACE

IF 处理物品使用
  → ItemEvents.USE_ITEM_ON_BLOCK / USE_ITEM

IF 处理实体死亡/伤害
  → LivingEntityEvents

IF 处理服务端启动/关闭
  → ServerLifecycleEvents.SERVER_STARTED / SERVER_STOPPING

IF 处理数据包加载
  → DataPackRegistry
```

---

## 常用 Fabric 事件

### PlayerTickEvents

```java
@Environment(EnvType.CLIENT)
public class ExampleModClient implements ClientModInitializer {
    @Override
    public void onInitializeClient() {
        PlayerTickEvents.END.register(player -> {
            // 每 tick 结束时执行(客户端)
        });
    }
}

// 服务端
public class ExampleMod implements ModInitializer {
    @Override
    public void onInitialize() {
        ServerTickEvents.END_SERVER_TICK.register(server -> {
            // 每 server tick 执行
        });
    }
}
```

### EntityEvents

```java
// 实体伤害
EntityEvents.ENTITY_HURT.register((entity, source, amount, flag) -> {
    if (entity instanceof PlayerEntity && amount > 1.0f) {
        // 玩家受伤逻辑
    }
    return amount;  // 返回修改后的伤害值
});

// 实体死亡
EntityEvents.ENTITY_DEATH.register((entity, source) -> {
    if (entity instanceof PlayerEntity) {
        // 玩家死亡逻辑
    }
});
```

### BlockEvents

```java
BlockEvents.BEFORE_BREAK.register((player, world, pos, state, blockEntity) -> {
    if (world.getBlockState(pos).getBlock() == Blocks.DIAMOND_ORE) {
        // 防止挖掘钻石矿
        return ActionResult.FAIL;  // 阻止破坏
    }
    return ActionResult.PASS;  // 允许破坏
});

BlockEvents.BEFORE_PLACE.register((world, pos, state, player, hand, itemStack, hitResult) -> {
    // 方块放置前逻辑
    return ActionResult.PASS;
});
```

### ItemEvents

```java
ItemEvents.USE_ITEM_ON_BLOCK.register((player, world, hand, hitResult) -> {
    ItemStack stack = player.getStackInHand(hand);
    if (stack.getItem() == Items.DIAMOND && !world.isRemote) {
        // 使用钻石右键方块
        return ActionResult.SUCCESS;
    }
    return ActionResult.PASS;
});
```

### ServerLifecycleEvents

```java
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
    System.out.println("Server started: " + server.getWorld(World.OVERWORLD).getWorldInfo().getWorldName());
});

ServerLifecycleEvents.SERVER_STOPPING.register(server -> {
    System.out.println("Server stopping...");
});
```

## 常见错误

- ❌ `EventCallback` 注册在非 static 字段上 — 回调不会被注册
- ❌ 在 `onInitialize()` 外注册事件 — 不会生效
- ❌ 在客户端处理服务端逻辑 — 使用 `world.isRemote` 区分
- ❌忘记处理 `ActionResult` — 返回值决定事件是否被"消费"

## 扩展点

| 配合 Skill | 协作说明 |
|------------|---------|
| `mc-registry` | 事件处理中引用已注册的对象 |
| `mc-item` | ItemEvents 用于物品交互逻辑 |
| `mc-entity` | EntityEvents 用于实体行为修改 |
| `mc-networking` | 事件中发送网络包 |