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.
This commit is contained in:
@@ -150,7 +150,7 @@ public enum ProtocolUtils {
|
|||||||
BinaryTagTypes.COMPOUND, BinaryTagTypes.INT_ARRAY, BinaryTagTypes.LONG_ARRAY};
|
BinaryTagTypes.COMPOUND, BinaryTagTypes.INT_ARRAY, BinaryTagTypes.LONG_ARRAY};
|
||||||
private static final QuietDecoderException BAD_VARINT_CACHED =
|
private static final QuietDecoderException BAD_VARINT_CACHED =
|
||||||
new QuietDecoderException("Bad VarInt decoded");
|
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 {
|
static {
|
||||||
for (int i = 0; i <= 32; ++i) {
|
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.
|
* The upper 11 bits will be discarded.
|
||||||
*
|
*
|
||||||
* @param buf the buffer to read from
|
* @param value the value to encode
|
||||||
* @param value the integer to write
|
* @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/
|
// 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);
|
return (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
|
||||||
buf.writeMedium(w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String readString(ByteBuf buf) {
|
public static String readString(ByteBuf buf) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class MinecraftCompressorAndLengthEncoder extends MessageToByteEncoder<By
|
|||||||
if (uncompressed < threshold) {
|
if (uncompressed < threshold) {
|
||||||
// Under the threshold, there is nothing to do.
|
// Under the threshold, there is nothing to do.
|
||||||
ProtocolUtils.writeVarInt(out, uncompressed + 1);
|
ProtocolUtils.writeVarInt(out, uncompressed + 1);
|
||||||
ProtocolUtils.writeVarInt(out, 0);
|
out.writeByte(0);
|
||||||
out.writeBytes(msg);
|
out.writeBytes(msg);
|
||||||
} else {
|
} else {
|
||||||
handleCompressed(ctx, msg, out);
|
handleCompressed(ctx, msg, out);
|
||||||
@@ -57,7 +57,7 @@ public class MinecraftCompressorAndLengthEncoder extends MessageToByteEncoder<By
|
|||||||
throws DataFormatException {
|
throws DataFormatException {
|
||||||
int uncompressed = msg.readableBytes();
|
int uncompressed = msg.readableBytes();
|
||||||
|
|
||||||
ProtocolUtils.write21BitVarInt(out, 0); // Dummy packet length
|
out.writeMedium(0); // Reserve the packet length
|
||||||
ProtocolUtils.writeVarInt(out, uncompressed);
|
ProtocolUtils.writeVarInt(out, uncompressed);
|
||||||
ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, msg);
|
ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, msg);
|
||||||
|
|
||||||
@@ -72,11 +72,8 @@ public class MinecraftCompressorAndLengthEncoder extends MessageToByteEncoder<By
|
|||||||
throw new DataFormatException("The server sent a very large (over 2MiB compressed) packet.");
|
throw new DataFormatException("The server sent a very large (over 2MiB compressed) packet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int writerIndex = out.writerIndex();
|
|
||||||
int packetLength = out.readableBytes() - 3;
|
int packetLength = out.readableBytes() - 3;
|
||||||
out.writerIndex(0);
|
out.setMedium(0, ProtocolUtils.encode21BitVarInt(packetLength)); // Rewrite packet length
|
||||||
ProtocolUtils.write21BitVarInt(out, packetLength); // Rewrite packet length
|
|
||||||
out.writerIndex(writerIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -83,9 +83,8 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
|||||||
|
|
||||||
// try to read the length of the packet
|
// try to read the length of the packet
|
||||||
in.markReaderIndex();
|
in.markReaderIndex();
|
||||||
int preIndex = in.readerIndex();
|
|
||||||
int length = readRawVarInt21(in);
|
int length = readRawVarInt21(in);
|
||||||
if (preIndex == in.readerIndex()) {
|
if (packetStart == in.readerIndex()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (length < 0) {
|
if (length < 0) {
|
||||||
@@ -94,38 +93,9 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder {
|
|||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) {
|
if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) {
|
||||||
StateRegistry.PacketRegistry.ProtocolRegistry registry =
|
if (validateServerboundHandshakePacket(in, length)) {
|
||||||
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;
|
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
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
if (MinecraftDecoder.DEBUG) {
|
if (MinecraftDecoder.DEBUG) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.protocol;
|
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.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ public class ProtocolUtilsTest {
|
|||||||
|
|
||||||
private void writeReadTest3Bytes(ByteBuf buf, int test) {
|
private void writeReadTest3Bytes(ByteBuf buf, int test) {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
ProtocolUtils.write21BitVarInt(buf, test);
|
buf.writeMedium(encode21BitVarInt(test));
|
||||||
assertEquals(test, ProtocolUtils.readVarInt(buf));
|
assertEquals(test, ProtocolUtils.readVarInt(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user