BuildCraft 封包层:5 个频道 + 14 个包 ID
事实基准:
/Users/evlos/a/mirror/BuildCraft(master,minecraftVersion = 1.7.10)逐行读源码。
频道(集合运算:恰好 5 个)
频道名前缀来自 core/DefaultProps.java:15:
public static final String NET_CHANNEL_NAME = "BC";
| 频道名 | 注册模块 | ChannelHandler | PacketHandler | 源码 |
|---|---|---|---|---|
BC-CORE |
BuildCraft|Core |
coreChannelHandler |
core.network.PacketHandlerCore |
BuildCraftCore.java:506 |
BC-ENERGY |
BuildCraft|Energy |
new ChannelHandler() |
new PacketHandler() |
BuildCraftEnergy.java:452 |
BC-FACTORY |
BuildCraft|Factory |
new ChannelHandler() |
new PacketHandler() |
BuildCraftFactory.java:157 |
BC-SILICON |
BuildCraft|Silicon |
new ChannelHandler() |
new PacketHandlerSilicon |
BuildCraftSilicon.java:139-141 |
BC-TRANSPORT |
BuildCraft|Transport |
new ChannelHandler() |
new PacketHandlerTransport |
BuildCraftTransport.java:529-530 |
⚠ 一仓 7 个 modid,但只有 5 个网络频道——
BuildCraft|Builders 与 BuildCraft|Robotics 没有自己的频道:
Builders 的数据走 BC-CORE,Robotics 的数据也走 BC-CORE。
按 modid 数推断频道数即为 7,是错的。
包 ID(core/network/PacketIds.java,集合运算:恰好 14 个)
| ID | 常量 | 语义 | 行 |
|---|---|---|---|
| 0 | TILE_UPDATE |
TileEntity 整体更新 | :11 |
| 1 | COMMAND |
ICommandReceiver 命令 |
:12 |
| 2 | PIPE_TRAVELER |
管道中的物品移动 | :13 |
| 3 | PIPE_LIQUID |
管道流体同步 | :14 |
| 4 | PIPE_POWER |
管道能量同步 | :15 |
| 5 | PIPE_ITEMSTACK_REQUEST |
管道物品请求 | :16 |
| 6 | PIPE_ITEMSTACK |
管道物品同步 | :17 |
| 7 | ENTITY_UPDATE |
实体更新 | :18 |
| 31 | DIAMOND_PIPE_SELECT |
钻石管道选槽 | :20 |
| 32 | EMERALD_PIPE_SELECT |
翡翠管道选槽 | :21 |
| 40 | TABLET_MESSAGE |
平板消息 | :23 |
| 70 | ADVANCED_WORKBENCH_SETSLOT |
高级工作台设槽 | :25 |
| 80 | GUI_RETURN |
GUI 返回 | :27 |
| 81 | GUI_WIDGET |
GUI 控件 | :28 |
| 100 | STATE_UPDATE |
状态更新 | :30 |
⚠ ID 不连续——分成 0–7、31–32、40、70、80–81、100 几段。 「最大 ID = 100,所以有 101 个包」是典型的枚举虚高; 实际只有 14 个常量。 ⚠ 同样,不能因为 ID 是 100 就断言「包 ID 有 101 种」。
承载数据的包类(core/lib/network/,9 个)
| 类 | 用途 |
|---|---|
Packet(:27 处 PacketHandler extends SimpleChannelInboundHandler<Packet>) |
封包基类,携带 int getID() |
PacketCoordinates |
坐标基类 |
PacketTileUpdate |
Tile 更新(ID 0) |
PacketEntityUpdate |
实体更新(ID 7) |
PacketTileState |
Tile 状态(ID 100) |
PacketNBT |
NBT 负载 |
PacketSlotChange |
单槽变化 |
PacketGuiReturn |
GUI 返回(ID 80) |
PacketGuiWidget |
GUI 控件(ID 81) |
基础分派(core/lib/network/PacketHandler.java:27)
PacketHandler extends SimpleChannelInboundHandler<Packet>(:27),
channelRead0 里取 int packetID = packet.getID()(:69)后 switch(:71):
| case | 行 | 处理 |
|---|---|---|
PacketIds.TILE_UPDATE |
:72 |
Tile 更新 |
PacketIds.ENTITY_UPDATE |
:77 |
实体更新 |
PacketIds.COMMAND |
:82 |
命令 |
PacketIds.STATE_UPDATE |
:87 |
状态更新 |
PacketIds.GUI_RETURN |
:100 |
GUI 返回 |
PacketIds.GUI_WIDGET |
:105 |
GUI 控件 |
⚠ 基础 PacketHandler 只处理 6 个 ID;
其余 8 个(管道 5 个、管道选择 2 个、平板 1 个)由各模块自己的
PacketHandlerCore / PacketHandlerSilicon / PacketHandlerTransport 扩展处理。
BC-ENERGY 与 BC-FACTORY 直接用基础 PacketHandler。
方向:按包类声明,不按包 ID
⚠ 重要纠正:方向不是按 PacketIds 的 ID 判的。
core/lib/network/ChannelHandler.java 用的是**「包类 → 侧掩码」**的注册表:
discriminators(:121):byte判别值 →Class<? extends Packet>sides(:129使用):每个判别值对应一个侧掩码,取值CLIENT_ONLY/SERVER_ONLY/ 两者
判定逻辑 ChannelHandler.java:126-129:
int expectedSide = ctx.channel().attr(NetworkRegistry.CHANNEL_SOURCE).get() == Side.CLIENT
? CLIENT_ONLY : SERVER_ONLY;
if ((expectedSide & sides.get(discriminator)) != expectedSide) {
logForgedPackets(player, clazz.getSimpleName(), payload.slice()); // :131 记为伪造包
}
| 事实 | 含义 |
|---|---|
判别值是 byte(:120 payload.readByte()) |
传输线上传的是判别值,不是 PacketIds 的 int |
未注册的判别值抛 NullPointerException |
:122-124「Undefined message for discriminator」 |
侧不匹配的包不抛异常,只记 logForgedPackets |
:129-131,属防御性日志 |
PacketHandler.channelRead0 里的 int packetID = packet.getID()(:69)
是读出载荷后才拿到的第二层分派,与「方向」是两件事。
⚠ 因此本条目不逐包断言方向——
PacketIds 只是 ID 常量表,方向由各 Packet 子类注册时的 sides 掩码决定,
逐包方向需回 ChannelHandler 的注册处核对,本条目不编造。
载荷读取路径
| 步骤 | 行 | 行为 |
|---|---|---|
| 读判别值 | ChannelHandler.java:120 |
payload.readByte() |
| 查类 | :121 |
discriminators.get(discriminator) |
| 侧校验 | :126-131 |
不匹配则 logForgedPackets |
| 反序列化 | :134-135 |
newMsg.readData(payload.slice()) |
ChannelHandler extends MessageToMessageCodec(:34 导入),
出站方向在 encode 中对称处理(:108 附近用 INBOUNDPACKETTRACKER 追踪原始包)。
与 TileEntity 的关系
TileBuildCraft层的sendNetworkUpdate()→PacketTileUpdate(ID 0)。- 实现
ICommandReceiver的 TE(TileRefinery、 TileZonePlan、TileRequester、 TileArchitect、TileProgrammingTable) 走COMMAND(ID 1)。 - 实现
ISyncedTile的 TE(TileGenericPipe) 走ByteBuf协议 +STATE_UPDATE(ID 100)。