Some keen eyed developers may have noticed that there's a new packet in Minecraft 1.1, with no purpose or usage anywhere in the codebase. You may be wondering why this was added, and what use it could possibly have. You may also be wondering who cares, and why you have this page open. I'm here to answer all of these questions in a nice little (disclaimer: may not be little) blog post!
Update: See the FAQ on this over here! Now with simple questions like "but what does this actually mean?"
Backstory
We did some thinking a while back, about how the state of client mods and server mods for minecraft isn't really what it could be. There's so many problems with conflicts and one mod causing a bug in another and so forth, that it's really harming the potential of Minecraft modding in general. While we by no means think that we can solve this, we did start hypothesizing ways that could at least improve how mods play together, and try to make things a nicer experience for everyone - the users, the client modders, and the server modders. Most importantly, those who fit in all of the above!
One of the biggest issues with client and server modding is that having both can be hell for your users. If you have a server mod that needs a client mod, if a user without the client mod tries to join then they'll crash with seemingly no cause. Ditto for users with client mods joining a server and crashing themselves, because the server mod isn't compatible with that client mod.
Honestly, this whole thing is a mess. I'm sure most people will agree with me here. There are attempted workarounds, sure, but they can break with a minecraft update and things just get worse from there. So this is where we started to talk to Mojang about improving this.
We designed a system with the help of Jens that should solve the compatibility issues between client and server mods, permanently. It's inspired by World of Warcrafts own approach to a similar (but not quite as big) issue, and we've just expanded on it. I call it Plugin Channels.
For All Modders
The system is basically as follows:
The server and client both have a list of their own installed mods, and these mods register for specific channel(s) each (they can mix'n'match, or have none at all, it doesn't matter). The client tells the server what channels it's listening on, and the server does the same to the client. Now they have a good idea about what the other side can support.
A client mod can then send a Plugin Message to the server by using a channel specific for that type of message that it wishes to send. The server will receive this message and the mod on the server side will read it and do its thing with the contents.
Because they have a list of what the other side supports, there's no bandwidth loss here. If the server wants to send a picture to the client (these messages are pure bytes, it can hold anything), but the client doesn't have anything listening on this Plugin Channel, it will know not to send it.
This system is implemented in vanilla, and you will not crash any vanilla client (or server!) using this system, not or in the future. What's not to love about that?
Example
Let's pretend our server has a mod installed that allows custom client skins. This works by sending a Plugin Message on the channel "SuperSkin:Set". A client mod by the same author listens on this channel for a Plugin Message, and turns that into a png, which is very easy because the message entirety is just the png compressed into an array of bytes. It then changes the players skin to the png upon receipt.
We have a player join, "James". He has this client mod installed. His client contacts the server and informs it that it has a mod listening for "SuperSkin:Set", and the server remembers this. We have another player join, "Sarah". She does not have this client mod installed.
The server mod decides to give both players a new skin with funky t-shirts, so it sends a Plugin Message to both players through the channel "SuperSkin:Set" with the skin encoded into the message. The server mod goes through whatever API wrapper is in place, and it decided to send the message to James because he's listening for this channel and thus can support the message. It doesn't send it to Sarah, because her client didn't inform the server that she can support this, because she can't.
James goes into third-person-view and notices he has a cool T-Shirt on, and wonders what sorcery makes this possible. He tells Sarah to do the same, because it looks really cool. She gives it a try, but no dice - she didn't have the mod installed, so nothing happened. However, she didn't crash from receiving a bad packet - so she's happy!
For API writers
There should be a basic API in place for this system to be easy for ordinary client and server modders to use. It should handle registering of Plugin Channels, unregistering also if required, and should make sure that no Plugin Messages are sent to any clients that haven't announced that they are listening for that specific channel.
On a packet scale, this is pretty simple. The packet ID is 250 (0xFA), and the format is as follows:
String, // Plugin Channel - something unique to the mod and this type of message. 16 chars max.
int, // Message Length - length of the message, must be below short.MAX_LENGTH (32,767).
byte[] // Plugin Message - the message itself, whatever it may be.
The channel can be anything at all, with two exceptions (to follow shortly), but is case sensitive. It has a 16 char restriction which should be plenty for now, but I'm sure Mojang will increase it if required. The message contents is entirely up to the mod sending the message, but has a max length of 32,766. If this is an issue, you can easily make something split across multiple messages.
When you have a mod that requests to listen on a specific Plugin Channel, you should send a Plugin Message with the channel "REGISTER", followed by the name (or names, separated by 0) of the channels it's now listening on. If a plugin decided that it's no longer listening on a channel, you can send an "UNREGISTER" message the same way as a "REGISTER". These two channels are marked as off-limits and no mods should be able to register these two channels. They are currently the only reserved channels.
Bukkit Plugin Devs
The API for Bukkit has already been designed and at time of writing, has been pushed live. The basic usage is this:
// This informs Bukkit that you will send messages through that channel
Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, channel);
// This informs Bukkit that you want to receive messages through that channel, to myPluginMessageListener
Bukkit.getMessenger().registerIncomingPluginChannel(plugin, channel, myPluginMessageListener);
class MyPluginMessageListener implements PluginMessageListener {
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
// Do something with this message from the player
}
}
somePlayer.sendPluginMessage(plugin, channel, message);
For full API documentation, consult the javadocs. You can find the commit over yonder
For the Users
Hopefully, you won't have any big issues between client mods and server mods anymore. Rejoice :)
For those who bothered to read to the bottom
I salute you.
Comments