(由于我按照老头的教程 https://bbs.mc9y.net/threads/914/ 测试后发现没有用,我猜测应该是Forge更新导致的问题,于是我研究了一阵后,发布该教程)其他被Forge折磨的开发者可以看看本教程
Forge方面:
为了方便我们后续开发,我们在Forge Mod的代码里,添加一个事件类ServerToClientPacketCallback
最重要的地方来了!
使用Mixins,对SCustomPayloadPlayPacket的handleCustomPayload
进行修改,插入一段代码,在客户端收到自定义发包的时候,post我们刚才写的ServerToClientPacketCallback事件
Forge接收信息的部分:
Forge发送信息的部分:
Bukkit收到信息和发送信息的代码:
!!!注意Channel必须统一,举例:
如果你的Bukkit插件的Channel名是 test:abc,那么你在Forge的Channel格式是
public static final ResourceLocation CHANNEL = new ResourceLocation("test", "abc");
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();
}
}
}
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);
}
}
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();
}
}
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");