diff --git a/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBuffer.java b/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBuffer.java
new file mode 100644
index 0000000000..c01c7c0515
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBuffer.java
@@ -0,0 +1,604 @@
+package com.github.retrooper.packetevents.binary;
+
+import com.github.retrooper.packetevents.PacketEvents;
+import com.github.retrooper.packetevents.manager.server.ServerVersion;
+import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
+import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
+import com.github.retrooper.packetevents.protocol.nbt.NBT;
+import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
+import com.github.retrooper.packetevents.protocol.player.ClientVersion;
+import com.github.retrooper.packetevents.protocol.player.GameMode;
+import com.github.retrooper.packetevents.protocol.player.PublicProfileKey;
+import com.github.retrooper.packetevents.protocol.world.Dimension;
+import com.github.retrooper.packetevents.protocol.world.WorldBlockPosition;
+import com.github.retrooper.packetevents.resources.ResourceLocation;
+import com.github.retrooper.packetevents.util.Either;
+import com.github.retrooper.packetevents.util.KnownPack;
+import com.github.retrooper.packetevents.util.Vector3i;
+import com.github.retrooper.packetevents.util.crypto.SaltSignature;
+import com.github.retrooper.packetevents.util.crypto.SignatureData;
+import com.github.retrooper.packetevents.util.mappings.IRegistry;
+import com.github.retrooper.packetevents.wrapper.PacketWrapper;
+
+import java.security.PublicKey;
+import java.time.Instant;
+import java.util.*;
+
+/**
+ * BinaryBuffer is a utility class for reading and writing protocol data from the underlying buffer
+ * This class was created to replace heavy usage of {@link PacketWrapper} for reading and writing data.
+ * {@link BinaryBufferType} is the format used to read and write data from the buffer.
+ * It provides methods to read and write various data types, including primitive types, collections, maps, and custom types.
+ * Custom classes that are not defined in the {@link BinaryBufferTypes} class can be added by creating a new class that extends {@link BinaryBufferType}.
+ * This is the encouraged way to add new types to the buffer.
+ * Example usage:
+ *
+ * class Person {
+ * public static final BinaryBufferType TYPE = new Serializer();
+ *
+ * public static Person read({@link BinaryBuffer} buffer, {@link ServerVersion} serverVersion, {@link ClientVersion} clientVersion) {
+ * return buffer.read(TYPE, serverVersion, clientVersion);
+ * }
+ *
+ * private int id;
+ * private String name;
+ *
+ * public Person(int id, String name) {
+ * this.id = id;
+ * this.name = name;
+ * }
+ *
+ *
+ *
+ * class Serializer implements {@link BinaryBufferType} {
+ *
+ * {@literal @}Override
+ * public Person read({@link BinaryBuffer} buffer, {@link ServerVersion} serverVersion, {@link ClientVersion} clientVersion) {
+ * int id = buffer.read(BinaryBuffer.INT, serverVersion, clientVersion);
+ * String name = buffer.read(BinaryBuffer.STRING, serverVersion, clientVersion);
+ * return new Person(id, name);
+ * }
+ *
+ * {@literal @}Override
+ * public void write({@link BinaryBuffer} buffer, Person value, {@link ServerVersion} serverVersion, {@link ClientVersion} clientVersion) {
+ * buffer.write(BinaryBuffer.INT, value.getId(), serverVersion, clientVersion);
+ * buffer.write(BinaryBuffer.STRING, value.getName(), serverVersion, clientVersion);
+ * }
+ *
+ * }
+ *
+ *
+ *
+ * TODO's:
+ * - Add more types to the {@link BinaryBufferTypes} class.
+ * - Convert the types like BYTE_ARRAY, INT_ARRAY, etc to a more reusable method, same with collections
+ */
+public final class BinaryBuffer {
+
+ public static final BinaryBufferType BYTE_ARRAY = new BinaryBufferTypes.ByteArray();
+ public static BinaryBufferType BYTE_ARRAY(int maxSize) {
+ return new BinaryBufferTypes.ByteArray(maxSize);
+ }
+
+ public static final BinaryBufferType BYTE = new BinaryBufferTypes.Byte();
+ public static final BinaryBufferType UBYTE = new BinaryBufferTypes.UByte();
+ public static final BinaryBufferType BOOLEAN = new BinaryBufferTypes.Bool();
+
+ public static final BinaryBufferType FLOAT = new BinaryBufferTypes.Float();
+ public static final BinaryBufferType DOUBLE = new BinaryBufferTypes.Double();
+
+ public static final BinaryBufferType SHORT = new BinaryBufferTypes.Short();
+ public static final BinaryBufferType INT = new BinaryBufferTypes.Int();
+ public static final BinaryBufferType LONG = new BinaryBufferTypes.Long();
+ public static final BinaryBufferType MEDIUM = new BinaryBufferTypes.Medium();
+
+ public static final BinaryBufferType VAR_INT = new BinaryBufferTypes.VarInt();
+ public static final BinaryBufferType VAR_LONG = new BinaryBufferTypes.VarLong();
+
+ public static final BinaryBufferType UUID = new BinaryBufferTypes.Uuid();
+ public static final BinaryBufferType BLOCK_POSITION = new BinaryBufferTypes.BlockPos();
+ public static final BinaryBufferType WORLD_BLOCK_POSITION = new BinaryBufferTypes.WorldBlockPos();
+ public static final BinaryBufferType GAME_MODE = new BinaryBufferTypes.GameMode();
+ public static final BinaryBufferType IDENTIFIER = new BinaryBufferTypes.Identifier();
+ public static final BinaryBufferType SALT_SIGNATURE = new BinaryBufferTypes.Salt();
+ public static final BinaryBufferType PUBLIC_KEY = new BinaryBufferTypes.Publickey();
+ public static final BinaryBufferType TIMESTAMP = new BinaryBufferTypes.Timestamp();
+ public static final BinaryBufferType PUBLIC_PROFILE_KEY = new BinaryBufferTypes.ProfileKey();
+ public static final BinaryBufferType SIGNATURE_DATA = new BinaryBufferTypes.SignatureData();
+ public static final BinaryBufferType ROTATION = new BinaryBufferTypes.Rotation();
+ public static final BinaryBufferType KNOWN_PACK = new BinaryBufferTypes.KnownPack();
+
+ /**
+ * Use {@link BinaryBuffer#DIMENSION_TYPE} instead
+ */
+ @Deprecated()
+ public static final BinaryBufferType DIMENSION = new BinaryBufferTypes.Dimension();
+
+ public static final BinaryBufferType NBT_RAW = new BinaryBufferTypes.NbtRaw();
+ public static final BinaryBufferType NBT = new BinaryBufferTypes.Nbt();
+ public static final BinaryBufferType NBT_RAW_UNLIMITED = new BinaryBufferTypes.NbtRawUnlimited();
+ public static final BinaryBufferType NBT_UNLIMITED = new BinaryBufferTypes.NbtUnlimited();
+
+ public static final BinaryBufferType STRING = new BinaryBufferTypes.String();
+ public static BinaryBufferType STRING(int maxSize) {
+ return new BinaryBufferTypes.String(maxSize);
+ }
+
+ private Object buffer;
+
+ /**
+ * Creates a new BinaryBuffer instance with the specified buffer object. This object is specified by the platform's netty ByteBuf
+ * @param buffer the underlying buffer object, typically a ByteBuf or similar.
+ */
+ public BinaryBuffer(Object buffer) {
+ this.buffer = buffer;
+ }
+
+ /**
+ * Creates a new BinaryBuffer instance with the specified capacity and whether it should be IO or direct.
+ * @param capacity the initial capacity of the buffer
+ * @param io whether the buffer should be an IO buffer (true) or a direct buffer (false)
+ */
+ public BinaryBuffer(int capacity, boolean io) {
+ if (io) {
+ this.buffer = PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().buffer(capacity);
+ }
+ else {
+ this.buffer = PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().directBuffer(capacity);
+ }
+ }
+
+ /**
+ * Creates a new BinaryBuffer instance with the specified capacity, using an IO buffer by default.
+ * @param capacity the initial capacity of the buffer
+ */
+ public BinaryBuffer(int capacity) {
+ this(capacity, true);
+ }
+
+ /**
+ * Creates a new BinaryBuffer instance that wraps the provided byte array.
+ * @param wrappedData the byte array to wrap in the buffer
+ */
+ public BinaryBuffer(byte[] wrappedData) {
+ this.buffer = PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().wrappedBuffer(wrappedData);
+ }
+
+ /**
+ * Resets the reader index of the underlying buffer.
+ */
+ public void resetReaderIndex() {
+ ByteBufHelper.resetReaderIndex(buffer);
+ }
+
+ /**
+ * Resets the writer index of the underlying buffer.
+ */
+ public void resetWriterIndex() {
+ ByteBufHelper.resetWriterIndex(buffer);
+ }
+
+ /**
+ * Clears the underlying buffer, resetting both reader and writer indices.
+ */
+ public void reset() {
+ ByteBufHelper.clear(buffer);
+ }
+
+ /**
+ * Reads a raw byte array from the underlying buffer.
+ * @param length the length of the byte array to read
+ * @return the byte array read from the buffer, or an empty array if a read is unsuccessful.
+ */
+ public byte[] readBytes(int length) {
+ byte[] bytes = new byte[length];
+ ByteBufHelper.readBytes(buffer, bytes);
+ return bytes;
+ }
+
+ /**
+ * Writes a raw byte array to the underlying buffer.
+ * @param array the byte array to write to the buffer
+ */
+ public void writeBytes(byte[] array) {
+ ByteBufHelper.writeBytes(buffer, array);
+ }
+
+ /**
+ * Reads a {@link BinaryBufferType}
+ * @param type the type to read
+ * @param serverVersion the server version to read the type for, these are important in specific cases.
+ * @param clientVersion the client version to read the type for, these are important in specific cases.
+ * @return The value read from the buffer. Will always return an instance of the type specified's generic, or throw an exception if the read fails.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public T read(BinaryBufferType type, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return type.read(this, serverVersion, clientVersion);
+ }
+
+ /**
+ * Reads a {@link BinaryBufferType} while automatically determining the server version based on the current server manager.
+ * @param type the type to read
+ * @param clientVersion the client version to read the type for, these are important in specific cases.
+ * @return The value read from the buffer. Will always return an instance of the type specified's generic, or throw an exception if the read fails.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public T read(BinaryBufferType type, ClientVersion clientVersion) {
+ return type.read(this, clientVersion);
+ }
+
+ /**
+ * Reads a {@link BinaryBufferType} while automatically determining the server and client versions based on the current server manager.
+ * @param type the type to read
+ * @return The value read from the buffer. Will always return an instance of the type specified's generic, or throw an exception if the read fails.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public T read(BinaryBufferType type) {
+ return type.read(this);
+ }
+
+
+ /** Reads an {@link Either} from the underlying buffer, where the first value is read as a boolean to determine if it is a left or right value.
+ * If the boolean is true, the left value is read using the specified leftType, otherwise the right value is read using the specified rightType.
+ * @param leftType the type to read the left value from the buffer
+ * @param rightType the type to read the right value from the buffer
+ * @param serverVersion the server version to read the type for, these are important in specific cases.
+ * @param clientVersion the client version to read the type for, these are important in specific cases.
+ * @return an Either containing a left or right value based on the boolean read from the buffer.
+ * @param the type of the left value, this is the generic of the {@link BinaryBufferType} specified for left values.
+ * @param the type of the right value, this is the generic of the {@link BinaryBufferType} specified for right values.
+ */
+ public Either readEither(BinaryBufferType leftType, BinaryBufferType rightType, ServerVersion serverVersion, ClientVersion clientVersion) {
+ boolean isLeft = read(BOOLEAN, serverVersion, clientVersion);
+ if (isLeft) {
+ return Either.createLeft(read(leftType, serverVersion, clientVersion));
+ } else {
+ return Either.createRight(read(rightType, serverVersion, clientVersion));
+ }
+ }
+
+ /** Reads an {@link Either} from the underlying buffer, where the first value is read as a boolean to determine if it is a left or right value.
+ * If the boolean is true, the left value is read using the specified leftType, otherwise the right value is read using the specified rightType.
+ * while automatically determining the server version based on the current server manager.
+ * @param leftType the type to read the left value from the buffer
+ * @param rightType the type to read the right value from the buffer
+ * @param clientVersion the client version to read the type for, these are important in specific cases.
+ * @return an Either containing a left or right value based on the boolean read from the buffer.
+ * @param the type of the left value, this is the generic of the {@link BinaryBufferType} specified for left values.
+ * @param the type of the right value, this is the generic of the {@link BinaryBufferType} specified for right values.
+ */
+ public Either readEither(BinaryBufferType leftType, BinaryBufferType rightType, ClientVersion clientVersion) {
+ return readEither(leftType, rightType, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ /** Reads an {@link Either} from the underlying buffer, where the first value is read as a boolean to determine if it is a left or right value.
+ * If the boolean is true, the left value is read using the specified leftType, otherwise the right value is read using the specified rightType.
+ * while automatically determining the server and client versions based on the current server manager.
+ * @param leftType the type to read the left value from the buffer
+ * @param rightType the type to read the right value from the buffer
+ * @return an Either containing a left or right value based on the boolean read from the buffer.
+ * @param the type of the left value, this is the generic of the {@link BinaryBufferType} specified for left values.
+ * @param the type of the right value, this is the generic of the {@link BinaryBufferType} specified for right values.
+ */
+ public Either readEither(BinaryBufferType leftType, BinaryBufferType rightType) {
+ return readEither(leftType, rightType, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+ /**
+ * Writes a value to the underlying buffer using the specified {@link BinaryBufferType}.
+ * @param type the type to write
+ * @param value the value to write to the buffer
+ * @param serverVersion the server version to write the type for, these are important in specific cases.
+ * @param clientVersion the client version to write the type for, these are important in specific cases.
+ * @param the type to write, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public void write(BinaryBufferType type, T value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ type.write(this, value, serverVersion, clientVersion);
+ }
+
+ /**
+ * Writes a value to the underlying buffer using the specified {@link BinaryBufferType} while automatically determining the server version based on the current server manager.
+ * @param type the type to write
+ * @param value the value to write to the buffer
+ * @param clientVersion the client version to write the type for, these are important in specific cases.
+ * @param the type to write, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public void write(BinaryBufferType type, T value, ClientVersion clientVersion) {
+ type.write(this, value, clientVersion);
+ }
+
+ /**
+ * Writes a value to the underlying buffer using the specified {@link BinaryBufferType} while automatically determining the server and client versions based on the current server manager.
+ * @param type the type to write
+ * @param value the value to write to the buffer
+ * @param the type to write, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public void write(BinaryBufferType type, T value) {
+ type.write(this, value);
+ }
+
+ /** Writes an {@link Either} to the underlying buffer, where the first value is written as a boolean to determine if it is a left or right value.
+ * If the boolean is true, the left value is written using the specified leftType, otherwise the right value is written using the specified rightType.
+ * @param either the Either to write to the buffer
+ * @param leftType the type to write the left value to the buffer
+ * @param rightType the type to write the right value to the buffer
+ * @param serverVersion the server version to write the type for, these are important in specific cases.
+ * @param clientVersion the client version to write the type for, these are important in specific cases.
+ * @param the type of the left value, this is the generic of the {@link BinaryBufferType} specified for left values.
+ * @param the type of the right value, this is the generic of the {@link BinaryBufferType} specified for right values.
+ */
+ public void writeEither(Either either, BinaryBufferType leftType, BinaryBufferType rightType, ServerVersion serverVersion, ClientVersion clientVersion) {
+ if (either.isLeft()) {
+ write(BOOLEAN, true, serverVersion, clientVersion);
+ write(leftType, either.getLeft(), serverVersion, clientVersion);
+ } else {
+ write(BOOLEAN, false, serverVersion, clientVersion);
+ write(rightType, either.getRight(), serverVersion, clientVersion);
+ }
+ }
+
+ /** Writes an {@link Either} to the underlying buffer, where the first value is written as a boolean to determine if it is a left or right value.
+ * If the boolean is true, the left value is written using the specified leftType, otherwise the right value is written using the specified rightType.
+ * while automatically determining the server version based on the current server manager.
+ * @param either the Either to write to the buffer
+ * @param leftType the type to write the left value to the buffer
+ * @param rightType the type to write the right value to the buffer
+ * @param clientVersion the client version to write the type for, these are important in specific cases.
+ * @param the type of the left value, this is the generic of the {@link BinaryBufferType} specified for left values.
+ * @param the type of the right value, this is the generic of the {@link BinaryBufferType} specified for right values.
+ */
+ public void writeEither(Either either, BinaryBufferType leftType, BinaryBufferType rightType, ClientVersion clientVersion) {
+ writeEither(either, leftType, rightType, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ /** Writes an {@link Either} to the underlying buffer, where the first value is written as a boolean to determine if it is a left or right value.
+ * If the boolean is true, the left value is written using the specified leftType, otherwise the right value is written using the specified rightType.
+ * while automatically determining the server and client versions based on the current server manager.
+ * @param either the Either to write to the buffer
+ * @param leftType the type to write the left value to the buffer
+ * @param rightType the type to write the right value to the buffer
+ * @param the type of the left value, this is the generic of the {@link BinaryBufferType} specified for left values.
+ * @param the type of the right value, this is the generic of the {@link BinaryBufferType} specified for right values.
+ */
+ public void writeEither(Either either, BinaryBufferType leftType, BinaryBufferType rightType) {
+ writeEither(either, leftType, rightType, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+
+ /**
+ * Reads a collection using {@link BinaryBuffer#VAR_INT} for the size specification.
+ * @param maxSize the maximum size of the collection to read, if the size exceeds this value an {@link IllegalArgumentException} will be thrown.
+ * @param type the type to read from the buffer
+ * @param serverVersion the server version to read the type for, these are important in specific cases These get passed into the type.
+ * @param clientVersion the client version to read the type for, these are important in specific cases These get passed into the type.
+ * @return a collection of the specified type read from the buffer, or an empty collection if the read is unsuccessful.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public Collection readCollection(int maxSize, BinaryBufferType type, ServerVersion serverVersion, ClientVersion clientVersion) {
+ int size = read(VAR_INT, serverVersion, clientVersion);
+ if (size > maxSize) {
+ throw new IllegalArgumentException("Size of collection is too big: " + size);
+ }
+ List list = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ list.add(read(type, serverVersion, clientVersion));
+ }
+ return list;
+ }
+
+
+ /**
+ * Reads a collection using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server version based on the current server manager.
+ * @param maxSize the maximum size of the collection to read, if the size exceeds this value an {@link IllegalArgumentException} will be thrown.
+ * @param type the type to read from the buffer
+ * @param clientVersion the client version to read the type for, these are important in specific cases These get passed into the type.
+ * @return a collection of the specified type read from the buffer, or an empty collection if the read is unsuccessful.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public Collection readCollection(int maxSize, BinaryBufferType type, ClientVersion clientVersion) {
+ return readCollection(maxSize, type, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ /**
+ * Reads a collection using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server and client versions based on the current server manager.
+ * @param maxSize the maximum size of the collection to read, if the size exceeds this value an {@link IllegalArgumentException} will be thrown.
+ * @param type the type to read from the buffer
+ * @return a collection of the specified type read from the buffer, or an empty collection if the read is unsuccessful.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public Collection readCollection(int maxSize, BinaryBufferType type) {
+ return readCollection(maxSize, type, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+ /**
+ * Reads a collection using {@link BinaryBuffer#VAR_INT} for the size specification with a maximum size of {@link Short#MAX_VALUE}.
+ * @param type the type to read from the buffer
+ * @param serverVersion the server version to read the type for, these are important in specific cases These get passed into the type.
+ * @param clientVersion the client version to read the type for, these are important in specific cases These get passed into the type.
+ * @return a collection of the specified type read from the buffer, or an empty collection if the read is unsuccessful.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public Collection readCollection(BinaryBufferType type, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return readCollection(Short.MAX_VALUE, type, serverVersion, clientVersion);
+ }
+
+ /**
+ * Reads a collection using {@link BinaryBuffer#VAR_INT} for the size specification with a maximum size of {@link Short#MAX_VALUE} while automatically determining the server version based on the current server manager.
+ * @param type the type to read from the buffer
+ * @param clientVersion the client version to read the type for, these are important in specific cases These get passed into the type.
+ * @return a collection of the specified type read from the buffer, or an empty collection if the read is unsuccessful.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public Collection readCollection(BinaryBufferType type, ClientVersion clientVersion) {
+ return readCollection(Short.MAX_VALUE, type, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ /**
+ * Reads a collection using {@link BinaryBuffer#VAR_INT} for the size specification with a maximum size of {@link Short#MAX_VALUE} while automatically determining the server and client versions based on the current server manager.
+ * @param type the type to read from the buffer
+ * @return a collection of the specified type read from the buffer, or an empty collection if the read is unsuccessful.
+ * @param the type to read, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public Collection readCollection(BinaryBufferType type) {
+ return readCollection(Short.MAX_VALUE, type, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+ /**
+ * Writes a collection using {@link BinaryBuffer#VAR_INT} for the size specification.
+ * @param collection the collection to write to the buffer
+ * @param type the type to write to the buffer
+ * @param serverVersion the server version to write the type for, these are important in specific cases These get passed into the type.
+ * @param clientVersion the client version to write the type for, these are important in specific cases These get passed into the type.
+ * @param the type to write, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public void writeCollection(Collection collection, BinaryBufferType type, ServerVersion serverVersion, ClientVersion clientVersion) {
+ write(VAR_INT, collection.size(), serverVersion, clientVersion);
+ for (T t : collection) {
+ write(type, t, serverVersion, clientVersion);
+ }
+ }
+
+ /**
+ * Writes a collection using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server version based on the current server manager.
+ * @param collection the collection to write to the buffer
+ * @param type the type to write to the buffer
+ * @param clientVersion the client version to write the type for, these are important in specific cases These get passed into the type.
+ * @param the type to write, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public void writeCollection(Collection collection, BinaryBufferType type, ClientVersion clientVersion) {
+ writeCollection(collection, type, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ /**
+ * Writes a collection using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server and client versions based on the current server manager.
+ * @param collection the collection to write to the buffer
+ * @param type the type to write to the buffer
+ * @param the type to write, this is the generic of the {@link BinaryBufferType} specified.
+ */
+ public void writeCollection(Collection collection, BinaryBufferType type) {
+ writeCollection(collection, type, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+
+ /**
+ * Writes a map to the underlying buffer using {@link BinaryBuffer#VAR_INT} for the size specification. Every iteration it reads the keyType, and then the valueType
+ * @param keyType the type to read the keys from the buffer
+ * @param valueType the type to read the values from the buffer
+ * @param serverVersion the server version to read the type for, these are important in specific cases These get passed into the type.
+ * @param clientVersion the client version to read the type for, these are important in specific cases These get passed into the type.
+ * @return a map of the specified types read from the buffer, or an empty map if the read is unsuccessful. {@link Map}
+ * @param the type to read the keys, this is the generic of the {@link BinaryBufferType} specified for keys.
+ * @param the type to read the values, this is the generic of the {@link BinaryBufferType} specified for values.
+ */
+ public Map readMap(BinaryBufferType keyType, BinaryBufferType valueType, ServerVersion serverVersion, ClientVersion clientVersion) {
+ int size = read(VAR_INT, serverVersion, clientVersion);
+ Map map = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ K key = read(keyType, serverVersion, clientVersion);
+ V value = read(valueType, serverVersion, clientVersion);
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ /**
+ * Reads a map from the underlying buffer using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server version based on the current server manager.
+ * @param keyType the type to read the keys from the buffer
+ * @param valueType the type to read the values from the buffer
+ * @param clientVersion the client version to read the type for, these are important in specific cases These get passed into the type.
+ * @return a map of the specified types read from the buffer, or an empty map if the read is unsuccessful. {@link Map}
+ * @param the type to read the keys, this is the generic of the {@link BinaryBufferType} specified for keys.
+ * @param the type to read the values, this is the generic of the {@link BinaryBufferType} specified for values.
+ */
+ public Map readMap(BinaryBufferType keyType, BinaryBufferType valueType, ClientVersion clientVersion) {
+ return readMap(keyType, valueType, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ /**
+ * Reads a map from the underlying buffer using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server and client versions based on the current server manager.
+ * @param keyType the type to read the keys from the buffer
+ * @param valueType the type to read the values from the buffer
+ * @return a map of the specified types read from the buffer, or an empty map if the read is unsuccessful. {@link Map}
+ * @param the type to read the keys, this is the generic of the {@link BinaryBufferType} specified for keys.
+ * @param the type to read the values, this is the generic of the {@link BinaryBufferType} specified for values.
+ */
+ public Map readMap(BinaryBufferType keyType, BinaryBufferType valueType) {
+ return readMap(keyType, valueType, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+ /**
+ * Writes a map to the underlying buffer using {@link BinaryBuffer#VAR_INT} for the size specification. Every iteration it writes the keyType, and then the valueType
+ * @param map the map to write to the buffer
+ * @param keyType the type to write the keys to the buffer
+ * @param valueType the type to write the values to the buffer
+ * @param serverVersion the server version to write the type for, these are important in specific cases These get passed into the type.
+ * @param clientVersion the client version to write the type for, these are important in specific cases These get passed into the type.
+ * @param the type to write the keys, this is the generic of the {@link BinaryBufferType} specified for keys.
+ * @param the type to write the values, this is the generic of the {@link BinaryBufferType} specified for values.
+ */
+ public void writeMap(Map map, BinaryBufferType keyType, BinaryBufferType valueType, ServerVersion serverVersion, ClientVersion clientVersion) {
+ write(VAR_INT, map.size(), serverVersion, clientVersion);
+ for (Map.Entry entry : map.entrySet()) {
+ write(keyType, entry.getKey(), serverVersion, clientVersion);
+ write(valueType, entry.getValue(), serverVersion, clientVersion);
+ }
+ }
+
+ /**
+ * Writes a map to the underlying buffer using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server version based on the current server manager.
+ * @param map the map to write to the buffer
+ * @param keyType the type to write the keys to the buffer
+ * @param valueType the type to write the values to the buffer
+ * @param clientVersion the client version to write the type for, these are important in specific cases These get passed into the type.
+ * @param the type to write the keys, this is the generic of the {@link BinaryBufferType} specified for keys.
+ * @param the type to write the values, this is the generic of the {@link BinaryBufferType} specified for values.
+ */
+ public void writeMap(Map map, BinaryBufferType keyType, BinaryBufferType valueType, ClientVersion clientVersion) {
+ writeMap(map, keyType, valueType, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ /**
+ * Writes a map to the underlying buffer using {@link BinaryBuffer#VAR_INT} for the size specification while automatically determining the server and client versions based on the current server manager.
+ * @param map the map to write to the buffer
+ * @param keyType the type to write the keys to the buffer
+ * @param valueType the type to write the values to the buffer
+ * @param the type to write the keys, this is the generic of the {@link BinaryBufferType} specified for keys.
+ * @param the type to write the values, this is the generic of the {@link BinaryBufferType} specified for values.
+ */
+ public void writeMap(Map map, BinaryBufferType keyType, BinaryBufferType valueType) {
+ writeMap(map, keyType, valueType, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+
+ /**
+ * Reads an enum value from the underlying buffer using its ordinal. The ordinal is read using {@link BinaryBuffer#VAR_INT}.
+ * @param enumClass the class of the enum to read
+ * @return the enum value read from the buffer, or throws an {@link IllegalArgumentException} if the ordinal is invalid.
+ * @param the type of the enum to read, this is the generic of the enum class specified.
+ */
+ public > T readEnum(Class enumClass) {
+ T[] constants = enumClass.getEnumConstants();
+ int ordinal = read(VAR_INT);
+ if (ordinal < 0 || ordinal >= constants.length) {
+ throw new IllegalArgumentException("Invalid ordinal for enum " + enumClass.getName() + ": " + ordinal);
+ }
+ return constants[ordinal];
+ }
+
+ /**
+ * Writes an enum value to the underlying buffer using its ordinal. The ordinal is written using {@link BinaryBuffer#VAR_INT}.
+ * @param value the enum value to write to the buffer
+ * @param the type of the enum to write, this is the generic of the enum class specified.
+ */
+ public > void writeEnum(T value) {
+ write(VAR_INT, value.ordinal());
+ }
+
+ /**
+ * Returns the underlying buffer object. These should rarely be used outside packetevents internals.
+ * @return the underlying buffer object, typically a ByteBuf or similar.
+ */
+ public Object getBuffer() {
+ return buffer;
+ }
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBufferType.java b/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBufferType.java
new file mode 100644
index 0000000000..4b11ccc0a0
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBufferType.java
@@ -0,0 +1,30 @@
+package com.github.retrooper.packetevents.binary;
+
+import com.github.retrooper.packetevents.PacketEvents;
+import com.github.retrooper.packetevents.manager.server.ServerVersion;
+import com.github.retrooper.packetevents.protocol.player.ClientVersion;
+
+public interface BinaryBufferType {
+
+ T read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion);
+
+ void write(BinaryBuffer buffer, T value, ServerVersion serverVersion, ClientVersion clientVersion);
+
+ default T read(BinaryBuffer buffer, ClientVersion clientVersion) {
+ return read(buffer, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ default void write(BinaryBuffer buffer, T value, ClientVersion clientVersion) {
+ write(buffer, value, PacketEvents.getAPI().getServerManager().getVersion(), clientVersion);
+ }
+
+ default T read(BinaryBuffer buffer) {
+ ServerVersion v = PacketEvents.getAPI().getServerManager().getVersion();
+ return read(buffer, v, v.toClientVersion());
+ }
+
+ default void write(BinaryBuffer buffer, T value) {
+ write(buffer, value, PacketEvents.getAPI().getServerManager().getVersion(), PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
+ }
+
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBufferTypes.java b/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBufferTypes.java
new file mode 100644
index 0000000000..503d430125
--- /dev/null
+++ b/api/src/main/java/com/github/retrooper/packetevents/binary/BinaryBufferTypes.java
@@ -0,0 +1,598 @@
+package com.github.retrooper.packetevents.binary;
+
+import com.github.retrooper.packetevents.manager.server.ServerVersion;
+import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
+import com.github.retrooper.packetevents.protocol.chat.filter.FilterMaskType;
+import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
+import com.github.retrooper.packetevents.protocol.nbt.NBT;
+import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
+import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
+import com.github.retrooper.packetevents.protocol.nbt.codec.NBTCodec;
+import com.github.retrooper.packetevents.protocol.player.ClientVersion;
+import com.github.retrooper.packetevents.protocol.player.GameMode;
+import com.github.retrooper.packetevents.protocol.player.PublicProfileKey;
+import com.github.retrooper.packetevents.protocol.world.Dimension;
+import com.github.retrooper.packetevents.protocol.world.WorldBlockPosition;
+import com.github.retrooper.packetevents.resources.ResourceLocation;
+import com.github.retrooper.packetevents.util.KnownPack;
+import com.github.retrooper.packetevents.util.MathUtil;
+import com.github.retrooper.packetevents.util.Vector3i;
+import com.github.retrooper.packetevents.util.crypto.MinecraftEncryptionUtil;
+import com.github.retrooper.packetevents.util.crypto.SaltSignature;
+import com.github.retrooper.packetevents.util.crypto.SignatureData;
+import com.github.retrooper.packetevents.util.mappings.IRegistry;
+import com.github.retrooper.packetevents.util.mappings.IRegistryHolder;
+
+import java.nio.charset.StandardCharsets;
+import java.security.PublicKey;
+import java.time.Instant;
+
+final class BinaryBufferTypes {
+
+ private BinaryBufferTypes() {}
+
+ static final class Float implements BinaryBufferType {
+ @Override
+ public java.lang.Float read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readFloat(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Float value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeFloat(buffer.getBuffer(), value);
+ }
+ }
+ static final class Double implements BinaryBufferType {
+ @Override
+ public java.lang.Double read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readDouble(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Double value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeDouble(buffer.getBuffer(), value);
+ }
+ }
+ static final class Bool implements BinaryBufferType {
+
+ @Override
+ public Boolean read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readBoolean(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, Boolean value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeBoolean(buffer.getBuffer(), value);
+ }
+ }
+ static final class Byte implements BinaryBufferType {
+
+ @Override
+ public java.lang.Byte read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readByte(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Byte value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeByte(buffer.getBuffer(), value);
+ }
+ }
+ static final class UByte implements BinaryBufferType {
+
+ @Override
+ public java.lang.Short read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readUnsignedByte(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Short value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ int s = value & 0xFF;
+ ByteBufHelper.writeByte(buffer.getBuffer(), s);
+ }
+ }
+ static final class Int implements BinaryBufferType {
+
+ @Override
+ public Integer read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readInt(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, Integer value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeInt(buffer.getBuffer(), value);
+ }
+ }
+ static final class Long implements BinaryBufferType {
+
+ @Override
+ public java.lang.Long read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readLong(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Long value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeLong(buffer.getBuffer(), value);
+ }
+ }
+ static final class Short implements BinaryBufferType {
+
+ @Override
+ public java.lang.Short read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readShort(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Short value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeShort(buffer.getBuffer(), value);
+ }
+ }
+ static final class VarInt implements BinaryBufferType {
+
+ @Override
+ public Integer read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ int value = 0;
+ int length = 0;
+ byte currentByte;
+ do {
+ currentByte = ByteBufHelper.readByte(buffer.getBuffer());
+ value |= (currentByte & 0x7F) << (length * 7);
+ length++;
+ if (length > 5) {
+ throw new RuntimeException("VarInt is too large. Must be smaller than 5 bytes.");
+ }
+ } while ((currentByte & 0x80) == 0x80);
+ return value;
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, Integer value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ /* Got this code/optimization from https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
+ * Copyright and permission notice above (above the class).
+ * Steinborn's post says that the code is under the MIT, last accessed 29.06.2024.
+ */
+ if ((value & (0xFFFFFFFF << 7)) == 0) {
+ ByteBufHelper.writeByte(buffer.getBuffer(), value);
+ } else if ((value & (0xFFFFFFFF << 14)) == 0) {
+ int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
+ ByteBufHelper.writeShort(buffer.getBuffer(), w);
+ } else if ((value & (0xFFFFFFFF << 21)) == 0) {
+ int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
+ ByteBufHelper.writeMedium(buffer.getBuffer(), w);
+ } else if ((value & (0xFFFFFFFF << 28)) == 0) {
+ int w = (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
+ | ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21);
+ ByteBufHelper.writeInt(buffer.getBuffer(), w);
+ } else {
+ int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
+ | ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80);
+ ByteBufHelper.writeInt(buffer.getBuffer(), w);
+ ByteBufHelper.writeByte(buffer.getBuffer(), value >>> 28);
+ }
+ }
+ }
+ static final class Medium implements BinaryBufferType {
+
+ @Override
+ public Integer read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return ByteBufHelper.readMedium(buffer.getBuffer());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, Integer value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeMedium(buffer.getBuffer(), value);
+ }
+ }
+ static final class VarLong implements BinaryBufferType {
+
+ @Override
+ public java.lang.Long read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ long value = 0;
+ int size = 0;
+ int b;
+ while (((b = ByteBufHelper.readByte(buffer.getBuffer())) & 0x80) == 0x80) {
+ value |= (long) (b & 0x7F) << (size++ * 7);
+ }
+ return value | ((long) (b & 0x7F) << (size * 7)); }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Long value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ while ((value & ~0x7F) != 0) {
+ ByteBufHelper.writeByte(buffer.getBuffer(), (int) (value & 0x7F) | 0x80);
+ value >>>= 7;
+ }
+
+ ByteBufHelper.writeByte(buffer.getBuffer(), value.intValue());
+ }
+ }
+ static final class String implements BinaryBufferType {
+
+ static final int MAX_LENGTH = java.lang.Short.MAX_VALUE;
+
+ private int maxLength = MAX_LENGTH;
+
+ String(int length) {
+ if (length < 0 || length > MAX_LENGTH) {
+ throw new IllegalArgumentException("String length must be between 0 and " + MAX_LENGTH + ", but was " + length);
+ }
+ this.maxLength = length;
+ }
+
+ String() {}
+
+ @Override
+ public java.lang.String read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ int j = buffer.read(BinaryBuffer.VAR_INT);
+ // TODO: Don't throw an exception if the string is too long (but still cut it off and probably kick the player)
+ if (j > maxLength * 4) {
+ throw new RuntimeException("The received encoded string buffer length is longer than maximum allowed (" + j + " > " + maxLength * 4 + ")");
+ } else if (j < 0) {
+ throw new RuntimeException("The received encoded string buffer length is less than zero! Weird string!");
+ } else {
+ java.lang.String s = ByteBufHelper.toString(buffer, ByteBufHelper.readerIndex(buffer), j, StandardCharsets.UTF_8);
+ ByteBufHelper.readerIndex(buffer, ByteBufHelper.readerIndex(buffer) + j);
+ if (s.length() > maxLength) {
+ throw new RuntimeException("The received string length is longer than maximum allowed (" + j + " > " + maxLength + ")");
+ } else {
+ return s;
+ }
+ }
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.String value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.VAR_INT, value.length());
+ ByteBufHelper.writeBytes(buffer.getBuffer(), value.getBytes(StandardCharsets.UTF_8));
+ }
+ }
+ static final class Uuid implements BinaryBufferType {
+
+ @Override
+ public java.util.UUID read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ long mostSigBits = ByteBufHelper.readLong(buffer.getBuffer());
+ long leastSigBits = ByteBufHelper.readLong(buffer.getBuffer());
+ return new java.util.UUID(mostSigBits, leastSigBits);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.util.UUID value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ ByteBufHelper.writeLong(buffer.getBuffer(), value.getMostSignificantBits());
+ ByteBufHelper.writeLong(buffer.getBuffer(), value.getLeastSignificantBits());
+ }
+ }
+ static final class BlockPos implements BinaryBufferType {
+
+ @Override
+ public Vector3i read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ long l = ByteBufHelper.readLong(buffer.getBuffer());
+ return new Vector3i(l, serverVersion);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, Vector3i value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ long val = value.getSerializedPosition(serverVersion);
+ ByteBufHelper.writeLong(buffer.getBuffer(), val);
+ }
+ }
+ static final class Identifier implements BinaryBufferType {
+
+ @Override
+ public ResourceLocation read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return new ResourceLocation(buffer.read(BinaryBuffer.STRING));
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, ResourceLocation value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.STRING, value.toString());
+ }
+ }
+ static final class Nbt implements BinaryBufferType {
+
+ @Override
+ public NBTCompound read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return (NBTCompound) buffer.read(BinaryBuffer.NBT_RAW);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, NBTCompound value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.NBT_RAW, value, serverVersion, clientVersion);
+ }
+ }
+ static final class NbtRaw implements BinaryBufferType {
+
+ @Override
+ public NBT read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return NBTCodec.readNBTFromBuffer(buffer.getBuffer(), serverVersion);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, NBT value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ NBTCodec.writeNBTToBuffer(buffer.getBuffer(), serverVersion, value);
+ }
+ }
+ static final class NbtUnlimited implements BinaryBufferType {
+
+ @Override
+ public NBTCompound read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return (NBTCompound) buffer.read(BinaryBuffer.NBT_RAW_UNLIMITED);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, NBTCompound value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.NBT_RAW, value, serverVersion, clientVersion);
+ }
+ }
+ static final class NbtRawUnlimited implements BinaryBufferType {
+
+ @Override
+ public NBT read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return NBTCodec.readNBTFromBuffer(buffer.getBuffer(), serverVersion, NBTLimiter.noop());
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, NBT value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ NBTCodec.writeNBTToBuffer(buffer.getBuffer(), serverVersion, value);
+ }
+ }
+ static final class GameMode implements BinaryBufferType {
+
+ @Override
+ public com.github.retrooper.packetevents.protocol.player.GameMode read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ byte b = ByteBufHelper.readByte(buffer.getBuffer());
+ return com.github.retrooper.packetevents.protocol.player.GameMode.getById(b);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, com.github.retrooper.packetevents.protocol.player.GameMode value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ int id = value == null ? -1 : value.getId();
+ ByteBufHelper.writeByte(buffer.getBuffer(), id);
+ }
+ }
+ static final class Dimension implements BinaryBufferType {
+
+ @Override
+ public com.github.retrooper.packetevents.protocol.world.Dimension read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
+ return new com.github.retrooper.packetevents.protocol.world.Dimension(
+ buffer.read(BinaryBuffer.VAR_INT));
+ }
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_19)
+ || serverVersion.isOlderThan(ServerVersion.V_1_16_2)) {
+ com.github.retrooper.packetevents.protocol.world.Dimension dimension = new com.github.retrooper.packetevents.protocol.world.Dimension(new NBTCompound());
+ dimension.setDimensionName(buffer.read(BinaryBuffer.IDENTIFIER).toString());
+ return dimension;
+ } else {
+ NBTCompound attrib = buffer.read(BinaryBuffer.NBT);
+ return new com.github.retrooper.packetevents.protocol.world.Dimension(attrib);
+ }
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, com.github.retrooper.packetevents.protocol.world.Dimension value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
+ buffer.write(BinaryBuffer.VAR_INT, value.getId());
+ return;
+ }
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_19)
+ || serverVersion.isOlderThan(ServerVersion.V_1_16_2)) {
+ buffer.write(BinaryBuffer.STRING, value.getDimensionName());
+ } else {
+ buffer.write(BinaryBuffer.NBT, value.getAttributes());
+ }
+ }
+ }
+ static final class ByteArray implements BinaryBufferType {
+ private static final int MAX_LENGTH = 32767; // 2^15 - 1
+ private final int maxLength;
+
+ public ByteArray(int maxLength) {
+ if (maxLength < 0 || maxLength > MAX_LENGTH) {
+ throw new IllegalArgumentException("ByteArray length must be between 0 and " + MAX_LENGTH + ", but was " + maxLength);
+ }
+ this.maxLength = maxLength;
+ }
+
+ public ByteArray() {
+ this(MAX_LENGTH);
+ }
+
+ @Override
+ public byte[] read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ int len = buffer.read(BinaryBuffer.VAR_INT, serverVersion, clientVersion);
+ if (len < 0 || len > maxLength) {
+ throw new RuntimeException("The received byte array length is invalid (" + len + " > " + maxLength + ")");
+ }
+ return buffer.readBytes(len);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, byte[] value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.VAR_INT, value.length, serverVersion, clientVersion);
+ buffer.writeBytes(value);
+ }
+ }
+ static final class Salt implements BinaryBufferType {
+
+ private static final BinaryBufferType BYTE_ARRAY = new ByteArray(256);
+
+ @Override
+ public SaltSignature read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ long salt = buffer.read(BinaryBuffer.LONG);
+ byte[] signature;
+ //1.19.3+
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_19_3)) {
+ //Read optional signature
+ if (buffer.read(BinaryBuffer.BOOLEAN)) {
+ signature = buffer.readBytes(256);
+ } else {
+ signature = new byte[0];
+ }
+ } else {
+ signature = buffer.read(BYTE_ARRAY);
+ }
+ return new SaltSignature(salt, signature); }
+
+ @Override
+ public void write(BinaryBuffer buffer, SaltSignature signature, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.LONG, signature.getSalt());
+ if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_19_3)) {
+ boolean present = signature.getSignature().length != 0;
+ buffer.write(BinaryBuffer.BOOLEAN, present);
+ if (present) {
+
+ buffer.writeBytes(signature.getSignature());
+ }
+
+ } else {
+ buffer.write(BYTE_ARRAY, signature.getSignature(), serverVersion, clientVersion);
+ }
+ }
+ }
+ static final class Publickey implements BinaryBufferType {
+
+ private static final BinaryBufferType BYTE_ARRAY = new ByteArray(512);
+
+ @Override
+ public PublicKey read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return MinecraftEncryptionUtil.publicKey(
+ buffer.read(BinaryBuffer.BYTE_ARRAY, serverVersion, clientVersion)
+ );
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, PublicKey value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BYTE_ARRAY, value.getEncoded());
+ }
+ }
+ static final class Timestamp implements BinaryBufferType {
+
+ @Override
+ public Instant read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return Instant.ofEpochMilli(buffer.read(BinaryBuffer.LONG, serverVersion, clientVersion));
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, Instant value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.LONG, value.toEpochMilli(), serverVersion, clientVersion);
+ }
+ }
+ static final class ProfileKey implements BinaryBufferType {
+
+ private static final BinaryBufferType BYTE_ARRAY = new ByteArray(4096);
+
+ @Override
+ public PublicProfileKey read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ Instant expiresAt = buffer.read(BinaryBuffer.TIMESTAMP, serverVersion, clientVersion);
+ PublicKey publicKey = buffer.read(BinaryBuffer.PUBLIC_KEY, serverVersion, clientVersion);
+ byte[] signature = buffer.read(BYTE_ARRAY, serverVersion, clientVersion);
+ return new PublicProfileKey(expiresAt, publicKey, signature);
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, PublicProfileKey value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.TIMESTAMP, value.getExpiresAt(), serverVersion, clientVersion);
+ buffer.write(BinaryBuffer.PUBLIC_KEY, value.getKey(), serverVersion, clientVersion);
+ buffer.write(BYTE_ARRAY, value.getKeySignature(), serverVersion, clientVersion);
+ }
+ }
+ static final class SignatureData implements BinaryBufferType {
+ private static final BinaryBufferType BYTE_ARRAY = new ByteArray(4096);
+
+ @Override
+ public com.github.retrooper.packetevents.util.crypto.SignatureData read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return new com.github.retrooper.packetevents.util.crypto.SignatureData(
+ buffer.read(BinaryBuffer.TIMESTAMP, serverVersion, clientVersion),
+ buffer.read(BinaryBuffer.PUBLIC_KEY, serverVersion, clientVersion),
+ buffer.read(BYTE_ARRAY, serverVersion, clientVersion)
+ );
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, com.github.retrooper.packetevents.util.crypto.SignatureData value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.TIMESTAMP, value.getTimestamp(), serverVersion, clientVersion);
+ buffer.write(BinaryBuffer.PUBLIC_KEY, value.getPublicKey(), serverVersion, clientVersion);
+ buffer.write(BYTE_ARRAY, value.getSignature(), serverVersion, clientVersion);
+ }
+ }
+ static final class MappedEntityDirect implements BinaryBufferType {
+
+ @Override
+ public T read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return null;
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, T value, ServerVersion serverVersion, ClientVersion clientVersion) {
+
+ }
+ }
+ static final class MappedEntity implements BinaryBufferType {
+
+ private final IRegistry registry;
+ private final IRegistryHolder registryHolder;
+
+ MappedEntity(IRegistry registry, IRegistryHolder registryHolder) {
+ this.registry = registry;
+ this.registryHolder = registryHolder;
+ }
+
+ @Override
+ public T read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ IRegistry replacedRegistry = registryHolder.getRegistryOr(
+ registry, serverVersion.toClientVersion()
+ );
+ return null; // TODO:
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, T value, ServerVersion serverVersion, ClientVersion clientVersion) {
+
+ }
+ }
+ static final class Rotation implements BinaryBufferType {
+
+ @Override
+ public java.lang.Float read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return (float) (buffer.read(BinaryBuffer.BYTE) * 360) / 256f;
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, java.lang.Float value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.BYTE, (byte) MathUtil.floor(value * 256f / 360f));
+ }
+ }
+ static final class KnownPack implements BinaryBufferType {
+
+ @Override
+ public com.github.retrooper.packetevents.util.KnownPack read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return new com.github.retrooper.packetevents.util.KnownPack(
+ buffer.read(BinaryBuffer.STRING, serverVersion, clientVersion),
+ buffer.read(BinaryBuffer.STRING, serverVersion, clientVersion),
+ buffer.read(BinaryBuffer.STRING, serverVersion, clientVersion)
+ );
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, com.github.retrooper.packetevents.util.KnownPack value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.STRING, value.getNamespace(), serverVersion, clientVersion);
+ buffer.write(BinaryBuffer.STRING, value.getId(), serverVersion, clientVersion);
+ buffer.write(BinaryBuffer.STRING, value.getVersion(), serverVersion, clientVersion);
+ }
+ }
+ static final class WorldBlockPos implements BinaryBufferType {
+
+ @Override
+ public WorldBlockPosition read(BinaryBuffer buffer, ServerVersion serverVersion, ClientVersion clientVersion) {
+ return new WorldBlockPosition(
+ buffer.read(BinaryBuffer.IDENTIFIER, serverVersion, clientVersion),
+ buffer.read(BinaryBuffer.BLOCK_POSITION, serverVersion, clientVersion)
+ );
+ }
+
+ @Override
+ public void write(BinaryBuffer buffer, WorldBlockPosition value, ServerVersion serverVersion, ClientVersion clientVersion) {
+ buffer.write(BinaryBuffer.IDENTIFIER, value.getWorld(), serverVersion, clientVersion);
+ buffer.write(BinaryBuffer.BLOCK_POSITION, value.getBlockPosition(), serverVersion, clientVersion);
+ }
+ }
+
+}
diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/ItemStackSerialization.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/ItemStackSerialization.java
index 1cff37d680..7b1cdbc666 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/ItemStackSerialization.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/ItemStackSerialization.java
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.item;
+import com.github.retrooper.packetevents.binary.BinaryBuffer;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.protocol.component.ComponentType;
@@ -27,6 +28,7 @@
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
+import com.github.retrooper.packetevents.util.mappings.IRegistryHolder;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.Nullable;
@@ -76,6 +78,35 @@ private static ItemStack readLegacy(PacketWrapper> wrapper) {
.wrapper(wrapper).build();
}
+ /**
+ * Migration of {@link ItemStackSerialization#readLegacy}
+ */
+ private static ItemStack readLegacyBinary(
+ BinaryBuffer buffer,
+ ServerVersion serverVersion,
+ IRegistryHolder registryHolder
+ ) {
+ boolean v1_13_2 = serverVersion.isNewerThanOrEquals(ServerVersion.V_1_13_2);
+ if (v1_13_2 && !buffer.read(BinaryBuffer.BOOLEAN)) {
+ return ItemStack.EMPTY;
+ }
+ int typeId = v1_13_2 ? buffer.read(BinaryBuffer.VAR_INT) : buffer.read(BinaryBuffer.SHORT);
+ if (typeId < 0 && !v1_13_2) { // 1.13.2 doesn't have this logic
+ return ItemStack.EMPTY;
+ }
+
+ ClientVersion version = serverVersion.toClientVersion();
+ ItemType type = ItemTypes.getRegistry().getByIdOrThrow(version, typeId);
+ int amount = buffer.read(BinaryBuffer.BYTE);
+ int legacyData = v1_13_2 ? -1 : buffer.read(BinaryBuffer.SHORT);
+ NBTCompound nbt = buffer.read(BinaryBuffer.NBT);
+ return ItemStack.builder().type(type).amount(amount)
+ .nbt(nbt).legacyData(legacyData)
+ .version(version)
+ .registryHolder(registryHolder)
+ .build();
+ }
+
/**
* Removed with 1.20.5
*/
diff --git a/api/src/main/java/com/github/retrooper/packetevents/settings/PacketEventsSettings.java b/api/src/main/java/com/github/retrooper/packetevents/settings/PacketEventsSettings.java
index f01ba5f5e0..255d13e6fc 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/settings/PacketEventsSettings.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/settings/PacketEventsSettings.java
@@ -40,6 +40,7 @@ public class PacketEventsSettings {
private boolean fullStackTraceEnabled = false;
private boolean kickOnPacketExceptionEnabled = true;
private boolean kickIfTerminated = true;
+ private boolean useBinaryBuffer = false;
private Function resourceProvider = path -> PacketEventsSettings.class
.getClassLoader()
.getResourceAsStream(path);
@@ -56,6 +57,18 @@ public PacketEventsSettings timeStampMode(TimeStampMode timeStampMode) {
return this;
}
+ /**
+ * NOTE TO TOFAA: Delete this after testing phase. TODO
+ * Should we use the new binary buffer system in {@link com.github.retrooper.packetevents.wrapper.PacketWrapper}
+ * @param useBinaryBuffer True if we should use the binary buffer system, false otherwise.
+ * @return Settings instance
+ */
+ @ApiStatus.Internal
+ public PacketEventsSettings useBinaryBuffer(boolean useBinaryBuffer) {
+ this.useBinaryBuffer = useBinaryBuffer;
+ return this;
+ }
+
/**
* Do we re-encode all packets by default?
*
@@ -193,6 +206,14 @@ public boolean shouldDownsampleColors() {
}
+ /**
+ * Should we use the new binary buffer system in {@link com.github.retrooper.packetevents.wrapper.PacketWrapper}?
+ * @return Getter for {@link #useBinaryBuffer}
+ */
+ public boolean shouldUseBinaryBuffer() {
+ return useBinaryBuffer;
+ }
+
/**
* Should we collect server data anonymously and report to bStats?
*
diff --git a/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java b/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java
index e83c0c36a4..b8b6819643 100644
--- a/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java
+++ b/api/src/main/java/com/github/retrooper/packetevents/wrapper/PacketWrapper.java
@@ -43,6 +43,8 @@
package com.github.retrooper.packetevents.wrapper;
import com.github.retrooper.packetevents.PacketEvents;
+import com.github.retrooper.packetevents.binary.BinaryBuffer;
+import com.github.retrooper.packetevents.binary.BinaryBufferType;
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
import com.github.retrooper.packetevents.event.PacketSendEvent;
import com.github.retrooper.packetevents.event.ProtocolPacketEvent;
@@ -130,6 +132,8 @@
public class PacketWrapper> {
@Nullable
public Object buffer;
+ @Nullable
+ public BinaryBuffer binaryBuffer;
@ApiStatus.Internal
public final Object bufferLock = new Object();
@@ -151,6 +155,7 @@ public PacketWrapper(ClientVersion clientVersion, ServerVersion serverVersion, i
this.clientVersion = clientVersion;
this.serverVersion = serverVersion;
this.buffer = null;
+ this.binaryBuffer = null;
this.packetTypeData = new PacketTypeData(null, packetID);
}
@@ -163,6 +168,7 @@ public PacketWrapper(PacketReceiveEvent event, boolean readData) {
this.serverVersion = event.getServerVersion();
this.user = event.getUser();
this.buffer = event.getByteBuf();
+ this.binaryBuffer = new BinaryBuffer(this.buffer);
this.packetTypeData = new PacketTypeData(event.getPacketType(), event.getPacketId());
if (readData) {
readEvent(event);
@@ -177,6 +183,7 @@ public PacketWrapper(PacketSendEvent event, boolean readData) {
this.clientVersion = event.getUser().getClientVersion();
this.serverVersion = event.getServerVersion();
this.buffer = event.getByteBuf();
+ this.binaryBuffer = new BinaryBuffer(this.buffer);
this.packetTypeData = new PacketTypeData(event.getPacketType(), event.getPacketId());
this.user = event.getUser();
if (readData) {
@@ -306,6 +313,7 @@ public Object getBuffer() {
public void setBuffer(Object buffer) {
this.buffer = buffer;
+ this.binaryBuffer = new BinaryBuffer(buffer);
}
/**
@@ -353,45 +361,90 @@ public void resetByteBuf() {
public void resetBuffer() {
ByteBufHelper.clear(buffer);
+ // TODO: Potentially use {@link BinaryBuffer#reset()} instead?
}
public byte readByte() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.BYTE);
+ }
return ByteBufHelper.readByte(buffer);
}
public void writeByte(int value) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.BYTE, (byte) value);
+ return;
+ }
ByteBufHelper.writeByte(buffer, value);
}
public short readUnsignedByte() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.UBYTE);
+ }
return ByteBufHelper.readUnsignedByte(buffer);
}
public boolean readBoolean() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.BOOLEAN);
+ }
return readByte() != 0;
}
public void writeBoolean(boolean value) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.BOOLEAN, value);
+ return;
+ }
writeByte(value ? 1 : 0);
}
public int readInt() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.INT);
+ }
return ByteBufHelper.readInt(buffer);
}
public void writeInt(int value) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.INT, value);
+ return;
+ }
ByteBufHelper.writeInt(buffer, value);
}
public int readMedium() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.MEDIUM);
+ }
return ByteBufHelper.readMedium(buffer);
}
public void writeMedium(int value) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.MEDIUM, value);
+ return;
+ }
ByteBufHelper.writeMedium(buffer, value);
}
public int readVarInt() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.VAR_INT);
+ }
int value = 0;
int length = 0;
byte currentByte;
@@ -407,6 +460,11 @@ public int readVarInt() {
}
public void writeVarInt(int value) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.VAR_INT, value);
+ return;
+ }
/* Got this code/optimization from https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
* Copyright and permission notice above (above the class).
* Steinborn's post says that the code is under the MIT, last accessed 29.06.2024.
@@ -431,10 +489,13 @@ public void writeVarInt(int value) {
}
}
+
+ // TODO: Map types need to be checked further if Reader instances are required for serialization
public Map readMap(Reader keyFunction, Reader valueFunction) {
return this.readMap(keyFunction, valueFunction, Integer.MAX_VALUE);
}
+ // TODO: Map types need to be checked further if Reader instances are required for serialization
public Map readMap(Reader keyFunction, Reader valueFunction, int maxSize) {
int size = this.readVarInt();
if (size > maxSize) {
@@ -450,6 +511,7 @@ public Map readMap(Reader keyFunction, Reader valueFunction,
return map;
}
+ // TODO: Map types need to be checked further if Writer instances are required for serialization
public void writeMap(Map map, Writer keyConsumer, Writer valueConsumer) {
writeVarInt(map.size());
for (Map.Entry entry : map.entrySet()) {
@@ -686,6 +748,10 @@ public void writeLong(long value) {
}
public long readVarLong() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.VAR_LONG);
+ }
long value = 0;
int size = 0;
int b;
@@ -696,6 +762,11 @@ public long readVarLong() {
}
public void writeVarLong(long l) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.VAR_LONG, l);
+ return;
+ }
while ((l & ~0x7F) != 0) {
this.writeByte((int) (l & 0x7F) | 0x80);
l >>>= 7;
@@ -705,18 +776,36 @@ public void writeVarLong(long l) {
}
public float readFloat() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.FLOAT);
+ }
return ByteBufHelper.readFloat(buffer);
}
public void writeFloat(float value) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.FLOAT, value);
+ return;
+ }
ByteBufHelper.writeFloat(buffer, value);
}
public double readDouble() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.DOUBLE);
+ }
return ByteBufHelper.readDouble(buffer);
}
public void writeDouble(double value) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.DOUBLE, value);
+ return;
+ }
ByteBufHelper.writeDouble(buffer, value);
}
@@ -827,31 +916,57 @@ public void writeLongArray(long[] array) {
}
public UUID readUUID() {
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.UUID);
+ }
long mostSigBits = readLong();
long leastSigBits = readLong();
return new UUID(mostSigBits, leastSigBits);
}
public void writeUUID(UUID uuid) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.UUID, uuid);
+ return;
+ }
writeLong(uuid.getMostSignificantBits());
writeLong(uuid.getLeastSignificantBits());
}
public Vector3i readBlockPosition() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.BLOCK_POSITION);
+ }
long val = readLong();
return new Vector3i(val, serverVersion);
}
public void writeBlockPosition(Vector3i pos) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.BLOCK_POSITION, pos);
+ return;
+ }
long val = pos.getSerializedPosition(serverVersion);
writeLong(val);
}
public GameMode readGameMode() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.GAME_MODE);
+ }
return GameMode.getById(readByte());
}
public void writeGameMode(@Nullable GameMode mode) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.GAME_MODE, mode);
+ return;
+ }
int id = mode == null ? -1 : mode.getId();
writeByte(id);
}
@@ -917,6 +1032,10 @@ public void writeEntityMetadata(EntityMetadataProvider metadata) {
@Deprecated
public Dimension readDimension() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.DIMENSION);
+ }
if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
return new Dimension(this.readVarInt());
}
@@ -932,6 +1051,11 @@ public Dimension readDimension() {
@Deprecated
public void writeDimension(Dimension dimension) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.DIMENSION, dimension);
+ return;
+ }
if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_20_5)) {
this.writeVarInt(dimension.getId());
return;
@@ -945,6 +1069,10 @@ public void writeDimension(Dimension dimension) {
}
public SaltSignature readSaltSignature() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.SALT_SIGNATURE);
+ }
long salt = readLong();
byte[] signature;
//1.19.3+
@@ -962,6 +1090,11 @@ public SaltSignature readSaltSignature() {
}
public void writeSaltSignature(SaltSignature signature) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.SALT_SIGNATURE, signature);
+ return;
+ }
writeLong(signature.getSalt());
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_19_3)) {
boolean present = signature.getSignature().length != 0;
@@ -976,14 +1109,27 @@ public void writeSaltSignature(SaltSignature signature) {
}
public PublicKey readPublicKey() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.PUBLIC_KEY);
+ }
return MinecraftEncryptionUtil.publicKey(readByteArray(512));
}
public void writePublicKey(PublicKey publicKey) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.PUBLIC_KEY, publicKey);
+ return;
+ }
writeByteArray(publicKey.getEncoded());
}
public PublicProfileKey readPublicProfileKey() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.PUBLIC_PROFILE_KEY);
+ }
Instant expiresAt = readTimestamp();
PublicKey key = readPublicKey();
byte[] keySignature = readByteArray(4096);
@@ -991,6 +1137,11 @@ public PublicProfileKey readPublicProfileKey() {
}
public void writePublicProfileKey(PublicProfileKey key) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.PUBLIC_PROFILE_KEY, key);
+ return;
+ }
writeTimestamp(key.getExpiresAt());
writePublicKey(key.getKey());
writeByteArray(key.getKeySignature());
@@ -1006,18 +1157,35 @@ public void writeRemoteChatSession(RemoteChatSession chatSession) {
}
public Instant readTimestamp() {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.TIMESTAMP);
+ }
return Instant.ofEpochMilli(readLong());
}
public void writeTimestamp(Instant timestamp) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.TIMESTAMP, timestamp);
+ return;
+ }
writeLong(timestamp.toEpochMilli());
}
public SignatureData readSignatureData() {
+ if (true) {
+ return binaryBuffer.read(BinaryBuffer.SIGNATURE_DATA);
+ }
return new SignatureData(readTimestamp(), readPublicKey(), readByteArray(4096));
}
public void writeSignatureData(SignatureData signatureData) {
+
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.SIGNATURE_DATA, signatureData);
+ return;
+ }
writeTimestamp(signatureData.getTimestamp());
writePublicKey(signatureData.getPublicKey());
writeByteArray(signatureData.getSignature());
@@ -1033,10 +1201,17 @@ public static IntFunction limitValue(IntFunction function, int limit)
}
public WorldBlockPosition readWorldBlockPosition() {
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.WORLD_BLOCK_POSITION);
+ }
return new WorldBlockPosition(readIdentifier(), readBlockPosition());
}
public void writeWorldBlockPosition(WorldBlockPosition pos) {
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.WORLD_BLOCK_POSITION, pos);
+ return;
+ }
writeIdentifier(pos.getWorld());
writeBlockPosition(pos.getBlockPosition());
}
@@ -1269,6 +1444,9 @@ public void writeNode(Node node) {
}
public KnownPack readKnownPack() {
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return binaryBuffer.read(BinaryBuffer.KNOWN_PACK);
+ }
String namespace = this.readString();
String id = this.readString();
String version = this.readString();
@@ -1276,6 +1454,10 @@ public KnownPack readKnownPack() {
}
public void writeKnownPack(KnownPack knownPack) {
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ binaryBuffer.write(BinaryBuffer.KNOWN_PACK, knownPack);
+ return;
+ }
this.writeString(knownPack.getNamespace());
this.writeString(knownPack.getId());
this.writeString(knownPack.getVersion());
@@ -1399,6 +1581,7 @@ public > Z readEnum(Class clazz) {
}
public > Z readEnum(Z[] values) {
+
return values[this.readVarInt()];
}
@@ -1495,10 +1678,17 @@ public void writeEither(Either either, Writer leftWriter, Writer
}
public void writeRotation(float rotation) {
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ this.binaryBuffer.write(BinaryBuffer.ROTATION, rotation);
+ return;
+ }
this.writeByte((byte) MathUtil.floor(rotation * 256f / 360f));
}
public float readRotation() {
+ if (PacketEvents.getAPI().getSettings().shouldUseBinaryBuffer()) {
+ return this.binaryBuffer.read(BinaryBuffer.ROTATION);
+ }
return (float) (this.readByte() * 360) / 256f;
}