• 游客, 欢迎您来到九域资源社区,如果您是新人,请前往 论坛公告 板块查看新人引导教程 或者 点我打开
    如果您发现没有下载许可, 请先验证邮箱再进行下载;金锭可通过每日登陆或资源出售获取,目前没有其他渠道可获取。

基于1.16.5 当前最新版Forge版本(36.2.42)的Forge与Bukkit的互相通讯的代码示例

YuaZer

Lv.1 泥土
高级创作者
2021-07-03
96
33
0
钻石
0.00 钻石
金锭
6,146 金锭
(由于我按照老头的教程 https://bbs.mc9y.net/threads/914/ 测试后发现没有用,我猜测应该是Forge更新导致的问题,于是我研究了一阵后,发布该教程)其他被Forge折磨的开发者可以看看本教程

Forge方面:
为了方便我们后续开发,我们在Forge Mod的代码里,添加一个事件类ServerToClientPacketCallback

Java:
package io.github.yuazer.huimod.api.events;

import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.eventbus.api.Event;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

public class ServerToClientPacketCallback extends Event {
    private ResourceLocation resourceLocation;
    private PacketBuffer friendlyByteBuf;

    public ServerToClientPacketCallback(ResourceLocation resourceLocation, PacketBuffer friendlyByteBuf) {
        this.resourceLocation = resourceLocation;
        this.friendlyByteBuf = friendlyByteBuf;
    }

    public boolean isCancelable() {
        return true;
    }

    public ResourceLocation getResourceLocation() {
        return this.resourceLocation;
    }

    public PacketBuffer getFriendlyByteBuf() {
        return this.friendlyByteBuf;
    }

    public void setResourceLocation(ResourceLocation resourceLocation) {
        this.resourceLocation = resourceLocation;
    }

    public void setFriendlyByteBuf(PacketBuffer friendlyByteBuf) {
        this.friendlyByteBuf = friendlyByteBuf;
    }

    @Override
    public String toString() {
        return "ServerToClientPacketCallback{" +
                "resourceLocation=" + resourceLocation +
                ", friendlyByteBuf=" + bufferToString(friendlyByteBuf) +
                '}';
    }
    public String getMessage() {
        return bufferToString(friendlyByteBuf);
    }
    private String bufferToString(PacketBuffer buffer) {
        if (buffer == null) {
            return "null";
        }
        PacketBuffer copy = new PacketBuffer(buffer.copy());
        byte[] bytes = new byte[copy.readableBytes()];
        copy.readBytes(bytes);
        try (DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes))) {
            return dataInputStream.readUTF();
        } catch (IOException e) {
            e.printStackTrace();
            return "[解析失败]";
        }
    }

}

最重要的地方来了!
使用Mixins,对SCustomPayloadPlayPacket的handleCustomPayload
进行修改,插入一段代码,在客户端收到自定义发包的时候,post我们刚才写的ServerToClientPacketCallback事件

Java:
package io.github.yuazer.huimod.mixin;

import io.github.yuazer.huimod.api.events.ServerToClientPacketCallback;
import net.minecraft.client.network.play.ClientPlayNetHandler;
import net.minecraft.network.play.server.SCustomPayloadPlayPacket;
import net.minecraftforge.common.MinecraftForge;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(ClientPlayNetHandler.class)
public class ClientPacketListenerMixin {

    @Inject(at = @At("HEAD"), method = "handleCustomPayload", cancellable = true)
    private void readPayload(SCustomPayloadPlayPacket packet, CallbackInfo ci) {
        // 创建自定义事件
        ServerToClientPacketCallback event = new ServerToClientPacketCallback(packet.getIdentifier(), packet.getData());

        // 触发 Forge 事件总线,如果被取消,则拦截默认行为
        if (MinecraftForge.EVENT_BUS.post(event)) {
            ci.cancel();
        }
    }
}
Forge接收信息的部分:
Java:
@SubscribeEvent

    public void onServerToClientPacket(ServerToClientPacketCallback event) {

        ResourceLocation identifier = event.getResourceLocation();

        //判断Channel是否是我们指定的Channel

        if (identifier.equals(HuiMod.CHANNEL)) {

            //获取来自Bukkit的信息

            String message = event.getMessage();

            event.setCanceled(true);

        }

    }
Forge发送信息的部分:
Java:
public static void send2Server(String message){
        if (Minecraft.getInstance().getConnection() == null) {
            System.out.println("[Forge] 未连接到服务器,无法发送消息");
            return;
        }

        try {
            // 使用 DataOutputStream 编码
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);
            dataOutputStream.writeUTF(message); // 这里确保写入的是标准 UTF-8
            dataOutputStream.close();

            byte[] data = byteArrayOutputStream.toByteArray();

            // 发送到 Bukkit
            PacketBuffer buffer = new PacketBuffer(Unpooled.wrappedBuffer(data));
            CCustomPayloadPacket packet = new CCustomPayloadPacket(CHANNEL, buffer);
            Minecraft.getInstance().getConnection().send(packet);

            System.out.println("[Forge] 发送消息到 Bukkit: " + message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
Bukkit收到信息和发送信息的代码:
Java:
import io.github.yuazer.huicore.HuiCore;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;

import java.io.*;


public class HuiMessage implements PluginMessageListener {
    public static final String CHANNEL = "huicore:zax";
    //注册频道
    public void loadNetwork() {
        HuiCore.getInstance().getServer().getMessenger().registerOutgoingPluginChannel(HuiCore.getInstance(), CHANNEL);
        HuiCore.getInstance().getServer().getMessenger().registerIncomingPluginChannel(HuiCore.getInstance(), CHANNEL, this);
    }
    //注销频道
    public void disableNetwork() {
        HuiCore.getInstance().getServer().getMessenger().unregisterOutgoingPluginChannel(HuiCore.getInstance(), CHANNEL);
        HuiCore.getInstance().getServer().getMessenger().unregisterIncomingPluginChannel(HuiCore.getInstance(), CHANNEL);
    }
    //接收来自Forge的信息
    @Override
    public void onPluginMessageReceived(String channel, Player player, byte[] message) {
        if (!channel.equals(CHANNEL)) {
            System.out.println("channel不匹配,当前channel:" + channel);
            return;
        }

        // 处理收到的数据包
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(message);
        DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);

        try {
            while (dataInputStream.available() > 0) {
                String receivedMessage = dataInputStream.readUTF();
                HuiCore.getInstance().getLogger().info("收到来自 Forge Mod 的消息: " + receivedMessage);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                dataInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //发送信息到Forge
    public void sendMessageToForgeClient(Player player, String message) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);

        try {
            dataOutputStream.writeUTF(message);
            player.sendPluginMessage(HuiCore.getInstance(), CHANNEL, byteArrayOutputStream.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                dataOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

!!!注意Channel必须统一,举例:
如果你的Bukkit插件的Channel名是 test:abc,那么你在Forge的Channel格式是
public static final ResourceLocation CHANNEL = new ResourceLocation("test", "abc");
 
!!!注意!!!

在当前Forge版本(36.2.42)中,SCustomPayloadPlayPacket 类下的方法名有所改动,比如getChannelName() -> getIdentifier()
getBufferData() -> getData()
 
打赏用户
废物随缘在线放鸽
如果该篇帖子帮助到了你,可以打赏点金粒或者回复一个谢谢你的示例
我会很开心
 
打赏用户
leu 南瓜NGK3 laoruimi