--- a/net/minecraft/network/CompressionDecoder.java
+++ b/net/minecraft/network/CompressionDecoder.java
@@ -13,13 +13,20 @@
     public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
     public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
     private final Inflater inflater;
+    private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper
     private int threshold;
     private boolean validateDecompressed;
 
+    // Paper start
     public CompressionDecoder(int compressionThreshold, boolean rejectsBadPackets) {
+        this(null, compressionThreshold, rejectsBadPackets);
+    }
+    public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
         this.threshold = compressionThreshold;
         this.validateDecompressed = rejectsBadPackets;
-        this.inflater = new Inflater();
+        this.inflater = compressor == null ? new Inflater() : null;
+        this.compressor = compressor;
+        // Paper end
     }
 
     protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
@@ -38,14 +45,42 @@
                     }
                 }
 
+                if (inflater != null) { // Paper - use velocity compression - fallback to vanilla inflater
                 this.setupInflaterInput(byteBuf);
                 ByteBuf byteBuf2 = this.inflate(channelHandlerContext, i);
                 this.inflater.reset();
                 list.add(byteBuf2);
+                return; // Paper - use velocity compression
+                } // Paper - use velocity compression
+
+                // Paper start - use velocity compression
+                int claimedUncompressedSize = i; // OBFHELPER
+                ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
+                ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(channelHandlerContext.alloc(), this.compressor, claimedUncompressedSize);
+                try {
+                    this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
+                    list.add(uncompressed);
+                    byteBuf.clear();
+                } catch (Exception e) {
+                    uncompressed.release();
+                    throw e;
+                } finally {
+                    compatibleIn.release();
+                }
+                // Paper end - use velocity compression
             }
         }
     }
 
+    // Paper start
+    @Override
+    public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
+        if (this.compressor != null) {
+            this.compressor.close();
+        }
+    }
+    // Paper end
+
     private void setupInflaterInput(ByteBuf buf) {
         ByteBuffer byteBuffer;
         if (buf.nioBufferCount() > 0) {
