diff --git a/src/main/java/io/minimum/minecraft/velocity/protocol/compression/JavaVelocityCompressor.java b/src/main/java/io/minimum/minecraft/velocity/protocol/compression/JavaVelocityCompressor.java new file mode 100644 index 000000000..d53fda8d6 --- /dev/null +++ b/src/main/java/io/minimum/minecraft/velocity/protocol/compression/JavaVelocityCompressor.java @@ -0,0 +1,60 @@ +package io.minimum.minecraft.velocity.protocol.compression; + +import com.google.common.base.Preconditions; +import io.netty.buffer.ByteBuf; + +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +public class JavaVelocityCompressor implements VelocityCompressor { + private final Deflater deflater; + private final Inflater inflater; + private final byte[] buf; + private boolean disposed = false; + + public JavaVelocityCompressor() { + this.deflater = new Deflater(); + this.inflater = new Inflater(); + this.buf = new byte[8192]; + } + + @Override + public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException { + ensureNotDisposed(); + + byte[] inData = new byte[source.readableBytes()]; + source.readBytes(inData); + inflater.setInput(inData); + while (!inflater.finished()) { + int read = inflater.inflate(buf); + destination.writeBytes(buf, 0, read); + } + inflater.reset(); + } + + @Override + public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException { + ensureNotDisposed(); + + byte[] inData = new byte[source.readableBytes()]; + source.readBytes(inData); + deflater.setInput(inData); + deflater.finish(); + while (!deflater.finished()) { + int bytes = deflater.deflate(buf); + destination.writeBytes(buf, 0, bytes); + } + deflater.reset(); + } + + @Override + public void dispose() { + ensureNotDisposed(); + disposed = true; + } + + private void ensureNotDisposed() { + Preconditions.checkState(!disposed, "Object already disposed"); + } +} diff --git a/src/main/java/io/minimum/minecraft/velocity/protocol/compression/VelocityCompressor.java b/src/main/java/io/minimum/minecraft/velocity/protocol/compression/VelocityCompressor.java new file mode 100644 index 000000000..d6aa52532 --- /dev/null +++ b/src/main/java/io/minimum/minecraft/velocity/protocol/compression/VelocityCompressor.java @@ -0,0 +1,13 @@ +package io.minimum.minecraft.velocity.protocol.compression; + +import io.netty.buffer.ByteBuf; + +import java.util.zip.DataFormatException; + +public interface VelocityCompressor { + void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException; + + void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException; + + void dispose(); +} diff --git a/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressDecoder.java b/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressDecoder.java index 08042f378..8552a22b1 100644 --- a/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressDecoder.java +++ b/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressDecoder.java @@ -2,20 +2,22 @@ package io.minimum.minecraft.velocity.protocol.netty; import com.google.common.base.Preconditions; import io.minimum.minecraft.velocity.protocol.ProtocolUtils; +import io.minimum.minecraft.velocity.protocol.compression.VelocityCompressor; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import java.util.List; -import java.util.zip.Inflater; public class MinecraftCompressDecoder extends MessageToMessageDecoder { private static final int MAXIMUM_INITIAL_BUFFER_SIZE = 65536; // 64KiB private final int threshold; + private final VelocityCompressor compressor; - public MinecraftCompressDecoder(int threshold) { + public MinecraftCompressDecoder(int threshold, VelocityCompressor compressor) { this.threshold = threshold; + this.compressor = compressor; } @Override @@ -24,22 +26,14 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder { if (uncompressedSize == 0) { // Strip the now-useless uncompressed size, this message is already uncompressed. out.add(msg.slice().retain()); + msg.skipBytes(msg.readableBytes()); return; } + Preconditions.checkState(uncompressedSize >= threshold, "Uncompressed size %s doesn't make sense with threshold %s", uncompressedSize, threshold); ByteBuf uncompressed = ctx.alloc().buffer(Math.min(uncompressedSize, MAXIMUM_INITIAL_BUFFER_SIZE)); try { - byte[] compressed = new byte[msg.readableBytes()]; - msg.readBytes(compressed); - Inflater inflater = new Inflater(); - inflater.setInput(compressed); - - byte[] decompressed = new byte[8192]; - while (!inflater.finished()) { - int inflatedBytes = inflater.inflate(decompressed); - uncompressed.writeBytes(decompressed, 0, inflatedBytes); - } - + compressor.inflate(msg, uncompressed); Preconditions.checkState(uncompressedSize == uncompressed.readableBytes(), "Mismatched compression sizes"); out.add(uncompressed); } catch (Exception e) { diff --git a/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressEncoder.java b/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressEncoder.java index ae34d3bc4..9696bc0be 100644 --- a/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressEncoder.java +++ b/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftCompressEncoder.java @@ -1,17 +1,18 @@ package io.minimum.minecraft.velocity.protocol.netty; import io.minimum.minecraft.velocity.protocol.ProtocolUtils; +import io.minimum.minecraft.velocity.protocol.compression.VelocityCompressor; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import java.util.zip.Deflater; - public class MinecraftCompressEncoder extends MessageToByteEncoder { private final int threshold; + private final VelocityCompressor compressor; - public MinecraftCompressEncoder(int threshold) { + public MinecraftCompressEncoder(int threshold, VelocityCompressor compressor) { this.threshold = threshold; + this.compressor = compressor; } @Override @@ -23,20 +24,10 @@ public class MinecraftCompressEncoder extends MessageToByteEncoder { return; } - Deflater deflater = new Deflater(); - byte[] buf = new byte[msg.readableBytes()]; - msg.readBytes(buf); - deflater.setInput(buf); - deflater.finish(); - ByteBuf compressedBuffer = ctx.alloc().buffer(); try { - byte[] deflated = new byte[8192]; - while (!deflater.finished()) { - int bytes = deflater.deflate(deflated); - compressedBuffer.writeBytes(deflated, 0, bytes); - } - ProtocolUtils.writeVarInt(out, buf.length); + compressor.deflate(msg, compressedBuffer); + ProtocolUtils.writeVarInt(out, msg.readableBytes()); out.writeBytes(compressedBuffer); } finally { compressedBuffer.release(); diff --git a/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftPipelineUtils.java b/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftPipelineUtils.java index efb05a29f..1f8bf83c5 100644 --- a/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftPipelineUtils.java +++ b/src/main/java/io/minimum/minecraft/velocity/protocol/netty/MinecraftPipelineUtils.java @@ -1,6 +1,7 @@ package io.minimum.minecraft.velocity.protocol.netty; import io.minimum.minecraft.velocity.protocol.ProtocolConstants; +import io.minimum.minecraft.velocity.protocol.compression.JavaVelocityCompressor; import io.netty.channel.Channel; public class MinecraftPipelineUtils { @@ -29,8 +30,9 @@ public class MinecraftPipelineUtils { return; } - MinecraftCompressEncoder encoder = new MinecraftCompressEncoder(threshold); - MinecraftCompressDecoder decoder = new MinecraftCompressDecoder(threshold); + JavaVelocityCompressor compressor = new JavaVelocityCompressor(); + MinecraftCompressEncoder encoder = new MinecraftCompressEncoder(threshold, compressor); + MinecraftCompressDecoder decoder = new MinecraftCompressDecoder(threshold, compressor); ch.pipeline().addBefore("minecraft-decoder", "compress-decoder", decoder); ch.pipeline().addBefore("minecraft-encoder", "compress-encoder", encoder); diff --git a/src/main/java/io/minimum/minecraft/velocity/proxy/MinecraftClientSessionHandler.java b/src/main/java/io/minimum/minecraft/velocity/proxy/MinecraftClientSessionHandler.java index 5ca14a851..bf278d69f 100644 --- a/src/main/java/io/minimum/minecraft/velocity/proxy/MinecraftClientSessionHandler.java +++ b/src/main/java/io/minimum/minecraft/velocity/proxy/MinecraftClientSessionHandler.java @@ -1,27 +1,13 @@ package io.minimum.minecraft.velocity.proxy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import io.minimum.minecraft.velocity.data.ServerPing; -import io.minimum.minecraft.velocity.protocol.MinecraftPacket; import io.minimum.minecraft.velocity.protocol.PacketWrapper; -import io.minimum.minecraft.velocity.protocol.StateRegistry; -import io.minimum.minecraft.velocity.protocol.netty.MinecraftDecoder; -import io.minimum.minecraft.velocity.protocol.netty.MinecraftEncoder; import io.minimum.minecraft.velocity.protocol.packets.*; -import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.SimpleChannelInboundHandler; -import net.kyori.text.Component; import net.kyori.text.TextComponent; -import net.kyori.text.serializer.GsonComponentSerializer; public class MinecraftClientSessionHandler extends ChannelInboundHandlerAdapter { - private static final Gson GSON = new GsonBuilder() - .registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer()) - .create(); - @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { InboundMinecraftConnection connection = ctx.channel().attr(InboundMinecraftConnection.CONNECTION).get();