Spigot服务器向Fabric客户端发送PluginMessage数据失败求助
问题分析与解决方案
核心问题:数据读写格式/顺序不匹配
你的代码出现读取空字符串的根本原因是服务器端和客户端的数据序列化/反序列化逻辑不兼容,加上读写顺序错位:
- 服务器端先用
ByteArrayDataOutput.write(0)写入了一个单独字节,随后用writeUTF()写入字符串(该方法会先写入2字节的无符号短整型长度前缀,再写入UTF-8字节)。 - 客户端直接调用
PacketByteBuf.readString(),该方法默认读取VarInt类型的长度前缀,且没有先处理服务器写入的开头字节,导致读取逻辑完全错位,自然读不到有效内容。
分步解决方案
1. 统一数据读写逻辑(二选一)
方案一:适配服务器端的DataOutput格式
如果要保留服务器端现有ByteArrayDataOutput的写法,客户端需要先读取开头的标识字节,再用DataInput的逻辑解析UTF字符串:
private void handlePacket(@NotNull MinecraftClient client, @NotNull ClientPlayNetworkHandler handler, @NotNull PacketByteBuf buffer, @NotNull PacketSender sender) { // 先读取服务器写入的标识字节 byte flag = buffer.readByte(); // 将剩余字节转为DataInput格式读取UTF字符串 byte[] remainingBytes = new byte[buffer.readableBytes()]; buffer.readBytes(remainingBytes); try (DataInput dataInput = new DataInputStream(new ByteArrayInputStream(remainingBytes))) { String message = dataInput.readUTF(); System.out.println(message); } catch (IOException e) { e.printStackTrace(); } }
方案二:统一用Netty PacketByteBuf格式(推荐)
服务器端改用PacketByteBuf构建数据(和客户端逻辑对齐),避免格式差异:
import io.netty.buffer.Unpooled; import net.minecraft.network.PacketByteBuf; public static void sendPmcAdd(Player player, RegionContainer container) { if(!player.hasPermission(ClientModPermission)) return; PacketByteBuf buf = Unpooled.buffer(); buf.writeByte(0); // 保留标识字节 buf.writeUtf("add"); // 用VarInt长度前缀的UTF写入,和客户端readString()匹配 container.serialize(buf, true); // 需要修改serialize方法适配PacketByteBuf,或转换为字节写入 // 如果无法修改serialize方法,可继续用ByteArrayDataOutput后转为字节数组写入buf // ByteArrayDataOutput out = ByteStreams.newDataOutput(); // container.serialize(out, true); // buf.writeBytes(out.toByteArray()); player.sendPluginMessage(MineBreak.getInstance(), "mb:region", buf.array()); }
客户端按顺序读取即可:
private void handlePacket(@NotNull MinecraftClient client, @NotNull ClientPlayNetworkHandler handler, @NotNull PacketByteBuf buffer, @NotNull PacketSender sender) { // 先读标识字节 byte flag = buffer.readByte(); // 直接读取字符串 String message = buffer.readString(); System.out.println(message); // 继续读取区域数据(需和服务器serialize逻辑对应) // container.deserialize(buffer); }
2. 确保通道正确注册
- 服务器端(Spigot):必须在插件
onEnable()中注册 outgoing 通道:@Override public void onEnable() { getServer().getMessenger().registerOutgoingPluginChannel(this, "mb:region"); } - 客户端(Fabric):在Mod初始化时注册 incoming 通道,且确保在客户端主线程处理逻辑:
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.minecraft.util.Identifier; public class YourClientMod implements ClientModInitializer { @Override public void onInitialize() { ClientPlayNetworking.registerGlobalReceiver(new Identifier("mb", "region"), (client, handler, buf, sender) -> { // 切换到客户端主线程执行(避免线程安全问题) client.execute(() -> { byte flag = buf.readByte(); String msg = buf.readString(); System.out.println(msg); }); }); } }
3. 排查ViaVersion兼容性
暂时禁用ViaVersion测试通信是否正常:
- 如果禁用后正常,说明ViaVersion过滤了该通道或存在版本兼容问题,需更新ViaVersion到最新版本,或在配置中允许
mb:region通道的转发。
内容的提问来源于stack exchange,提问作者SatanicAntiChrist
相关产品推荐
相关产品推荐

