数据包同步

基本信息

属性 值
包类 ca.wescook.nutrition.network.SPacketNutrients$Message
通道名 nutrition(NetworkRegistry.INSTANCE.newSimpleChannel("nutrition"))
注册方向 Side.CLIENT(S2C)
消息 ID MESSAGE_S2C_SYNC_NUTRIENTS = 0
触发点 EventPlayerJoinWorld.entityJoinWorldEvent 主动 send(player)

功能

服务端把玩家全部 Nutrient 值打包成 S2C 包发给客户端,客户端用按 name 反查 NutrientList.getByName(identifier) 重建 Map<Nutrient, Float>,保证客户端 GUI 显示与服务端一致。

发送(toBytes)

@Override
public void toBytes(ByteBuf buf) {
    for (Map.Entry<Nutrient, Float> entry : this.values.entrySet()) {
        ByteBufUtils.writeUTF8String(buf, entry.getKey().name);
        buf.writeFloat(entry.getValue());
    }
}

格式:循环写入 <utf8 nutrientName> <float value> 对,直到 Map 遍历完毕。

接收(fromBytes)

@Override
public void fromBytes(ByteBuf buf) {
    this.values = new HashMap<>();
    while (buf.isReadable()) {
        String identifier = ByteBufUtils.readUTF8String(buf);
        Float value = buf.readFloat();
        this.values.put(NutrientList.getByName(identifier), value);
    }
}

反查 Nutrient 对象;若客户端 NutrientList 中找不到该 name(mod 加载顺序或配置不一致),返回 null,最终被 NutritionManager.setAll 忽略。

客户端处理

@SideOnly(Side.CLIENT)
private void handle(final Message message) {
    EntityPlayer player = Minecraft.getMinecraft().thePlayer;
    NutritionManager.instance().setAll(player, message.values);

    // If Nutrition GUI is open, update GUI
    GuiScreen currentScreen = Minecraft.getMinecraft().currentScreen;
    if (currentScreen instanceof NutritionGui) {
        ((NutritionGui) currentScreen).redrawLabels();
    }
}

若当前正打开 NutritionGui,强制 redrawLabels() 立即重绘,无需等下个 110 ticks 的 potionTicking 循环。

主动发送时机

时机 调用
玩家首次加入世界 EventPlayerJoinWorld.entityJoinWorldEvent
死亡后重生 同上(PlayerEvent.Clone 触发 wasDeath=true 后必然进入 EntityJoinWorldEvent)
/nutrition reload 重载配置 DataImporter.updatePlayerCapabilitiesOnServer 重写 Map 后由下一次 EntityJoinWorldEvent 同步(注意 reload 不会主动 push,需玩家重连或触发 EntityJoin)
WitcheryEventHandler 每 tick 修改吸血鬼玩家营养 通过 setAll → 内部会发包(NutritionManager.setAll 默认 sync=true)

跳过同步的调用

PlayerDataHandler.initializeForPlayer 显式调用 setAll(player, values, false) —— 此时客户端尚未 ready,注释明确写:

“Do not sync here since the client is not yet ready to receive it. It will be synced later in EventPlayerJoinWorld.”

相关条目