Skip to content

messaging channels

Fulminazzo edited this page Oct 17, 2023 · 4 revisions

How to work with Messaging Channel

If you have been working with Bukkit and BungeeCord/Velocity for a while, you will surely have encountered the term Plugin Messaging Channel. If you want to know more about this technology and a little bit of history, I will link you to Spigot article on how Bukkit and BungeeCord implement this, but in synthesis the Messaging Channels are a way with which a Proxy servers and a Bukkit servers communicate with each other. What can be done with this communication is up to the user, but if you have experience with socket connections you will realize how powerful this system is.
However, especially for a newcomer, this might be intimidating, or they can find it difficult to work with certain Java objects (DataStreams). This is where BearCommands comes in: the plugin offers a custom method to send and receive messages among plugins.
We will use Bukkit as our Sender and BungeeCord as our Receiver, however do keep in mind that not only Velocity supports everything we are going to talk about, but it is also interchangeable, meaning that BungeeCord/Velocity can be the Sender while Bukkit the Receiver.

Getting started: MessagingChannel

To start with sending or receiving data, you first have to register your custom channel. You can use the MessagingChannel class to create it:

public MessagingChannel(IBearPlugin<?> plugin, String channel);

This will create a channel with name <plugin-name>:<channel>.
NOTE: you can also directly pass the channel name, without the plugin, but it is not recommended for identifying purposes.
Do keep in mind that channel must be equal among platforms, meaning that if my Bukkit channel is bukkitplugin:channel, I cannot use bungeeplugin:channel in BungeeCord.

Sending Messages: Bukkit

Now that we have our custom channel ready, we can start talking!
Before doing that though, we have to tell the server that we are going to communicate using it. Assuming you have followed the How to start a plugin guide and that your plugin main class is an instance of IBearPlugin, you can use addMessagingChannel():

public void addMessagingChannel(MessagingChannel channel);

This will make sure to register any channel passes as an outgoing plugin channel.
Communicating is really simple, thanks to cross-platform MessagingUtils class. All you have to do, is invoke sendPluginMessage() with the right parameters:

public static void sendPluginMessage(IBearPlugin<?> plugin, PlayerWrapper receiver, MessagingChannel channel, Object... data);

A couple of notes before we continue:


That's great and all, but what should I send? MessagingCommands

BearCommands uses a custom object to handle any message received from a messaging channel: MessagingCommand.
This is an abstract class that you can use to create your own requests to the receiver and handle them accordingly. To better understand this, let's check out how ExecuteCommand works, an implementation of MessagingCommand used by the executeBungeeCommand() method:

public class ExecuteCommand extends MessagingCommand {
    private final IBearPlugin<?> plugin;

    public ExecuteCommand(IBearPlugin<?> plugin) {
        super("executecommand");
        this.plugin = plugin;
    }

    @Override
    public void execute(PlayerWrapper player, DataInputStream inputStream) {
        try {
            String command = inputStream.readUTF();
            if (command.startsWith("/")) command = command.substring(1);
            if (ServerUtils.isVelocity()) {
                ((VelocityBearPlugin<?>) plugin).getProxyServer().getCommandManager().executeAsync(player.getPlayer(), command);
            } else ServerUtils.getPluginManager().callMethod("dispatchCommand",
                    new Class<?>[]{ReflUtil.getClass("net.md_5.bungee.api.CommandSender"), String.class},
                    player.getPlayer(), command);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

There are only two things to keep in mind:

  • A constructor that specifies the command name by using the super() call;
  • The execute(PlayerWrapper player, DataInputStream inputStream) method, with:
    • PlayerWrapper player as the player sending the message (the sender);
    • DataInputStream inputStream as the data sent.

As you might immagine, using this kind of system allows to **easier interactions** among platforms while using the messaging channels.

Receiving Messages: BungeeCord

Now that we have seen how to send messages and how to prepare commands, it is time to listen to them. We can do this thanks to a Listener provided for every platform (Bukkit: BukkitMessagingListener, BungeeCord: BungeeMessagingListener, Velocity: VelocityMessagingListener). However, BearCommands makes sure to simplify even more the listening, by providing a universal method from the IBearPlugin interface:

public void addMessagingListener(MessagingChannel channel, MessagingCommand... commands);

Just as we have seen when sending messages, you need to add the same messaging channel of the sender part. Then, you can just add any MessagingCommand that you desire, and the plugin will automatically handle the rest for you.
Let's continue with our example and see how BearCommands prepares to listen to executeBungeeCommand() requests:

@Override
public void onEnable() {
    addMessagingListener(BearMessagingChannel.MESSAGING_CHANNEL, new ExecuteCommand(this));
    super.onEnable();
}

NOTE: it is crucial that you call the addMessagingListener() method before enabling the plugin, as every channel and listener will be registered when calling the loadMessagingChannels() and loadListeners() methods.

Clone this wiki locally