关于forge和bukkit通信,查阅了很多资料发现大多都已经过时,或是不详细。自己折腾了一番,记录一下

forge 接收方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

@Mod("story_hud_show")
public class StoryHudShow
{
private SimpleChannel channel;
private static final int idx = 222;

public static String[] data = {"23456","星期天14日","上午9:00"};


public StoryHudShow()
{
...
}



private void setup(final FMLCommonSetupEvent event)
{
channel = NetworkRegistry.ChannelBuilder.named(new ResourceLocation("story_hud_show", "main_channel"))
.networkProtocolVersion(() -> "bzdo")
.serverAcceptedVersions(NetworkRegistry.ACCEPTVANILLA::equals)
.clientAcceptedVersions(NetworkRegistry.ACCEPTVANILLA::equals)
.simpleChannel();
channel.registerMessage(idx,String.class,this::enc,this::dec,this::proc);
}
private void enc(String str, FriendlyByteBuf buffer) {
buffer.writeBytes(str.getBytes(StandardCharsets.UTF_8));
}

private String dec(FriendlyByteBuf buffer) {
return buffer.toString(StandardCharsets.UTF_8);
}

private void proc(String str, Supplier<NetworkEvent.Context> supplier) {
System.out.println(str);
data = str.split("/");
NetworkEvent.Context context = supplier.get();
context.setPacketHandled(true);
channel.reply("rpl", context);
}
}

ChannelBuilder用于构建一个频道,显而易见是一个工厂类,所示代码中用到的方法解释如下

named 其中需传入一个ResourceLocation,如资源文件一般的引用方式 namespace:path
networkProtocolVersion 为网络协议版本
server||client AcceptedVersion 参数为NetworkRegistry.ACCEPTVANILLA::equals表示允许未安装forge的原版客户端进入
最后使用simpleChannel得到SimpleChannel对象

SimpleChannel就是我们的主角了,接下来使用registerMessage注册完消息种类

channel.registerMessage(idx,String.class,this::enc,this::dec,this::proc);

其中,idx用于区分不同的消息类型;第二个参数为数据类型,在示例中使用String;后面跟着的分别是编码和解码的处理方法
如此,我们便完成了消息频道的注册,proc便是接收消息的处理了,其中的channel.reply为接收到消息后,对服务端的回复
(其实不写也无关紧要,只是会刷报错不雅观,当然玩家也看不到)

bukkit 发送方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public final class StarHudServerPackPlugin extends JavaPlugin implements Listener {
private static final int idx = 222;
private final String channel = "star_hud_show:main_channel";
@Override
public void onEnable() {
// Plugin startup logic
getServer().getMessenger().registerIncomingPluginChannel(this,channel,(channel,player,message)->{
getLogger().info("received message from client");
getLogger().info(message.toString());
});
getServer().getMessenger().registerOutgoingPluginChannel(this, channel);
getServer().getPluginManager().registerEvents(this, this);
}


private void send(Player player, String msg) {
msg = "23846"+"/"+"星期天14日"+"/"+"上午9:00";
byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
ByteBuf buf = Unpooled.buffer(bytes.length + 1);
buf.writeByte(idx);
buf.writeBytes(bytes);
player.sendPluginMessage(this, channel, buf.array());
}

private String read(byte[] array) {
ByteBuf buf = Unpooled.wrappedBuffer(array);
if (buf.readUnsignedByte() == idx) {
return buf.toString(StandardCharsets.UTF_8);
} else throw new RuntimeException();
}

@EventHandler
void onJoin(PlayerJoinEvent event){
Player player = event.getPlayer();
try {
Class<? extends CommandSender> senderClass = player.getClass();
Method addChannel = senderClass.getDeclaredMethod("addChannel", String.class);
addChannel.setAccessible(true);
addChannel.invoke(player, channel);
} catch (Exception e) {
e.printStackTrace();
}
Bukkit.getScheduler().runTaskLater(this,
() -> send(player, "23846"+"/"+"星期天14日"+"/"+"上午9:00"), 100);
}

@Override
public void onDisable() {
// Plugin shutdown logic
}
}

bukkit的代码就较为熟悉了,其中疑惑是这个丑陋的反射是怎么回事?
原因是在1.13之后的版本,forge客户端不会在加入前向服务器注册消息通道,只能用反射假装我们已经收到了register包hh

有了forge和bukkit通信,可以做到很多有意思的事情,且负荷都在客户端,服务端只需发个包就好了。那些付费的龙核,vv等也都是一样的原理
比如说,用hud展示服务器信息,这可比计分板优雅多了
image.png