BungeeCord 插件消息频道

本教程讲解 Bukkit 和 BungeeCord 的插件消息频道(Plugin Messaging Channel)的使用。该频道允许 BungeeCord 和其他服务器的插件通过连接该频道进行交流。

插件消息(Plugin message)是什么?

插件消息被实现后,服务端插件可以发送任何数据到客户端模组。在此之前,我们需要自行实现自定义的数据包,但问题是,Minecraft 客户端无法读取服务器自定义的数据包,而导致出现一些意义不明的错误,并与服务器断开连接。为了修复这个问题,Mojang 实现了 “自定义插件消息包(Custom Plugin Message Packets)”

一个插件消息数据包(Plugin Message Packet)的结构是这样的:
包含一个插件消息需要使用的频道(Channel)的名称(在 BungeeCord 里也叫标签(tag))。在这里了解更多信息

BungeeCord 插件频道

BungeeCord 暴露了一个叫 “BungeeCord” 的插件频道在后端/Bukkit 服务器。你需要使用 DataInputStream 和 DataOutputStream 类来处理你需要发送(接收)的字节数组(byte[])。为了方便起见,我们使用 Google 的 Guava 的 ByteArrayDataInput 和 ByteArrayDataOutput 类,因为这不会抛出 IOException 。

使用 BungeeCord 插件频道之前,你需要在 Bukkit 的 Messenger 注册它。通俗的说,你需要告诉 Bukkit “嘿,我想使用这个频道。”我们也需要告诉 Bukkit 当它收到来自频道 “BungeeCord” 的消息时调用的方法。这是一个简单的插件例子:

  1. public class MyPlugin extends JavaPlugin implements PluginMessageListener {
  2. @Override
  3. public void onEnable() {
  4. this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
  5. this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", this);
  6. }
  7. @Override
  8. public void onPluginMessageReceived(String channel, Player player, byte[] message) {
  9. if (!channel.equals("BungeeCord")) {
  10. return;
  11. }
  12. ByteArrayDataInput in = ByteStreams.newDataInput(message);
  13. String subchannel = in.readUTF();
  14. if (subchannel.equals("SomeSubChannel")) {
  15. // 使用下文中的"返回(Response)"一节的代码进行读取
  16. // 数据处理
  17. }
  18. }
  19. }

发送插件消息是非常简单的:

  1. ByteArrayDataOutput out = ByteStreams.newDataOutput();
  2. out.writeUTF("Subchannel");
  3. out.writeUTF("Argument");
  4. // 如果你不关心玩家是谁,可以使用下面的代码
  5. // Player player = Iterables.getFirst(Bukkit.getOnlinePlayers(), null);
  6. // 或者,你希望指定一个玩家接收消息
  7. Player player = Bukkit.getPlayerExact("Example");
  8. player.sendPluginMessage(this, "BungeeCord", out.toByteArray());

注意,你不能直接在玩家加入事件 (PlayerJoinEvent)发送插件消息。你必须有一点延迟。

BungeeCord 频道约定

连接到服务器(Connect)

使一个玩家连接到指定服务器.

参数 (Arguments)

  • (String)你希望连接到的服务器名称,必须要在 BungeeCord 的配置文件(config.yml)内设置。

接收者(Receiver)

你希望传送的玩家

发送例子

  1. out.writeUTF("Connect");
  2. out.writeUTF("pvp");

返回(Response)

使玩家连接到服务器(ConnectOther)

使一个指定玩家连接到指定服务器

参数

  • (String)你希望传送的玩家名称
  • (String)你希望连接到的服务器名称,必须要在 BungeeCord 的配置文件(config.yml)内设置。

接收者

任何玩家

发送例子

  1. out.writeUTF("ConnectOther");
  2. out.writeUTF("roblabla");
  3. out.writeUTF("pvp");

返回

获取玩家IP(IP)

获取玩家的真实IP

参数

接收者

你希望获取IP的玩家

例子

  1. out.writeUTF("IP");

返回

  1. String ip = in.readUTF();
  2. int port = in.readInt();

获取玩家数量(PlayerCount)

获取某个服务器或者所有服务器的玩家数量

参数

  • (String) 你希望获取玩家数量的服务器名称,如果你希望获取所有服务器的玩家数量,使用 “ALL”

接收者

任何玩家

例子

  1. out.writeUTF("PlayerCount");
  2. out.writeUTF("pvp");

返回

  1. String server = in.readUTF(); // 服务器的名称,如给定的参数所示
  2. int playercount = in.readInt();

获取玩家列表(PlayerList)

获取某个服务器或者所有服务器的玩家名称

参数

  • (String) 你希望获取玩家名称的服务器名词,如果你希望获取全部服务器的玩家名称,使用 “ALL”

接收者

任何玩家

例子

  1. out.writeUTF("PlayerList");
  2. out.writeUTF("pvp");

返回

  1. String server = in.readUTF(); // 你想要获取玩家列表的服务器的名称,如给定的参数所示
  2. String[] playerList = in.readUTF().split(", ");

获取所有服务器名称(GetServers)

