You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过FabricMC模组在Minecraft客户端发送数据包并在多人模式的Bukkit服务器接收?

Hey there! I’ve worked on cross-compatibility between Fabric mods and Bukkit plugins before, so let’s break down exactly how to send a custom text packet from your Fabric client and catch it on a Bukkit server. This requires custom packet handling on both sides, since we’re bridging two different modding ecosystems.

1. Core Concept

We’ll create a custom client-to-server (C2S) packet in your Fabric mod, define its structure (in this case, a text string), then write a Bukkit plugin that listens for this specific packet using Netty (the underlying network library Minecraft uses). The key is making sure both sides agree on the packet ID and data format to avoid misinterpretation.

2. Fabric Client: Send the Custom Packet

First, we’ll build the packet class and add logic to send it (e.g., when a player presses a key or right-clicks an item).

Step 2.1: Create the Custom Packet Class

This class defines how the packet is written (client-side) and read (server-side):

import net.minecraft.network.Packet;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.listener.ServerPlayPacketListener;

public class CustomTextC2SPacket implements Packet<ServerPlayPacketListener> {
    private final String message;

    // Initialize with the text we want to send
    public CustomTextC2SPacket(String message) {
        this.message = message;
    }

    // Constructor for reading the packet from a byte buffer (used by the server)
    public CustomTextC2SPacket(PacketByteBuf buf) {
        this.message = buf.readString(32767); // Match Minecraft's max string length
    }

    // Write the text to the byte buffer (client sends this)
    @Override
    public void write(PacketByteBuf buf) {
        buf.writeString(message);
    }

    // Server-side handler (we'll handle this in Bukkit, so leave empty for now)
    @Override
    public void apply(ServerPlayPacketListener listener) {}
}

Step 2.2: Add Logic to Send the Packet

Trigger this code when you want to send the packet (e.g., in a key bind or item interaction):

import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;

// Get the client's network handler (make sure the player is connected!)
ClientPlayNetworkHandler networkHandler = MinecraftClient.getInstance().getNetworkHandler();
if (networkHandler != null) {
    // Create and send the packet with your text
    CustomTextC2SPacket packet = new CustomTextC2SPacket("Hello from my Fabric mod!");
    networkHandler.sendPacket(packet);
}

Important: Assign a unique packet ID for your custom packet. For this example, we’ll use 0x42—double-check that this ID isn’t used by vanilla Minecraft for C2S packets (look up your Minecraft version’s packet list to avoid conflicts).

3. Bukkit Server: Receive the Custom Packet

Bukkit uses Netty under the hood, so we’ll create a channel interceptor to listen for incoming packets and parse our custom one.

Step 3.1: Plugin Main Class

Register the Netty interceptor when your plugin enables:

import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import net.minecraft.server.network.ServerConnection;
import io.netty.channel.Channel;

public class PacketReceiverPlugin extends JavaPlugin {
    @Override
    public void onEnable() {
        registerPacketInterceptor();
        getLogger().info("Custom packet receiver is active!");
    }

    private void registerPacketInterceptor() {
        ServerConnection serverConnection = ((CraftServer) getServer()).getServerConnection();
        for (var networkManager : serverConnection.getNetworkManagers()) {
            Channel channel = networkManager.channel;
            // Add our handler after the decoder to process parsed packets
            if (channel.pipeline().get("custom-packet-handler") == null) {
                channel.pipeline().addAfter("decoder", "custom-packet-handler", new CustomPacketHandler(this));
            }
        }
    }
}

Step 3.2: Netty Handler to Catch the Packet

This class filters incoming packets and processes our custom one:

import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import net.minecraft.network.PacketDataSerializer;
import org.bukkit.Bukkit;

public class CustomPacketHandler extends ChannelDuplexHandler {
    private final PacketReceiverPlugin plugin;
    private static final int CUSTOM_PACKET_ID = 0x42; // Match the ID you used in Fabric

    public CustomPacketHandler(PacketReceiverPlugin plugin) {
        this.plugin = plugin;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof PacketDataSerializer serializer) {
            serializer.markReaderIndex(); // Save the current read position
            try {
                int packetId = serializer.readVarInt(); // Read the packet ID (vanilla format)
                if (packetId == CUSTOM_PACKET_ID) {
                    // Read the text from the packet
                    String message = serializer.readString(32767);
                    plugin.getLogger().info("Received custom packet: " + message);

                    // Run Bukkit API actions on the main thread (critical for thread safety!)
                    Bukkit.getScheduler().runTask(plugin, () -> {
                        Bukkit.broadcastMessage("[Custom Packet] " + message);
                    });

                    // Don't pass the packet to vanilla (it doesn't recognize our ID)
                    return;
                }
            } catch (Exception e) {
                serializer.resetReaderIndex(); // Reset if reading fails
            }
        }
        // Pass non-custom packets to vanilla processing
        super.channelRead(ctx, msg);
    }
}
4. Critical Notes to Avoid Issues
  • Packet ID Uniqueness: Always verify your chosen packet ID isn’t used by vanilla Minecraft for your target version. Using a conflicting ID will break vanilla functionality.
  • Data Format Consistency: The order you write data in the Fabric packet must exactly match the order you read it in the Bukkit handler (e.g., write a string first, read a string first).
  • Thread Safety: Never call Bukkit API methods directly from the Netty IO thread—use Bukkit.getScheduler().runTask() to switch to the main server thread.
  • Connection Check: In the Fabric client, always check if networkHandler is non-null before sending the packet (this ensures the player is connected to a server).
Testing the Setup
  1. Package your Fabric mod and install it in your client’s mods folder.
  2. Build your Bukkit plugin and drop it in the server’s plugins folder.
  3. Launch the client, connect to your server, and trigger the packet send event.
  4. Check the server console—you should see your custom message, and it will be broadcast to all players!

内容的提问来源于stack exchange,提问作者Sudo-su-Bash

火山引擎 最新活动