网络封包

基本信息

属性 值
通道名 lain|nm|cos(CosmeticArmorReworked.network,构造参数)
编解码器 lain.mods.cos.network.NetworkPacketCodec extends FMLIndexedMessageToMessageCodec<NetworkPacket>
封包基类 lain.mods.cos.network.NetworkPacket(4 个抽象方法)
处理器 lain.mods.cos.network.NetworkPacketHandler(@Sharable)
封包总数 4(判别字 1~4;判别字 0 未使用)
权限检查 无——4 个封包均不校验 OP / 权限等级,任何客户端都能发

NetworkPacket 要求实现 handlePacketClient()、handlePacketServer(EntityPlayerMP)、readFromBuffer(ByteBuf)、writeToBuffer(ByteBuf)。NetworkPacketHandler.channelRead0() 按 NetworkRegistry.CHANNEL_SOURCE 是否为服务端分流到 handlePacketServer(取自 NET_HANDLER 的 playerEntity) 或 handlePacketClient()——同一份代码两端都要能反序列化。

NetworkManager 提供 sendTo / sendToAll / sendToAllAround / sendToDimension / sendToServer 五个发送方法,本 mod 实际只用到 sendTo(1 次) 与 sendToAll(3 处) 和 sendToServer(4 处)。

封包对比

封包类 判别字 方向 载荷 何时发送 服务端处理 客户端处理
PacketSyncCosArmor 1 服务端 → 客户端 UUID(2×long)+ slot(byte)+ isSkinArmor(boolean)+ ItemStack ① PlayerLoggedInEvent:先把自己的 4 个槽 sendToAll,再把其他在线玩家的 4 个槽逐个 sendTo 给新玩家;② PlayerTickEvent(START):若 inv.isDirty() 则把 4 个槽 sendToAll,随后 markClean() 空实现 getCosArmorInventoryClient(uuid).setInventorySlotContents(slot, item) + setSkinArmor(slot, isSkinArmor) + markDirty()
PacketSetSkinArmor 2 客户端 → 服务端 slot(byte)+ isSkinArmor(boolean) GuiCosArmorInventory.actionPerformed(),玩家点击皮肤模式按钮 ID 80~83 getCosArmorInventory(uuid).setSkinArmor(slot, isSkinArmor) + markDirty(),随后由 tick 广播出去 空实现
PacketOpenCosArmorInventory 3 客户端 → 服务端 无(readFromBuffer / writeToBuffer 都是空方法体) ① KeyHandler.handleEvent(KeyInputEvent):热键被按下;② GuiEvents.guiPostAction():在原版背包界面点击按钮 76 player.openGui(CosmeticArmorReworked.instance, 1, worldObj, (int)posX, (int)posY, (int)posZ) → 打开 GUI ID 1 空实现
PacketOpenNormalInventory 4 客户端 → 服务端 无(同上,空方法体) GuiEvents.guiPostAction():在成品界面点击按钮 76 player.openContainer.onContainerClosed(player); 然后 player.openContainer = player.inventoryContainer; 空实现

关键设计点

  • 单向权威:PacketSyncCosArmor 与 PacketSetSkinArmor 的 handlePacketServer / handlePacketClient 有一侧是空方法体。物品栏的增删走原版 Forge 容器同步,本 mod 只额外同步每槽的 isSkinArmor 标志——因为 IInventory 接口没有 isDirty/markClean,无法塞进原版 Container 的同步链路。
  • PacketSyncCosArmor 每次只发一个槽,不做整包打包。因此一次改动会产生 4 个封包(4 个槽各一个)。
  • 同步时机是「下一 tick」:markDirty() 只置位,真正的广播发生在 PlayerTickEvent 的 START 阶段。所以服务端改动最多有 1 tick 延迟,且每个玩家每 tick 只广播一次(广播后立即 markClean())。
  • PacketSyncCosArmor 是全服广播(sendToAll),不做距离剔除——任何玩家的时装改动都会推给所有在线玩家,包含跨维度的玩家。
  • 两个开屏封包无载荷,纯粹是「请求服务端改变 openContainer」。PacketOpenNormalInventory 是手动把 openContainer 改回 inventoryContainer(而不是调用 closeScreen()),因为跨端容器状态无法直接同步,必须由服务端权威改写。
  • 无任何权限等级:PacketOpenCosArmorInventory 允许任何玩家打开自己的成品界面——这是安全的,因为 ContainerCosArmor 只绑定 player.inventory 与 player 自己的 UUID。但如果不做此检查,任何玩家理论上都能触发 openGui,属于设计上刻意省略。

相关条目