Skip to content

Plugin Messaging Protocol

LOOHP edited this page Dec 31, 2021 · 5 revisions

WORK IN PROGRESS

Definitions

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 Types

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);
    }
}

Packet

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;
}

Proxy to Backend

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

Backend to Backend through Proxy

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