-
-
Notifications
You must be signed in to change notification settings - Fork 45
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