You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bukkit向Forge发送自定义数据包失败,寻求技术解决方案

问题:Bukkit插件发送自定义数据包无法被Forge模组接收(1.19.3)

我用Bukkit插件发送自定义数据包,发送代码如下:

public static boolean sendPacket(Player player, String message) {
    try {
        String full_msg = "Text(" + message + ")";
        byte[] data = full_msg.getBytes(StandardCharsets.UTF_8);

        MinecraftKey channel = new MinecraftKey("chilove", "main");

        PacketContainer packet = new PacketContainer(PacketType.Play.Server.CUSTOM_PAYLOAD);
        packet.getMinecraftKeys().write(0, channel);

        ByteBuf byteBuf = Unpooled.wrappedBuffer(data);
        Object serializer = MinecraftReflection.getPacketDataSerializer(byteBuf);

        packet.getModifier().withType(ByteBuf.class).write(0, serializer);

        ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
        return true;
    } catch (Exception exception) {
        Bukkit.getLogger().info(ConsoleColor.ANSI_RED + "Error on TextPacket: " + ConsoleColor.ANSI_CYAN + " (" + exception.getMessage() + ")" + ConsoleColor.ANSI_RESET);
        exception.printStackTrace();
    }
    return false;
}

数据包能正常发送,但Forge模组端的消息处理器完全没反应。我写的处理器代码如下:

private static final String PROTOCOL_VERSION = "1";
public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
        new ResourceLocation("chilove", "main"),
        () -> PROTOCOL_VERSION,
        PROTOCOL_VERSION::equals,
        PROTOCOL_VERSION::equals
);

public static void register() {
//        INSTANCE.registerMessage(0, TextPacket.class, TextPacket::encode, TextPacket::decode, TextPacket::handle);

    int id = 0;
    INSTANCE.messageBuilder(TextPacket.class, id++, NetworkDirection.PLAY_TO_SERVER)
            .decoder(TextPacket::decode)
            .consumerMainThread(TextPacket::handle)
            .add();
    INSTANCE.registerMessage(id++, TextPacket.class, TextPacket::encode, TextPacket::decode, TextPacket::handle);
    INSTANCE.messageBuilder(TextPacket.class, id++, NetworkDirection.PLAY_TO_CLIENT)
            .decoder(TextPacket::decode)
            .consumerMainThread(TextPacket::handle)
            .add();
}

TextPacket类代码:

public class TextPacket {
    private String text;

    public TextPacket() { }

    public TextPacket(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public static TextPacket decode(FriendlyByteBuf buffer) {
        byte[] bytes = buffer.readByteArray();
        String text = new String(bytes, StandardCharsets.UTF_8);
        ChiloveMod.LOGGER.info("Decoded text: {}", text);
        return new TextPacket(text);
    }

    public void encode(FriendlyByteBuf buffer) {
        byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
        buffer.writeByteArray(bytes);
    }

    public void handle(Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            ChiloveMod.LOGGER.info("Received message: {}", text);
        });
        ctx.get().setPacketHandled(true);
    }
}

发送数据包时控制台会弹出以下两个错误:

