06-networking · git:20260711.586c86f · 2026-07-11 · sha256 a34e74fc60f005f0
06-networking git:20260711.586c86fA
Immutable. This exact content is served forever at /api/v1/blob/a34e74fc60f005f0.
# 06 — 网络通信
> 适用版本:Fabric 1.14.4
---
## 约束
### 核心原则
- Fabric 网络使用 `Identifier` 作为包 ID
- 客户端只能向服务端发送数据包,服务端可以向客户端发送
- 网络包必须是确定性的(不含随机或非确定性状态)
- `network` 必须在 `fabric.mod.json` 中声明
- 使用 `Identifier` 作为包 ID 命名空间
---
## Decision Flow
### Decision: 选择网络通信方式
```
IF 服务端向客户端单向推送数据
→ ServerSidePacketRegistry.register()
IF 客户端向服务端单向请求
→ ClientSidePacketRegistry.register()
IF 需要双向通信
→ 分别为服务端和客户端注册不同的包处理器
IF 使用 Fabric API 网络模块
→ fabric-entity-dragon-v0 或 fabric-networking-v0
```
---
## 基础网络注册
```java
// C2S: 客户端向服务端
// S2C: 服务端向客户端
public class MyNetworking {
// 包 ID
public static final Identifier MY_PACKET_ID =
new Identifier(MOD_ID, "my_packet");
// C2S 注册(客户端发送,服务端接收)
public static final ClientSidePacketRegistry CLIENT_PACKET_REGISTRY =
ClientSidePacketRegistry.INSTANCE;
public static void registerServerReceivers() {
CLIENT_PACKET_REGISTRY.register(MY_PACKET_ID, (packetContext, attachedData) -> {
packetContext.getTaskQueue().execute(() -> {
// 在服务端线程执行
ServerPlayerEntity player = packetContext.getPlayer();
int value = attachedData.readInt();
// 处理数据包
});
});
}
// S2C 注册(服务端发送,客户端接收)
public static void registerClientReceivers() {
ServerSidePacketRegistry.INSTANCE.register(MY_PACKET_ID, (packetContext, attachedData) -> {
packetContext.getTaskQueue().execute(() -> {
// 在客户端线程执行
});
});
}
}
```
## 发送数据包
```java
// 服务端向客户端发送
public void sendToClient(ServerPlayerEntity player, int value) {
ServerSidePacketRegistry.INSTANCE.sendToPlayer(player,
MY_PACKET_ID, PacketByteBuf.createUnpooled().writeInt(value));
}
// 客户端向服务端发送
public void sendToServer(int value) {
ClientSidePacketRegistry.INSTANCE.sendToServer(MY_PACKET_ID,
PacketByteBuf.createUnpooled().writeInt(value));
}
```
## fabric.mod.json 配置
```json
{
"entrypoints": {
"main": ["com.example.examplemod.ExampleMod"],
"client": ["com.example.examplemod.ExampleModClient"]
},
"mixins": ["examplemod.mixins.json"]
}
```
## 客户端入口点(网络初始化)
```java
public class ExampleModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
// 注册客户端接收器
MyNetworking.registerClientReceivers();
// 注册快捷键
KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.examplemod.open_gui",
InputUtil.Type.KEYSYM,
InputUtil.Type.KEYSYM.createFromCode(80), // P 键
"category.examplemod"
));
// 快捷键回调
ClientTickEvents.END_CLIENT_TICK.register(client -> {
while (KeyBindings.OPEN_GUI.isPressed()) {
// 打开 GUI 并发送网络包
sendToServer(value);
}
});
}
}
```
## 使用 Fabric Networking API
```groovy
// build.gradle
modImplementation "net.fabric.sdk:fabric-networking-v0:0.1.3+build.7"
```
## 常见错误
- ❌ 在非主线程处理网络数据 — 使用 `packetContext.getTaskQueue().execute()`
- ❌ 忘记在 `fabric.mod.json` 中注册 entrypoint — 网络回调不生效
- ❌ 在服务端处理客户端数据时修改世界状态 — 确认线程安全
- ❌ 包 ID 命名空间不一致 — 服务端和客户端 ID 必须完全相同
- ❌ 在 `onInitialize()` 中注册客户端网络 — 应在 `ClientModInitializer.onInitializeClient()` 中
## 扩展点
| 配合 Skill | 协作说明 |
|------------|---------|
| `mc-gui` | 网络用于 GUI 数据同步 |
| `mc-item` | 物品使用触发网络包 |
| `mc-entity` | 实体状态通过网络同步 |