-
-
Notifications
You must be signed in to change notification settings - Fork 27
Plugin Messaging Protocol
LOOHP edited this page Dec 31, 2021
·
5 revisions
InteractiveChat communicates by sending packets through plugin messaging to transfer data between backend servers and the proxy. A packet is a sequence of bytes sent over using plugin messaging. The meaning of a packet depends on its packet ID.
Data Type | Size (Byte) | Encode | Notes |
---|---|---|---|
Boolean | 1 | Either false or true | True is encoded as 0x01, false as 0x00. |
Byte | 1 | An integer between -128 and 127 | Signed 8-bit integer, two's complement. |
Short | 2 | An integer between -32768 and 32767 | Signed 16-bit integer, two's complement. |
Int | 4 | An integer between -2147483648 and 2147483647 | Signed 32-bit integer, two's complement. |
Long | 8 | An integer between -9223372036854775808 and 9223372036854775807 | Signed 64-bit integer, two's complement. |
String | ≥ 4 | A sequence of Unicode scalar values | Usually a UTF-8 string prefixed with its size in bytes as an Int. |
ItemStack | ≥ 4 | See below | |
Inventory | ≥ 4 | See below | |
UUID | 16 | A UUID | Encoded as an unsigned 128-bit integer (or two unsigned 64-bit integers: the most significant 64 bits and then the least significant 64 bits) |
Optional X | 0 or size of X | A field of type X, or nothing | Whether or not the field is present must be known from the context. |
Array of X | Count times size of X | Zero or more fields of type X | The count must be known from the context. |
Byte Array | Varies | Depends on context | This is just a sequence of zero or more bytes, its meaning should be explained somewhere else, e.g. in the packet description. |
ItemStack
Field Name | Field Type | Notes | ||
---|---|---|---|---|
Encoding Format | Byte | Determine which ItemStack encoding scheme is used below | ||
ItemStack | Encoding Format | Field Name | ||
0 | ItemStack | String | ItemStack encoding as a String with the key "i" serialized into YAML using Bukkit API | |
1 | Is Present | Boolean | If the ItemStack is not null | |
Material | Optional String | The ItemStack's material encoded as a String using XMaterial, only if Is Present is true | ||
Amount | Optional Int | The size of the ItemStack, only if Is Present is true | ||
Is Damagable | Optional Boolean | If the ItemStack is damable, only if Is Present is true | ||
Durability | Optional Int | The ItemStack's durability, only if Is Present is true and Is Damagable is true | ||
NBT | Optional String | The ItemStack's NBT tag encoded as Mojangson, only if Is Present is true |
Inventory
Field Name | Field Type | Notes | ||
---|---|---|---|---|
Encoding Format | Byte | Determine which Inventory encoding scheme is used below | ||
Has Title | Boolean | Whether the Inventory has a title | ||
Title | Optional String | Only if Has Title is true | ||
Inventory | Encoding Format | Field Name | ||
0 | Inventory | String | Inventory encoded as a base64 String, see below | |
1 | Size | Int | The size of the Inventory | |
Contents | Array of ItemStack | One ItemStack for each slot in Size |
Example on encoding and decoding a Inventory Base64 Hash
public static String toBase64(Inventory inventory) throws IllegalStateException {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
// Write the size of the inventory
dataOutput.writeInt(inventory.getSize());
// Save every element in the list
for (int i = 0; i < inventory.getSize(); i++) {
dataOutput.writeObject(inventory.getItem(i));
}
// Serialize that array
dataOutput.close();
return Base64Coder.encodeLines(outputStream.toByteArray());
} catch (Exception e) {
throw new IllegalStateException("Unable to save item stacks.", e);
}
}
public static Inventory fromBase64(String data, String title) throws IOException {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
Inventory inventory = null;
if (title == null || title.equals("")) {
inventory = Bukkit.getServer().createInventory(null, dataInput.readInt());
} else {
inventory = Bukkit.getServer().createInventory(null, dataInput.readInt(), title);
}
// Read the serialized inventory
for (int i = 0; i < inventory.getSize(); i++) {
inventory.setItem(i, (ItemStack) dataInput.readObject());
}
dataInput.close();
return inventory;
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
}
Due to plugin messaging packets having a limit of 32767 bytes, packets are compressed and then split into chunks.
Format
Field Name | Field Type | Notes |
---|---|---|
Packet Number | Int | A randomly generated Integer for each packet (Same for each chunk of the same packet) |
Packet ID | Byte | |
Is Last Chunk | Boolean | Whether this chunk is the last chunk sent for this packet |
Data | Byte Array | Depends on the packet ID, see the sections below |
Compression
public static byte[] compress(byte[] data) throws IOException {
Deflater deflater = new Deflater();
deflater.setInput(data);
deflater.setLevel(Deflater.BEST_SPEED);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
deflater.finish();
byte[] buffer = new byte[1024];
while (!deflater.finished()) {
int count = deflater.deflate(buffer); // returns the generated code... index
outputStream.write(buffer, 0, count);
}
outputStream.close();
byte[] output = outputStream.toByteArray();
return output;
}
public static byte[] decompress(byte[] data) throws IOException, DataFormatException {
Inflater inflater = new Inflater();
inflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] buffer = new byte[1024];
while (!inflater.finished()) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
byte[] output = outputStream.toByteArray();
return output;
}
Player List Data
Packet ID | Bound To | Field Name | Field Type | Notes | |
---|---|---|---|---|---|
0x00 | Backend | Size | Int | The amount of players in the following array | |
Array | Server | String | The name of the server in which the player is connected | ||
UUID | UUID | The player's UUID | |||
Display Name | String | The player's display name |
Ping and Encoding Formats
Packet ID | Bound To | Field Name | Field Type | Notes |
---|---|---|---|---|
0x01 | Backend | Delay | Int | The expected latency of the network |
ItemStack Encoding Format | Short | The format which should be used in upcoming packets | ||
Inventory Encoding Format | Short | The format which should be used in upcoming packets |
Forward Mention Player Pair
Packet ID | Bound To | Field Name | Field Type | Notes |
---|---|---|---|---|
0x02 | Backend through Proxy | Sender | UUID | |
Receiver | UUID |
Forward Player Equipment
Packet ID | Bound To | Field Name | Field Type | Notes |
---|---|---|---|---|
0x03 | Backend through Proxy | Player | UUID | |
Size | Byte | The size of the following array | ||
Array of ItemStack | ItemStack | The player's equipment. 0: Helmet, 1: Chestplate, 2: Leggings, 3: Boots, 4: Mainhand, 5: Offhand (If offhand is present) |
Forward Player Inventory
Packet ID | Bound To | Field Name | Field Type | Notes |
---|---|---|---|---|
0x04 | Backend through Proxy | Player | UUID | |
Inventory Type | Byte | 0: Player Inventory, 1: Player Enderchest | ||
Inventory | Inventory |
Forward Player Placeholders
Packet ID | Bound To | Field Name | Field Type | Notes | |
---|---|---|---|---|---|
0x05 | Backend through Proxy | Player | UUID | ||
Size | Int | The size of the following array | |||
Array | Placeholder | String | |||
Replace Text | String |
Add Message Player Pair
Packet ID | Bound To | Field Name | Field Type | Notes | |
---|---|---|---|---|---|
0x06 | Backend through Proxy | Message | String | ||
Player | UUID |
Add Player Command Match
Packet ID | Bound To | Field Name | Field Type | Notes |
---|---|---|---|---|
0x07 | Backend through Proxy | Player | UUID | |
Placeholder | String | |||
UUID Match | String |
Respond Processed Message
Packet ID | Bound To | Field Name | Field Type | Notes |
---|---|---|---|---|
0x08 | Backend through Proxy | Player | UUID | |
Chat Component | String | The chat component as json |
Reload Bungeecord InteractiveChat Config
Packet ID | Bound To | Field Name | Field Type | Notes |
---|---|---|---|---|
0x09 | Backend through Proxy | (No Fields) |
WIP