- [15:15:32] [Netty Client IO #1/ERROR] [ne.mi.ne.si.IndexedMessageCodec/SIMPLENET]: Received invalid discriminator byte 84 on channel chilove:main
- [15:15:32] [Render thread/WARN] [minecraft/ClientPacketListener]: Unknown custom packet identifier: chilove:main

我试过各种方法,包括用ClientboundCustomPayloadEvent,都解决不了,求帮助。Minecraft版本是1.19.3。


解决方案

问题根源

Forge的SimpleChannel处理自定义Payload时,要求数据包开头必须先写入消息判别符(varint类型的消息ID),你当前Bukkit端发送的数据包直接是Text(xxx)的字节内容,没有添加这个判别符,导致Forge端无法匹配对应的消息类型,触发invalid discriminator byte错误。同时Forge端的注册代码存在冗余,重复注册消息类可能导致判别符混乱。

修复步骤

1. 修改Bukkit端发送代码,添加消息判别符

Bukkit端需要先写入Forge端注册的消息ID(varint格式),再写入实际内容,修改后的代码如下:

public static boolean sendPacket(Player player, String message) {
    try {
        MinecraftKey channel = new MinecraftKey("chilove", "main");
        PacketContainer packet = new PacketContainer(PacketType.Play.Server.CUSTOM_PAYLOAD);
        packet.getMinecraftKeys().write(0, channel);

        // 创建ByteBuf并写入消息ID和文本内容
        ByteBuf byteBuf = Unpooled.buffer();
        // 写入消息判别符:对应Forge端注册的PLAY_TO_CLIENT消息ID(这里用0)
        writeVarInt(byteBuf, 0);
        // 写入文本内容:先写长度(varint),再写字节数组
        byte[] data = message.getBytes(StandardCharsets.UTF_8);
        writeVarInt(byteBuf, data.length);
        byteBuf.writeBytes(data);

        Object serializer = MinecraftReflection.getPacketDataSerializer(byteBuf);
        packet.getModifier().withType(ByteBuf.class).write(0, serializer);

        ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet);
        return true;
    } catch (Exception exception) {
        Bukkit.getLogger().info(ConsoleColor.ANSI_RED + "Error on TextPacket: " + ConsoleColor.ANSI_CYAN + " (" + exception.getMessage() + ")" + ConsoleColor.ANSI_RESET);
        exception.printStackTrace();
    }
    return false;
}

// 实现Minecraft的VarInt编码逻辑
private static void writeVarInt(ByteBuf buf, int value) {
    do {
        byte part = (byte)(value & 0x7F);
        value >>>= 7;
        if (value != 0) {
            part |= 0x80;
        }
        buf.writeByte(part);
    } while (value != 0);
}

2. 清理Forge端的消息注册代码

删除冗余注册逻辑,只保留服务器到客户端的消息注册(Bukkit发往客户端对应PLAY_TO_CLIENT方向):

private static final String PROTOCOL_VERSION = "1";
public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
        new ResourceLocation("chilove", "main"),
        () -> PROTOCOL_VERSION,
        PROTOCOL_VERSION::equals,
        PROTOCOL_VERSION::equals
);

public static void register() {
    // 仅注册服务器到客户端的消息,ID为0
    INSTANCE.messageBuilder(TextPacket.class, 0, NetworkDirection.PLAY_TO_CLIENT)
            .encoder(TextPacket::encode)
            .decoder(TextPacket::decode)
            .consumerMainThread(TextPacket::handle)
            .add();
}

3. 修正TextPacket的编解码逻辑

确保解码时先读取字符串长度,再读取字节数组,和Bukkit端的发送逻辑对应:

public class TextPacket {
    private String text;

    public TextPacket() { }

    public TextPacket(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public static TextPacket decode(FriendlyByteBuf buffer) {
        // 先读字符串长度(varint),再读对应字节数组
        int length = buffer.readVarInt();
        byte[] bytes = new byte[length];
        buffer.readBytes(bytes);
        String text = new String(bytes, StandardCharsets.UTF_8);
        ChiloveMod.LOGGER.info("Decoded text: {}", text);
        return new TextPacket(text);
    }

    public void encode(FriendlyByteBuf buffer) {
        byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
        // 先写字符串长度(varint),再写字节数组
        buffer.writeVarInt(bytes.length);
        buffer.writeBytes(bytes);
    }

    public void handle(Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            ChiloveMod.LOGGER.info("Received message: {}", text);
        });
        ctx.get().setPacketHandled(true);
    }
}

额外注意事项

  • 确保Bukkit端和Forge端的**频道ID(chilove:main)**完全一致,大小写敏感。
  • 消息ID必须匹配:Bukkit端发送的判别符要和Forge端注册的消息ID对应(示例中用的是0)。
  • 必须使用Minecraft标准的VarInt编码,不能直接写入普通int值,否则会导致解码失败。

内容的提问来源于stack exchange,提问作者owoPeef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:54:55