From 70c3eabdb1459419cb7e7187cd0a741cdefaa66a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 18 Oct 2025 16:15:22 -0400 Subject: [PATCH] Minor optimizations for `MinecraftCompressorAndLengthEncoder` and friends No need to bounce around changing the writer index, we can just set the value directly. Also pull out the handshake checks into a separate function, to improve inlining. --- .../proxy/protocol/ProtocolUtils.java | 13 ++-- .../MinecraftCompressorAndLengthEncoder.java | 9 +-- .../netty/MinecraftVarintFrameDecoder.java | 69 ++++++++++--------- .../proxy/protocol/ProtocolUtilsTest.java | 3 +- 4 files changed, 48 insertions(+), 46 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 0be9065a..6e8ba6ac 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -150,7 +150,7 @@ public enum ProtocolUtils { BinaryTagTypes.COMPOUND, BinaryTagTypes.INT_ARRAY, BinaryTagTypes.LONG_ARRAY}; private static final QuietDecoderException BAD_VARINT_CACHED = new QuietDecoderException("Bad VarInt decoded"); - private static final int[] VAR_INT_LENGTHS = new int[65]; + private static final int[] VAR_INT_LENGTHS = new int[33]; static { for (int i = 0; i <= 32; ++i) { @@ -250,16 +250,15 @@ public enum ProtocolUtils { } /** - * Writes the specified {@code value} as a 21-bit Minecraft VarInt to the specified {@code buf}. + * Directly encodes a 21-bit Minecraft VarInt, ready to be written with {@link ByteBuf#writeMedium(int)}. * The upper 11 bits will be discarded. * - * @param buf the buffer to read from - * @param value the integer to write + * @param value the value to encode + * @return the encoded value */ - public static void write21BitVarInt(ByteBuf buf, int value) { + public static int encode21BitVarInt(int value) { // See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/ - int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14); - buf.writeMedium(w); + return (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14); } public static String readString(ByteBuf buf) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java index 90952a72..990d1732 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java @@ -46,7 +46,7 @@ public class MinecraftCompressorAndLengthEncoder extends MessageToByteEncoder 0) { if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) { - StateRegistry.PacketRegistry.ProtocolRegistry registry = - state.getProtocolRegistry(direction, ProtocolVersion.MINIMUM_VERSION); - - final int index = in.readerIndex(); - final int packetId = readRawVarInt21(in); - // Index hasn't changed, we've read nothing - if (index == in.readerIndex()) { - in.resetReaderIndex(); + if (validateServerboundHandshakePacket(in, length)) { return; } - final int payloadLength = length - ProtocolUtils.varIntBytes(packetId); - - MinecraftPacket packet = registry.createPacket(packetId); - - // We handle every packet in this phase, if you said something we don't know, something is really wrong - if (packet == null) { - throw UNKNOWN_PACKET; - } - - // We 'technically' have the incoming bytes of a payload here, and so, these can actually parse - // the packet if needed, so, we'll take advantage of the existing methods - int expectedMinLen = packet.expectedMinLength(in, direction, registry.version); - int expectedMaxLen = packet.expectedMaxLength(in, direction, registry.version); - if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) { - throw handleOverflow(packet, expectedMaxLen, in.readableBytes()); - } - if (payloadLength < expectedMinLen) { - throw handleUnderflow(packet, expectedMaxLen, in.readableBytes()); - } - - - in.readerIndex(index); } } @@ -139,6 +109,41 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { } } + private boolean validateServerboundHandshakePacket(ByteBuf in, int length) throws Exception { + StateRegistry.PacketRegistry.ProtocolRegistry registry = + state.getProtocolRegistry(direction, ProtocolVersion.MINIMUM_VERSION); + + final int index = in.readerIndex(); + final int packetId = readRawVarInt21(in); + // Index hasn't changed, we've read nothing + if (index == in.readerIndex()) { + in.resetReaderIndex(); + return true; + } + final int payloadLength = length - ProtocolUtils.varIntBytes(packetId); + + MinecraftPacket packet = registry.createPacket(packetId); + + // We handle every packet in this phase, if you said something we don't know, something is really wrong + if (packet == null) { + throw UNKNOWN_PACKET; + } + + // We 'technically' have the incoming bytes of a payload here, and so, these can actually parse + // the packet if needed, so, we'll take advantage of the existing methods + int expectedMinLen = packet.expectedMinLength(in, direction, registry.version); + int expectedMaxLen = packet.expectedMaxLength(in, direction, registry.version); + if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) { + throw handleOverflow(packet, expectedMaxLen, in.readableBytes()); + } + if (payloadLength < expectedMinLen) { + throw handleUnderflow(packet, expectedMaxLen, in.readableBytes()); + } + + in.readerIndex(index); + return false; + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (MinecraftDecoder.DEBUG) { diff --git a/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java b/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java index 1ed59cd8..ccd9cb7a 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java @@ -17,6 +17,7 @@ package com.velocitypowered.proxy.protocol; +import static com.velocitypowered.proxy.protocol.ProtocolUtils.encode21BitVarInt; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -83,7 +84,7 @@ public class ProtocolUtilsTest { private void writeReadTest3Bytes(ByteBuf buf, int test) { buf.clear(); - ProtocolUtils.write21BitVarInt(buf, test); + buf.writeMedium(encode21BitVarInt(test)); assertEquals(test, ProtocolUtils.readVarInt(buf)); }