网络封包
基本信息
| 属性 | 值 |
|---|---|
| 通道名 | 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,属于设计上刻意省略。