网络封包(ModularNetwork 与 7 个封包)

基本信息

属性 值
通道 NetworkRegistry.INSTANCE.newSimpleChannel(ModularUI.ID)(network/NetworkHandler.java:23)
通道名 modularui2(取自 ModularUI.ID,ModularUI.java:27)
底层 SimpleNetworkWrapper(FML 1.7.10 SimpleNetworkImpl)
封包数 7
初始化 NetworkHandler.init(),由 CommonProxy.preInit() 调 NetworkHandler.init()(CommonProxy.java:52)

功能

7 个封包与注册方向

NetworkHandler.init()(network/NetworkHandler.java:26-36)逐个注册, 按注册顺序分配递增的数字 id(packetId++,:24 与 :39/:40):

顺序 封包类 文件 注册方式 方向 占用 id 数
1 SClipboard network/packets/SClipboard.java registerS2C(:27) 服务端 → 客户端 1
2 SyncConfig network/packets/SyncConfig.java registerC2S(:29) 客户端 → 服务端 1
3 OpenGuiPacket network/packets/OpenGuiPacket.java registerBoth(:31) 双向 2
4 ReopenGuiPacket network/packets/ReopenGuiPacket.java registerBoth(:32) 双向 2
5 CloseGuiPacket network/packets/CloseGuiPacket.java registerBoth(:33) 双向 2
6 CloseAllGuiPacket network/packets/CloseAllGuiPacket.java registerBoth(:34) 双向 2
7 PacketSyncHandler network/packets/PacketSyncHandler.java registerBoth(:35) 双向 2

7 个封包类实际占用 12 个 id 槽(1 + 1 + 2×5)。 原因见 NetworkHandler.java:46-49:registerBoth 内部先调 registerS2C 再调 registerC2S, 两次调用各自执行一次 packetId++(:43 / :39)。 这也是 id 顺序敏感的原因——插入或重排封包会改变后续所有 id。其他 mod 不应硬编码 id。

三个注册方向方法的实现

方法 行 Side 参数
registerC2S(Class) :38-40 Side.SERVER
registerS2C(Class) :42-44 Side.CLIENT
registerBoth(Class) :46-49 先 S2C 后 C2S,各占一个 id

发送辅助方法

NetworkHandler 提供 3 个静态发送方法:

方法 行 目标
sendToServer(IPacket) :51-53 服务器
sendToWorld(IPacket, World) :55-57 指定维度(sendToDimension)
sendToPlayer(IPacket, EntityPlayerMP) :59-60 指定玩家

封包接口 IPacket

network/IPacket.java:15 定义 IPacket extends IMessage(FML 的 IMessage):

成员 行 说明
void write(PacketBuffer) :22 序列化
void read(PacketBuffer) :30 反序列化;javadoc 明确要求「除读取外不要做任何别的事」(:26-27)
executeClient(NetHandlerPlayClient) :38-42 @SideOnly(Side.CLIENT),默认返回 null
executeServer(NetHandlerPlayServer) :50-53 默认返回 null
fromBytes(ByteBuf) :55-63 @Deprecated——IMessage 的旧签名,内部转调 read
toBytes(ByteBuf) :65-73 @Deprecated——同上,转调 write

fromBytes / toBytes 会把 ByteBuf 包装成 PacketBuffer(:59 / :69), 所以实现类只需按 PacketBuffer 写。

剪贴板同步

SClipboard 是唯一的 S2C 封包,负责把服务端剪贴板内容推到客户端; utils/serialization/ 包(见 utils/ 目录)提供配套的序列化。

ASM 补丁:ItemStack 变长编码

core/ModularUITransformer.java:15-19 注册了 IClassTransformer, 对 net.minecraft.network.PacketBuffer (core/visitor/PacketByteBufferVisitor.java:14)做字节码改写:

改写 说明 行
writeByte → writeVarIntToBuffer / func_150787_b 物品堆叠数改用 varint :60-64
readByte → readVarIntFromBuffer / func_150792_a 同上,读侧 :65-69
丢弃 POP 指令 writeByte 返回 ByteBuf 而 writeVarIntToBuffer 返回 void :49-54

方法名在开发环境与生产环境不同(PacketByteBufferVisitor.java:17-22): 开发环境用 MCP 名(writeItemStackToBuffer / readItemStackFromBuffer), 生产环境用混淆名(a / c),由 ModularUICore.isDevEnv() 决定 (core/ModularUICore.java:54,injectData 里读 runtimeDeobfuscationEnabled)。

源码注释说明了原因(PacketByteBufferVisitor.java:15-16): Mixin 给出的是混淆名而非 SRG 名,即使把 SortingIndex 设得更高也一样。

交互

触发 封包
打开界面 OpenGuiPacket
重开界面(保留 Container) ReopenGuiPacket
关闭当前界面 CloseGuiPacket
关闭全部 MUI2 界面 CloseAllGuiPacket
同步值变化 PacketSyncHandler
服务端剪贴板变化 SClipboard(S→C)
客户端配置请求 SyncConfig(C→S)

相关条目