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