获取所有服务器的名称, 必须要在 BungeeCord 的配置文件(config.yml)内设置

参数

接收者

任何玩家

例子

  1. out.writeUTF("GetServers");

返回

  1. String[] serverList = in.readUTF().split(", ");

发送消息(Message)

向指定玩家发送一个消息(比如聊天消息)

参数

  • (String)需要发送消息的玩家名称
  • (String)需要发送的消息内容

接收者

任何玩家

例子

  1. out.writeUTF("Message");
  2. out.writeUTF("roblabla");
  3. out.writeUTF(ChatColor.RED + "Congrats, you just won 1$!");

返回

获取服务器名称(GetServer)

获得当前服务器名称,必须在 BungeeCord 的配置文件(config.yml)内设置

参数

接收者

任何玩家

例子

  1. out.writeUTF("GetServer");

返回

  1. String servername = in.readUTF();

发送自定义插件消息(Forward)

发送一个自定义插件消息(Custom plugin message)到指定服务器。这是最有效的方式之一。
记住,发送和接受服务器都必须至少有一个玩家在线。

参数

  • (String)接受插件消息的服务器名, “ALL” 表示发送到所有服务器(除了发送插件消息的服务器), “ONLINE” 表示发送到每一个在线的服务器(除了发送插件消息的服务器)
  • (String)插件使用的子频道(Subchannel)名称
  • (Short)插件消息的数组长度
  • (Byte[])需要发送的插件消息

接收者

任何玩家

例子

  1. out.writeUTF("Forward"); // 这样写BungeeCord就知道要转发它
  2. out.writeUTF("ALL");
  3. out.writeUTF("MyChannel"); // 用于检查这是否是你的数据的频道名称
  4. ByteArrayOutputStream msgbytes = new ByteArrayOutputStream();
  5. DataOutputStream msgout = new DataOutputStream(msgbytes);
  6. msgout.writeUTF("Some kind of data here"); // 你可以用msgout发送任何你想发送的数据
  7. msgout.writeShort(123);
  8. out.writeShort(msgbytes.toByteArray().length);
  9. out.write(msgbytes.toByteArray());

返回

  1. String subChannel = in.readUTF();
  2. short len = in.readShort();
  3. byte[] msgbytes = new byte[len];
  4. in.readFully(msgbytes);
  5. DataInputStream msgin = new DataInputStream(new ByteArrayInputStream(msgbytes));
  6. String somedata = msgin.readUTF(); // 以与写入数据相同的顺序读取数据
  7. short somenumber = msgin.readShort();

发送自定义插件消息到玩家(ForwardToPlayer)

发送自定义插件消息(Custom plugin message)到指定玩家

参数

  • (String)接受消息的玩家名称
  • (String)插件使用的子频道(Subchannel)名称
  • (Short)插件消息的数组长度
  • (Byte[])需要发送的插件消息

接收者

任何玩家

例子

  1. out.writeUTF("ForwardToPlayer");
  2. out.writeUTF("md_5");
  3. out.writeUTF("MyChannel");
  4. ByteArrayOutputStream msgbytes = new ByteArrayOutputStream();
  5. DataOutputStream msgout = new DataOutputStream(msgbytes);
  6. msgout.writeUTF("Some kind of data here");
  7. msgout.writeShort(123);
  8. out.writeShort(msgbytes.toByteArray().length);
  9. out.write(msgbytes.toByteArray());

返回

  1. String subChannel = in.readUTF();
  2. short len = in.readShort();
  3. byte[] msgbytes = new byte[len];
  4. in.readFully(msgbytes);
  5. DataInputStream msgin = new DataInputStream(new ByteArrayInputStream(msgbytes));
  6. String somedata = msgin.readUTF(); // 以与写入数据相同的顺序读取数据
  7. short somenumber = msgin.readShort();

获取玩家的UUID(UUID)

获取该玩家的UUID

参数

接收者

需要获取UUID的玩家

例子

  1. out.writeUTF("UUID");

返回

  1. String uuid = in.readUTF();

获取指定玩家的UUID(UUIDOther)

获取指定玩家的UUID

参数

  • (String)你需要获取UUID的玩家的名称

接收者

发送者(译注:原文如此,可能是任何玩家)

例子

  1. out.writeUTF("UUIDOther");
  2. out.writeUTF("Notch");

返回

  1. String playerName = in.readUTF();
  2. String uuid = in.readUTF();

获取服务器IP(ServerIP)

获取指定服务器在群组中的IP

参数

  • (String) 需要获取IP的服务器名称

接收者

任何玩家

例子

  1. out.writeUTF("ServerIP");
  2. out.writeUTF("lobby");

返回

  1. String serverName = in.readUTF();
  2. String ip = in.readUTF();
  3. int port = in.readUnsignedShort();

踢出玩家(KickPlayer)

从整个服务器踢出玩家

参数

  • (String)玩家名
  • (String)踢出玩家的理由

接收者

任何玩家

例子

  1. out.writeUTF("KickPlayer");
  2. out.writeUTF("Notch");
  3. out.writeUTF("You were kicked from the server");

返回