网络协议
BetterBuildersWands 有一个简单的 FML SimpleNetworkWrapper 通道
"bbwands",共 3 个包(其中 2 个仅在安装 AE2 时注册)。
通道注册
BetterBuildersWandsMod.preInit():
networkWrapper = NetworkRegistry.INSTANCE.newSimpleChannel("bbwands");
| 属性 | 值 |
|---|---|
| 通道名 | bbwands |
| 类型 | SimpleNetworkWrapper(FML simpleimpl) |
| 句柄工具 | GenericHandler<REQ>(network/GenericHandler.java) |
GenericHandler 是 1.7 / 1.8 代码统一的线程安全包装:它实现
IMessageHandler<REQ, IMessage>,在 onMessage 里转调抽象方法
processMessage 并返回 null(不回送 reply)。
包注册表
| discriminator | 包类 | 处理端 | 方向 | 条件 |
|---|---|---|---|---|
0 |
PacketWandActivate |
Side.SERVER |
客户端 → 服务端 | 无条件 |
1 |
SynchronizeAEItemQuantity.SyncServer |
Side.SERVER |
客户端 → 服务端 | 需安装 appliedenergistics2 |
2 |
SynchronizeAEItemQuantity.SyncClient |
Side.CLIENT |
服务端 → 客户端 | 需安装 appliedenergistics2 |
Side.SERVER/Side.CLIENT指处理端所在的一侧。SyncServer名字里的 “Server” 指的是"发给服务端的请求", 它由客户端发出、服务端处理。
注册代码:
networkWrapper.registerMessage(PacketWandActivate.Handler.class, PacketWandActivate.class, 0, Side.SERVER);
if (Loader.isModLoaded("appliedenergistics2")) {
networkWrapper.registerMessage(SynchronizeAEItemQuantity.SyncServer.class,
SynchronizeAEItemQuantity.SyncServer.class, 1, Side.SERVER);
networkWrapper.registerMessage(SynchronizeAEItemQuantity.SyncClient.class,
SynchronizeAEItemQuantity.SyncClient.class, 2, Side.CLIENT);
}
包 0:PacketWandActivate(模式切换)
| 字段 | 类型 | 说明 |
|---|---|---|
keyActive |
boolean | 方向模式键的边沿状态 |
keyFluidActive |
boolean | 流体模式键的边沿状态 |
序列化格式(2 字节):
buffer.writeBoolean(keyActive);
buffer.writeBoolean(keyFluidActive);
服务端处理(PacketWandActivate.Handler.processMessage):
- 取发送者的
EntityPlayerMP keyActive为真且手持物品instanceof IWandItem→ 调nextMode(),回复聊天消息keyFluidActive为真且手持物品instanceof IWandItem→ 调nextFluidMode(),回复聊天消息
手持非魔杖时静默忽略,不报错。客户端只上报"键变了", 服务端才决定是否有实际效果,因此伪造包也无法在没有魔杖时改动任何状态。
该文件源自 VeinMiner 项目(LGPL),源码头部保留了原始版权声明。
包 1 / 2:SynchronizeAEItemQuantity(AE2 数量同步)
仅在安装 AE2 时注册,用于让客户端知道 ME 网络里有多少目标方块。
SyncServer(客户端 → 服务端,discriminator 1)
| 字段 | 类型 |
|---|---|
stack |
ItemStack |
stack = ByteBufUtils.readItemStack(buf);
客户端没有 AE2 网格访问权,所以只能把想查的 ItemStack 发给服务端。
SyncClient(服务端 → 客户端,discriminator 2)
| 字段 | 类型 |
|---|---|
size |
int(匹配到的数量) |
BasicPlayerShim.AEItemSize = buf.readInt(); // 存入静态字段
服务端 SyncServer.onMessage 的逻辑:
| 情况 | 回复 |
|---|---|
obj == null 或 stack == null |
SyncClient(0) |
obj.rangeCheck() 为假 |
SyncClient(0) |
| 网格中匹配到物品 | SyncClient(min(found.getStackSize(), Integer.MAX_VALUE)) |
| 未匹配到 | SyncClient(0) |
客户端缓存策略
BasicPlayerShim.getAEItem 的客户端分支维护一个 LastItem 静态记录:
| 情况 | 行为 |
|---|---|
| 查询的物品变了 | 立即发 SyncServer,返回上一次缓存的 AEItemSize |
物品没变,且 worldTime % 10 == 0 |
每 10 个世界刻发一次,返回缓存值 |
| 玩家身上没有任何无线终端 | AEItemSize = 0 |
由于回复是异步的,客户端第一次查询某物品时返回的是上一次缓存值 (可能为 0),所以"刚切换目标方块时上限算不准、放置不出方块"是已知现象, 等 10 刻(0.5 秒)后重试即可。
AEItemSize是public static int,属于全局共享状态 —— 多玩家服务器上 各自客户端进程独立,不互相干扰。
放置本身不走网络包
魔杖的方块放置完全在服务端完成:ItemBasicWand.onItemUse 的
onItemUse 在服务端被调用时(!world.isRemote)才执行
WandWorker.placeBlocks(),客户端只是发一次原版的 C0S 使用物品包。
方块与物品的同步由 Minecraft 自带的区块更新机制负责。
相关条目
- 模式切换热键 - 包 0 的触发端
- 流体模式切换热键 - 包 0 的另一个字段
- AE2 Integration - 数量同步的完整业务逻辑
- Baubles 集成 - 无线终端的搜索范围
- BFS 方块扩展算法 - 服务端侧的搜索与放置