-
-
Notifications
You must be signed in to change notification settings - Fork 254
Plugin messaging Docs #196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
dbb9dc9
Sending Plugin Messages
DeJayDev 1d51198
Merge remote-tracking branch 'source/main' into plugin_messaging
olijeffers0n c624064
Plugin Messaging Docs
olijeffers0n 3443a4c
Apply suggestions from code review
olijeffers0n 940779b
Merge branch 'main' into plugin_messaging
olijeffers0n 681fceb
Update docs/paper/dev/api/plugin-messaging.md
olijeffers0n 5becef4
Add Note to the forward example
olijeffers0n b997cda
Merge branch 'main' into plugin_messaging
olijeffers0n 33fcf0b
Merge branch 'plugin_messaging' of https://github.com/olijeffers0n/pa…
olijeffers0n 22978d1
Use `BungeeCord` everywhere
olijeffers0n be70f0d
Add a bungee channel explanation
olijeffers0n File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| --- | ||
| slug: /dev/plugin-messaging | ||
| --- | ||
|
|
||
| # Plugin Messaging | ||
|
|
||
| First introduced in [2012](https://web.archive.org/web/20220711204310/https://dinnerbone.com/blog/2012/01/13/minecraft-plugin-channels-messaging/), | ||
| Plugin messaging is a way for Paper plugins to communicate with clients. When your servers are behind a proxy, | ||
| it will allow your Paper plugins to communicate with the proxy server. | ||
|
|
||
| ## BungeeCord Channel | ||
|
|
||
| The BungeeCord channel is used for communication between your Paper server and a BungeeCord (Or protocol supporting) proxy. | ||
|
|
||
| Originally, the channel supported by the BungeeCord proxy was called `BungeeCord`. In versions 1.13 and above, | ||
| the channel was renamed to `bungeecord:main` to create a key structure for plugin messaging channels. | ||
|
|
||
| Paper handles this change internally, and automatically changes any messages sent on the `BungeeCord` channel | ||
| to the `bungeecord:main` channel. This means that your Paper plugins should continue to use the `BungeeCord` channel. | ||
|
|
||
| ## Sending Plugin Messages | ||
|
|
||
| First, we're going to take a look at your Paper server. Your Paper plugin will need to register that it | ||
| will be sending on any given plugin channel. You should to do this alongside your other event listener registrations. | ||
|
|
||
| ```java | ||
| public final class PluginMessagingSample extends JavaPlugin { | ||
|
|
||
| @Override | ||
| public void onEnable() { | ||
| getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); | ||
| // Blah blah blah... | ||
| } | ||
|
|
||
| } | ||
| ``` | ||
|
|
||
| Now that we're registered, we can send messages on the `BungeeCord` channel. | ||
|
|
||
| Plugin messages are formatted as byte arrays and can be sent using the `sendPluginMessage` method on a `Player` object. | ||
| Let's take a look at an example of sending a plugin message to the `BungeeCord` channel to send our player to another server. | ||
|
|
||
| ```java | ||
| public final class PluginMessagingSample extends JavaPlugin implements Listener { | ||
|
|
||
| @Override | ||
| public void onEnable() { | ||
| getServer().getPluginManager().registerEvents(this, this); | ||
| getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); | ||
| } | ||
|
|
||
| @EventHandler | ||
| public void onPlayerJump(PlayerJumpEvent event) { | ||
| Player player = event.getPlayer(); | ||
|
|
||
| ByteArrayDataOutput out = ByteStreams.newDataOutput(); | ||
| out.writeUTF("Connect"); | ||
| out.writeUTF("hub2"); | ||
| player.sendPluginMessage(this, "BungeeCord", out.toByteArray()); | ||
| } | ||
|
|
||
| } | ||
| ``` | ||
|
|
||
| :::tip | ||
|
|
||
| These channels rely on the Minecraft protocol, and are sent as a special type of packet called a | ||
| [Plugin Message](https://wiki.vg/Plugin_channels#Plugin_Messages). They piggyback on player connections, so if there is no | ||
| player connected to the server, it will not be able to send or receive plugin messages. | ||
|
|
||
| ::: | ||
|
|
||
| ### What did we just do? | ||
|
|
||
| We sent a plugin message on the `BungeeCord` channel! The message we sent was a byte array that contained two strings converted to bytes: `Connect` and `hub2`. | ||
|
|
||
| Our proxy server received the message through the player who triggered the `PlayerJumpEvent` on our Java server. | ||
| Then, it recognized the channel as its own and in alignment with BungeeCord's format sent our player to the `hub2` server. | ||
|
|
||
| For BungeeCord, we can think of this message as a case-sensitive command with arguments. | ||
| Here, our command is `Connect` and our only argument is `hub2`, but some "commands" may have multiple arguments. | ||
| For other channels introduced by client side mods, refer to their documentation to best understand how to format your messages. | ||
|
|
||
| ### BungeeCord Plugin Message Types | ||
|
|
||
| Although we sent a `Connect` message to the proxy, there are a few other cases that proxies will act on. | ||
| These are the following: | ||
|
|
||
| | Message Type | Description | Arguments | Response | | ||
| |:------------------|:-------------------------------------------------------|:-----------------------------------------------------------------|:--------------------------------------------------| | ||
| | `Connect` | Connects the player to the specified server. | `server name` | N/A | | ||
| | `ConnectOther` | Connects another player to the specified server. | `player name`, `server name` | N/A | | ||
| | `IP` | Returns the IP of the specified player. | `player name` | `IP`, `port` | | ||
| | `PlayerCount` | Returns the number of players on the specified server. | `server name` | `server name`, player count` | | ||
| | `PlayerList` | Returns a list of players on the specified server. | `server name` | `server name`, `CSV player names` | | ||
| | `GetServers` | Returns a list of all servers. | N/A | `CSV server names` | | ||
| | `Message` | Sends a message to the specified player. | `player name`, `message` | N/A | | ||
| | `GetServer` | Returns the server the player is connected to. | N/A | `server name` | | ||
| | `UUID` | Returns the UUID of player. | N/A | `UUID` | | ||
| | `UUIDOther` | Returns the UUID of the specified player. | `player name` | `player name`, `UUID` | | ||
| | `ServerIp` | Returns the IP of the specified server. | `server name` | `server name`, `IP`, `port` | | ||
| | `KickPlayer` | Kicks the specified player. | `player name`, `reason` | N/A | | ||
| | `Forward` | Forwards a plugin message to another server. | `server`, `subchannel`, `size of plugin message`, `message` | `subchannel`, `size of plugin message`, `message` | | ||
| | `ForwardToPlayer` | Forwards a plugin message to another player. | `player name`, `subchannel`, `size of plugin message`, `message` | `subchannel`, `size of plugin message`, `message` | | ||
|
|
||
| #### Example: `PlayerCount` | ||
|
|
||
| ```java | ||
| public class MyPlugin extends JavaPlugin implements PluginMessageListener { | ||
|
|
||
| @Override | ||
| public void onEnable() { | ||
| this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); | ||
| this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", this); | ||
|
|
||
| Player player = ...; | ||
| ByteArrayDataOutput out = ByteStreams.newDataOutput(); | ||
| out.writeUTF("PlayerCount"); | ||
| out.writeUTF("lobby"); | ||
| player.sendPluginMessage(this, "BungeeCord", out.toByteArray()); | ||
| // The response will be handled in onPluginMessageReceived | ||
| } | ||
|
|
||
| @Override | ||
| public void onPluginMessageReceived(String channel, Player player, byte[] message) { | ||
| if (!channel.equals("BungeeCord")) { | ||
| return; | ||
| } | ||
| ByteArrayDataInput in = ByteStreams.newDataInput(message); | ||
| String subchannel = in.readUTF(); | ||
| if (subchannel.equals("PlayerCount")) { | ||
| // This is our response to the PlayerCount request | ||
| String server = in.readUTF(); | ||
| int playerCount = in.readInt(); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| #### Example: `Forward` | ||
|
|
||
| ```java | ||
| public class MyPlugin extends JavaPlugin implements PluginMessageListener { | ||
|
|
||
| @Override | ||
| public void onEnable() { | ||
| this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); | ||
| this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", this); | ||
|
|
||
| Player player = ...; | ||
| ByteArrayDataOutput out = ByteStreams.newDataOutput(); | ||
| out.writeUTF("Forward"); | ||
| out.writeUTF("ALL"); // This is the target server. "ALL" will message all servers appart from the one sending the message | ||
| out.writeUTF("SecretInternalChannel"); // This is the channel. | ||
|
|
||
| ByteArrayOutputStream msgbytes = new ByteArrayOutputStream(); | ||
| DataOutputStream msgout = new DataOutputStream(msgbytes); | ||
| msgout.writeUTF("Paper is the meaning of life"); // You can do anything you want with msgout | ||
| msgout.writeShort(42); // Writing a random short | ||
|
|
||
| out.writeShort(msgbytes.toByteArray().length); // This is the length. | ||
| out.write(msgbytes.toByteArray()); // This is the message. | ||
|
|
||
| player.sendPluginMessage(this, "BungeeCord", out.toByteArray()); | ||
| // The response will be handled in onPluginMessageReceived | ||
| } | ||
|
|
||
| @Override | ||
| public void onPluginMessageReceived(String channel, Player player, byte[] message) { | ||
| if (!channel.equals("BungeeCord")) { | ||
| return; | ||
| } | ||
| ByteArrayDataInput in = ByteStreams.newDataInput(message); | ||
| String subchannel = in.readUTF(); | ||
| if (subchannel.equals("SecretInternalChannel")) { | ||
| short len = in.readShort(); | ||
| byte[] msgbytes = new byte[len]; | ||
| in.readFully(msgbytes); | ||
|
|
||
| DataInputStream msgIn = new DataInputStream(new ByteArrayInputStream(msgbytes)); | ||
| String secretMessage = msgIn.readUTF(); // Read the data in the same way you wrote it | ||
| short meaningofLife = msgIn.readShort(); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| This message is used to forward a plugin message to another server. This is useful for server-to-server communication within a proxy network. | ||
| For example, if a certain player is banned on one server, you can forward a message to all other servers to ban them there too. | ||
|
|
||
| :::caution[Example of banning a player on all servers] | ||
|
|
||
| This is not a recommended way to ban players due to the fact that there may not be anyone online on the target servers, | ||
| but it is an example of how this can be used. | ||
|
|
||
| ::: | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| --- | ||
| slug: /dev/plugin-messaging | ||
| --- | ||
|
|
||
| # Plugin Messaging | ||
|
|
||
| First introduced in [2012](https://web.archive.org/web/20220711204310/https://dinnerbone.com/blog/2012/01/13/minecraft-plugin-channels-messaging/), | ||
|
olijeffers0n marked this conversation as resolved.
|
||
| Plugin messaging is a way for Paper plugins to communicate with clients. | ||
|
|
||
| ## Compatibility with BungeeCord | ||
|
|
||
| When your servers are behind a proxy, it will allow your Paper plugins to communicate with the proxy server. | ||
| By default, your Velocity server will respond to the `bungeecord:main` channel unless you have disabled | ||
| `bungee-plugin-message-channel` in the Velocity configuration. Let's take a look at how plugin messaging works, | ||
| and more importantly, how your Velocity server is already prepared to handle it. | ||
|
|
||
| ## Sending Plugin Messages | ||
|
|
||
| Firstly, our Velocity server will need to register that it will be sending on any given plugin channel. | ||
| You should to do this alongside your other event listener registrations: | ||
|
|
||
| ```java | ||
| @Subscribe | ||
| public void onProxyInitialization(ProxyInitializeEvent event) { | ||
| proxyServer.getChannelRegistrar().register(MinecraftChannelIdentifier.from("bungeecord:main")); | ||
| } | ||
| ``` | ||
|
|
||
| Then, you can send messages on the `bungeecord:main` channel. | ||
| Plugin messages are formatted as byte arrays and can be sent using the `sendPluginMessage` method on a `Player` object. | ||
|
|
||
| Let's take a look at an example of sending a plugin message to the `bungeecord:main` channel to send our player to another server. | ||
|
|
||
| ```java | ||
| @Subscribe | ||
| public void onPlayerChat(PlayerChatEvent event) { | ||
| Player player = event.getPlayer(); | ||
|
|
||
| ByteArrayDataOutput out = ByteStreams.newDataOutput(); | ||
| out.writeUTF("Connect"); | ||
| out.writeUTF("hub2"); | ||
| player.sendPluginMessage(plugin, "bungeecord:main", out.toByteArray()); | ||
| } | ||
| ``` | ||
|
|
||
| ## Receiving Plugin Messages | ||
|
|
||
| Now that we've sent a plugin message, we'll need to receive it on the other end. | ||
| This is done by registering a listener for the PluginMessageEvent. | ||
|
|
||
| ```java | ||
| @Subscribe | ||
| public void onPluginMessage(PluginMessageEvent event) { | ||
| if (!event.getIdentifier().getId().equals("identifier")) { | ||
| return; | ||
| } | ||
|
|
||
| ByteArrayDataInput in = ByteStreams.newDataInput(event.getData()); | ||
| String subChannel = in.readUTF(); | ||
|
|
||
| if (subChannel.equals("Connect")) { | ||
| String server = in.readUTF(); | ||
| // Do something with the server name | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| :::tip[The "bungeecord" specification] | ||
|
|
||
| See [here](/paper/dev/plugin-messaging#bungeecord-plugin-message-types) for a list of all the plugin messages that BungeeCord / Velocity supports. | ||
|
|
||
| ::: | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The player count in the last column doesn't seems well formatted