From ff1c3c0077ecc5647f4c09aaa0941a3c567528ff Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Mon, 16 Dec 2024 12:25:25 +0100
Subject: [PATCH] More feature patches

---
 CONTRIBUTING.md                               |   3 +-
 ...ocity-compression-and-cipher-natives.patch | 309 ++++++++----------
 ...oalSelector-Goal.Flag-Set-operations.patch | 124 ++++---
 paper-server/build.gradle.kts                 |   5 +
 4 files changed, 206 insertions(+), 235 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9e48879b0e..38fb143438 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -169,13 +169,14 @@ move it under the line of the patch you wish to modify;
 
 1. Make your change while at HEAD;
 1. Make a fixup commit. `git commit -a --fixup <hashOfPatchToFix>`;
+   - If you want to modify a per-file patch, use `git commit -a --fixup file`
    - You can also use `--squash` instead of `--fixup` if you want the commit
    message to also be changed.
    - You can get the hash by looking at `git log` or `git blame`; your IDE can
   assist you too.
    - Alternatively, if you only know the name of the patch, you can do
   `git commit -a --fixup "Subject of Patch name"`.
-1. Rebase with autosquash: `git rebase -i --autosquash base`.
+1. Rebase with autosquash: `git rebase -i --autosquash mache/main`.
 This will automatically move your fixup commit to the right place, and you just
 need to "save" the changes.
 1. Type `./gradlew rebuildPatches` in the root directory;
diff --git a/feature-patches/1045-Use-Velocity-compression-and-cipher-natives.patch b/feature-patches/1045-Use-Velocity-compression-and-cipher-natives.patch
index b339b20f9b..8ec9093e28 100644
--- a/feature-patches/1045-Use-Velocity-compression-and-cipher-natives.patch
+++ b/feature-patches/1045-Use-Velocity-compression-and-cipher-natives.patch
@@ -3,30 +3,12 @@ From: Andrew Steinborn <git@steinborn.me>
 Date: Mon, 26 Jul 2021 02:15:17 -0400
 Subject: [PATCH] Use Velocity compression and cipher natives
 
-== AT ==
-private-f net.minecraft.network.CompressionDecoder inflater
 
-diff --git a/build.gradle.kts b/build.gradle.kts
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/build.gradle.kts
-+++ b/build.gradle.kts
-@@ -0,0 +0,0 @@ dependencies {
-     runtimeOnly("org.xerial:sqlite-jdbc:3.47.0.0")
-     runtimeOnly("com.mysql:mysql-connector-j:9.1.0")
-     runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
-+    // Paper start - Use Velocity cipher
-+    implementation("com.velocitypowered:velocity-native:3.3.0-SNAPSHOT") {
-+        isTransitive = false
-+    }
-+    // Paper end - Use Velocity cipher
- 
-     runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6")
-     runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
-diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/network/CipherDecoder.java
-+++ b/src/main/java/net/minecraft/network/CipherDecoder.java
-@@ -0,0 +0,0 @@ import java.util.List;
+diff --git a/net/minecraft/network/CipherDecoder.java b/net/minecraft/network/CipherDecoder.java
+index 429325ffb7db2b85ed271ddf3da64c6fdc593673..4a445cb0ab19c6eca94fdc2172e1b286d1947c63 100644
+--- a/net/minecraft/network/CipherDecoder.java
++++ b/net/minecraft/network/CipherDecoder.java
+@@ -7,14 +7,30 @@ import java.util.List;
  import javax.crypto.Cipher;
  
  public class CipherDecoder extends MessageToMessageDecoder<ByteBuf> {
@@ -39,13 +21,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.cipher = cipher;  // Paper - Use Velocity cipher
      }
  
-     protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
--        list.add(this.cipher.decipher(channelHandlerContext, byteBuf));
+     @Override
+     protected void decode(ChannelHandlerContext context, ByteBuf in, List<Object> out) throws Exception {
+-        out.add(this.cipher.decipher(context, in));
 +        // Paper start - Use Velocity cipher
-+        ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
++        ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.cipher, in);
 +        try {
-+            cipher.process(compatible);
-+            list.add(compatible);
++            this.cipher.process(compatible);
++            out.add(compatible);
 +        } catch (Exception e) {
 +            compatible.release(); // compatible will never be used if we throw an exception
 +            throw e;
@@ -56,19 +39,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    // Paper start - Use Velocity cipher
 +    @Override
 +    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
-+        cipher.close();
++        this.cipher.close();
 +    }
 +    // Paper end - Use Velocity cipher
  }
-diff --git a/src/main/java/net/minecraft/network/CipherEncoder.java b/src/main/java/net/minecraft/network/CipherEncoder.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/network/CipherEncoder.java
-+++ b/src/main/java/net/minecraft/network/CipherEncoder.java
-@@ -0,0 +0,0 @@ import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
+diff --git a/net/minecraft/network/CipherEncoder.java b/net/minecraft/network/CipherEncoder.java
+index 992b9c7aed57ce29cdd2b4f66737d39db214f0cf..e087a35bcaf326c37a3e58f4d06165a61747c5a9 100644
+--- a/net/minecraft/network/CipherEncoder.java
++++ b/net/minecraft/network/CipherEncoder.java
+@@ -5,15 +5,31 @@ import io.netty.channel.ChannelHandlerContext;
  import io.netty.handler.codec.MessageToByteEncoder;
  import javax.crypto.Cipher;
-+import java.util.List;
  
 -public class CipherEncoder extends MessageToByteEncoder<ByteBuf> {
 -    private final CipherBase cipher;
@@ -81,13 +62,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        this.cipher = cipher;  // Paper - Use Velocity cipher
      }
  
--    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception {
--        this.cipher.encipher(byteBuf, byteBuf2);
-+    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
-+        // Paper start - Use Velocity cipher
-+        ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
++    // Paper start - Use Velocity cipher
+     @Override
+-    protected void encode(ChannelHandlerContext context, ByteBuf message, ByteBuf out) throws Exception {
+-        this.cipher.encipher(message, out);
++    protected void encode(ChannelHandlerContext context, ByteBuf message, java.util.List<Object> list) throws Exception {
++        ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.cipher, message);
 +        try {
-+            cipher.process(compatible);
++            this.cipher.process(compatible);
 +            list.add(compatible);
 +        } catch (Exception e) {
 +            compatible.release(); // compatible will never be used if we throw an exception
@@ -99,57 +81,58 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    // Paper start - Use Velocity cipher
 +    @Override
 +    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
-+        cipher.close();
++        this.cipher.close();
 +    }
 +    // Paper end - Use Velocity cipher
  }
-diff --git a/src/main/java/net/minecraft/network/CompressionDecoder.java b/src/main/java/net/minecraft/network/CompressionDecoder.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/network/CompressionDecoder.java
-+++ b/src/main/java/net/minecraft/network/CompressionDecoder.java
-@@ -0,0 +0,0 @@ public class CompressionDecoder extends ByteToMessageDecoder {
+diff --git a/net/minecraft/network/CompressionDecoder.java b/net/minecraft/network/CompressionDecoder.java
+index fcf0e557fbcbd5f306625096d859578fe8511734..2c7f935fcecb24a4394fdde523219a5b5984a673 100644
+--- a/net/minecraft/network/CompressionDecoder.java
++++ b/net/minecraft/network/CompressionDecoder.java
+@@ -12,14 +12,22 @@ import java.util.zip.Inflater;
+ public class CompressionDecoder extends ByteToMessageDecoder {
      public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
      public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
-     private Inflater inflater;
 +    private com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
+     private Inflater inflater;
      private int threshold;
      private boolean validateDecompressed;
  
 +    // Paper start - Use Velocity cipher
 +    @io.papermc.paper.annotation.DoNotUse
-     public CompressionDecoder(int compressionThreshold, boolean rejectsBadPackets) {
-+        this(null, compressionThreshold, rejectsBadPackets);
+     public CompressionDecoder(int threshold, boolean validateDecompressed) {
++        this(null, threshold, validateDecompressed);
 +    }
-+    public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
-         this.threshold = compressionThreshold;
-         this.validateDecompressed = rejectsBadPackets;
++    public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, boolean validateDecompressed) {
+         this.threshold = threshold;
+         this.validateDecompressed = validateDecompressed;
 -        this.inflater = new Inflater();
 +        this.inflater = compressor == null ? new Inflater() : null;
 +        this.compressor = compressor;
 +        // Paper end - Use Velocity cipher
      }
  
-     protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
-@@ -0,0 +0,0 @@ public class CompressionDecoder extends ByteToMessageDecoder {
+     @Override
+@@ -39,14 +47,42 @@ public class CompressionDecoder extends ByteToMessageDecoder {
                      }
                  }
  
 +                if (inflater != null) { // Paper - Use Velocity cipher; fallback to vanilla inflater
-                 this.setupInflaterInput(byteBuf);
-                 ByteBuf byteBuf2 = this.inflate(channelHandlerContext, i);
+                 this.setupInflaterInput(in);
+                 ByteBuf byteBuf = this.inflate(context, i);
                  this.inflater.reset();
-                 list.add(byteBuf2);
+                 out.add(byteBuf);
 +                return; // Paper - Use Velocity cipher
 +                } // Paper - use velocity compression
 +
 +                // Paper start - Use Velocity cipher
 +                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);
++                ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.compressor, in);
++                ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(context.alloc(), this.compressor, claimedUncompressedSize);
 +                try {
 +                    this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
-+                    list.add(uncompressed);
-+                    byteBuf.clear();
++                    out.add(uncompressed);
++                    in.clear();
 +                } catch (Exception e) {
 +                    uncompressed.release();
 +                    throw e;
@@ -163,36 +146,36 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 +    // Paper start - Use Velocity cipher
 +    @Override
-+    public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
++    public void handlerRemoved0(ChannelHandlerContext ctx) {
 +        if (this.compressor != null) {
 +            this.compressor.close();
 +        }
 +    }
 +    // Paper end - Use Velocity cipher
 +
-     private void setupInflaterInput(ByteBuf buf) {
+     private void setupInflaterInput(ByteBuf buffer) {
          ByteBuffer byteBuffer;
-         if (buf.nioBufferCount() > 0) {
-@@ -0,0 +0,0 @@ public class CompressionDecoder extends ByteToMessageDecoder {
+         if (buffer.nioBufferCount() > 0) {
+@@ -81,7 +117,13 @@ public class CompressionDecoder extends ByteToMessageDecoder {
          }
      }
  
--    public void setThreshold(int compressionThreshold, boolean rejectsBadPackets) {
+-    public void setThreshold(int threshold, boolean validateDecompressed) {
 +    // Paper start - Use Velocity cipher
-+    public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
++    public void setThreshold(com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold, boolean validateDecompressed) {
 +        if (this.compressor == null && compressor != null) { // Only re-configure once. Re-reconfiguring would require closing the native compressor.
 +            this.compressor = compressor;
 +            this.inflater = null;
 +        }
-+    // Paper end - Use Velocity cipher
-         this.threshold = compressionThreshold;
-         this.validateDecompressed = rejectsBadPackets;
++        // Paper end - Use Velocity cipher
+         this.threshold = threshold;
+         this.validateDecompressed = validateDecompressed;
      }
-diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/network/CompressionEncoder.java
-+++ b/src/main/java/net/minecraft/network/CompressionEncoder.java
-@@ -0,0 +0,0 @@ import io.netty.handler.codec.MessageToByteEncoder;
+diff --git a/net/minecraft/network/CompressionEncoder.java b/net/minecraft/network/CompressionEncoder.java
+index bc674b08a41d5529fe06c6d3f077051cf4138f73..ea8a894158c44c2e7943dea43ecd8e1f0075b18f 100644
+--- a/net/minecraft/network/CompressionEncoder.java
++++ b/net/minecraft/network/CompressionEncoder.java
+@@ -6,17 +6,31 @@ import io.netty.handler.codec.MessageToByteEncoder;
  import java.util.zip.Deflater;
  
  public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
@@ -200,16 +183,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    @javax.annotation.Nullable private final byte[] encodeBuf; // Paper - Use Velocity cipher
 +    @javax.annotation.Nullable // Paper - Use Velocity cipher
      private final Deflater deflater;
-+    @javax.annotation.Nullable // Paper - Use Velocity cipher
-+    private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
++    @javax.annotation.Nullable private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
      private int threshold;
  
 +    // Paper start - Use Velocity cipher
-     public CompressionEncoder(int compressionThreshold) {
-+        this(null, compressionThreshold);
+     public CompressionEncoder(int threshold) {
++        this(null, threshold);
 +    }
-+    public CompressionEncoder(@javax.annotation.Nullable com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold) {
-         this.threshold = compressionThreshold;
++    public CompressionEncoder(@javax.annotation.Nullable com.velocitypowered.natives.compression.VelocityCompressor compressor, int threshold) {
+         this.threshold = threshold;
 -        this.deflater = new Deflater();
 +        if (compressor == null) {
 +            this.encodeBuf = new byte[8192];
@@ -222,40 +204,46 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        // Paper end - Use Velocity cipher
      }
  
--    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
-+    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper - Use Velocity cipher
-         int i = byteBuf.readableBytes();
+     @Override
+-    protected void encode(ChannelHandlerContext context, ByteBuf encodingByteBuf, ByteBuf byteBuf) {
++    protected void encode(ChannelHandlerContext context, ByteBuf encodingByteBuf, ByteBuf byteBuf) throws Exception { // Paper - Use Velocity cipher
+         int i = encodingByteBuf.readableBytes();
          if (i > 8388608) {
              throw new IllegalArgumentException("Packet too big (is " + i + ", should be less than 8388608)");
-@@ -0,0 +0,0 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
-                 VarInt.write(byteBuf2, 0);
-                 byteBuf2.writeBytes(byteBuf);
+@@ -25,6 +39,7 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
+                 VarInt.write(byteBuf, 0);
+                 byteBuf.writeBytes(encodingByteBuf);
              } else {
 +                if (this.deflater != null) { // Paper - Use Velocity cipher
-                 byte[] bs = new byte[i];
-                 byteBuf.readBytes(bs);
-                 VarInt.write(byteBuf2, bs.length);
-@@ -0,0 +0,0 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
+                 byte[] bytes = new byte[i];
+                 encodingByteBuf.readBytes(bytes);
+                 VarInt.write(byteBuf, bytes.length);
+@@ -37,6 +52,17 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
                  }
  
                  this.deflater.reset();
-+                // Paper start - Use Velocity cipher
++                    // Paper start - Use Velocity cipher
 +                    return;
 +                }
 +
-+                VarInt.write(byteBuf2, i);
-+                final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
++                VarInt.write(byteBuf, i);
++                final ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(context.alloc(), this.compressor, encodingByteBuf);
 +                try {
-+                    this.compressor.deflate(compatibleIn, byteBuf2);
++                    this.compressor.deflate(compatibleIn, byteBuf);
 +                } finally {
 +                    compatibleIn.release();
 +                }
              }
          }
      }
- 
+@@ -48,4 +74,31 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
+     public void setThreshold(int threshold) {
+         this.threshold = threshold;
+     }
++
++    // Paper start - Use Velocity cipher
 +    @Override
-+    protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception{
++    protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception {
 +        if (this.compressor != null) {
 +            // We allocate bytes to be compressed plus 1 byte. This covers two cases:
 +            //
@@ -273,44 +261,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    }
 +
 +    @Override
-+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
++    public void handlerRemoved(ChannelHandlerContext ctx) {
 +        if (this.compressor != null) {
 +            this.compressor.close();
-+            // Paper end - Use Velocity cipher
 +        }
 +    }
-+
-     public int getThreshold() {
-         return this.threshold;
-     }
-diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/network/Connection.java
-+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
-         return networkmanager;
++    // Paper end - Use Velocity cipher
+ }
+diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
+index c4bb28857ee11dccc9924666634488044c666fd1..8fe485c5bf79804bb4d1f774f95a92b14a576e80 100644
+--- a/net/minecraft/network/Connection.java
++++ b/net/minecraft/network/Connection.java
+@@ -770,11 +770,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+         return connection;
      }
  
--    public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
+-    public void setEncryptionKey(Cipher decryptingCipher, Cipher encryptingCipher) {
 -        this.encrypted = true;
--        this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
--        this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
+-        this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptingCipher));
+-        this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptingCipher));
 +    // Paper start - Use Velocity cipher
-+//    public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
-+//        this.encrypted = true;
-+//        this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
-+//        this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
-+//    }
-+
-+    public void setupEncryption(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException {
++    public void setEncryptionKey(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException {
 +        if (!this.encrypted) {
 +            try {
-+                com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
-+                com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
++                com.velocitypowered.natives.encryption.VelocityCipher decryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
++                com.velocitypowered.natives.encryption.VelocityCipher encryptionCipher = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
 +
 +                this.encrypted = true;
-+                this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryption));
-+                this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryption));
++                this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
++                this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
 +            } catch (java.security.GeneralSecurityException e) {
 +                throw new net.minecraft.util.CryptException(e);
 +            }
@@ -320,68 +299,56 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public boolean isEncrypted() {
          return this.encrypted;
-@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- 
-     public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
-         if (compressionThreshold >= 0) {
+@@ -813,16 +824,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+     // Paper end - add proper async disconnect
+     public void setupCompression(int threshold, boolean validateDecompressed) {
+         if (threshold >= 0) {
 +            com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper - Use Velocity cipher
-             ChannelHandler channelhandler = this.channel.pipeline().get("decompress");
- 
-             if (channelhandler instanceof CompressionDecoder) {
-                 CompressionDecoder packetdecompressor = (CompressionDecoder) channelhandler;
- 
--                packetdecompressor.setThreshold(compressionThreshold, rejectsBadPackets);
-+                packetdecompressor.setThreshold(compressor, compressionThreshold, rejectsBadPackets); // Paper - Use Velocity cipher
+             if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder compressionDecoder) {
+-                compressionDecoder.setThreshold(threshold, validateDecompressed);
++                compressionDecoder.setThreshold(compressor, threshold, validateDecompressed); // Paper - Use Velocity cipher
              } else {
--                this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressionThreshold, rejectsBadPackets));
-+                this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressor, compressionThreshold, rejectsBadPackets)); // Paper - Use Velocity cipher
+-                this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(threshold, validateDecompressed));
++                this.channel.pipeline().addAfter("splitter", "decompress", new CompressionDecoder(compressor, threshold, validateDecompressed)); // Paper - Use Velocity cipher
              }
  
-             channelhandler = this.channel.pipeline().get("compress");
-@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- 
-                 packetcompressor.setThreshold(compressionThreshold);
+             if (this.channel.pipeline().get("compress") instanceof CompressionEncoder compressionEncoder) {
+                 compressionEncoder.setThreshold(threshold);
              } else {
--                this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
-+                this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressor, compressionThreshold)); // Paper - Use Velocity cipher
+-                this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(threshold));
++                this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressor, threshold)); // Paper - Use Velocity cipher
              }
              this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
          } else {
-diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-@@ -0,0 +0,0 @@ public class ServerConnectionListener {
+diff --git a/net/minecraft/server/network/ServerConnectionListener.java b/net/minecraft/server/network/ServerConnectionListener.java
+index b68adf37af7172671163d4a8074d2bfa97724b4b..9d9f1b93a68bbc3e201408a3669bba7f73006218 100644
+--- a/net/minecraft/server/network/ServerConnectionListener.java
++++ b/net/minecraft/server/network/ServerConnectionListener.java
+@@ -108,6 +108,10 @@ public class ServerConnectionListener {
+                 LOGGER.warn("Using HAProxy, please ensure the server port is adequately firewalled.");
              }
              // Paper end - Warn people with console access that HAProxy is in use.
- 
 +            // Paper start - Use Velocity cipher
 +            ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.compress.getLoadedVariant() + " compression from Velocity.");
 +            ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.cipher.getLoadedVariant() + " cipher from Velocity.");
 +            // Paper end - Use Velocity cipher
-+
-             this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() {
-                 protected void initChannel(Channel channel) {
-                     try {
-diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
-+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
-@@ -0,0 +0,0 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
+ 
+             this.channels
+                 .add(
+diff --git a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+index d5d1cdc0759338ce1554b19689fca90d2573189e..507c6b2628cab56e00b64fe1b21f873e717eda2d 100644
+--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
++++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+@@ -276,11 +276,9 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
              }
  
-             SecretKey secretkey = packet.getSecretKey(privatekey);
--            Cipher cipher = Crypt.getCipher(2, secretkey);
--            Cipher cipher1 = Crypt.getCipher(1, secretkey);
-+            // Paper start - Use Velocity cipher
-+//            Cipher cipher = Crypt.getCipher(2, secretkey);
-+//            Cipher cipher1 = Crypt.getCipher(1, secretkey);
-+            // Paper end - Use Velocity cipher
- 
-             s = (new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretkey))).toString(16);
+             SecretKey secretKey = packet.getSecretKey(_private);
+-            Cipher cipher = Crypt.getCipher(2, secretKey);
+-            Cipher cipher1 = Crypt.getCipher(1, secretKey);
+             string = new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretKey)).toString(16);
              this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
 -            this.connection.setEncryptionKey(cipher, cipher1);
-+            this.connection.setupEncryption(secretkey); // Paper - Use Velocity cipher
-         } catch (CryptException cryptographyexception) {
-             throw new IllegalStateException("Protocol error", cryptographyexception);
++            this.connection.setEncryptionKey(secretKey); // Paper - Use Velocity cipher
+         } catch (CryptException var7) {
+             throw new IllegalStateException("Protocol error", var7);
          }
diff --git a/feature-patches/1047-Optimize-GoalSelector-Goal.Flag-Set-operations.patch b/feature-patches/1047-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
index 4200e29c19..a550caaf22 100644
--- a/feature-patches/1047-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
+++ b/feature-patches/1047-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
@@ -6,20 +6,19 @@ Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
 Optimise the stream.anyMatch statement to move to a bitset
 where we can replace the call with a single bitwise operation.
 
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
-@@ -0,0 +0,0 @@ import net.minecraft.world.entity.Entity;
+diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java
+index e99836c4073d8b25eddd3b4c784dbdb1f0c4f2b1..a2d788a656b2a5767c8a00bbcaca16efa2db8d24 100644
+--- a/net/minecraft/world/entity/ai/goal/Goal.java
++++ b/net/minecraft/world/entity/ai/goal/Goal.java
+@@ -7,7 +7,15 @@ import net.minecraft.world.entity.Entity;
  import net.minecraft.world.level.Level;
  
  public abstract class Goal {
 -    private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
-+    private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
 +    private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
 +
 +    // Paper start - remove streams from pathfindergoalselector; make sure types are not empty
-+    public Goal() {
++    protected Goal() {
 +        if (this.goalTypes.size() == 0) {
 +            this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
 +        }
@@ -28,23 +27,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
      public abstract boolean canUse();
  
-@@ -0,0 +0,0 @@ public abstract class Goal {
+@@ -33,8 +41,13 @@ public abstract class Goal {
      }
  
-     public void setFlags(EnumSet<Goal.Flag> controls) {
+     public void setFlags(EnumSet<Goal.Flag> flagSet) {
 -        this.flags.clear();
--        this.flags.addAll(controls);
+-        this.flags.addAll(flagSet);
 +        // Paper start - remove streams from pathfindergoalselector
 +        this.goalTypes.clear();
-+        this.goalTypes.addAllUnchecked(controls);
++        this.goalTypes.addAllUnchecked(flagSet);
 +        if (this.goalTypes.size() == 0) {
 +            this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
 +        }
-+        // Paper end - remove streams from pathfindergoalselector
++        // Paper end - remove streams from pathfindergoalselector;
      }
  
      @Override
-@@ -0,0 +0,0 @@ public abstract class Goal {
+@@ -42,18 +55,20 @@ public abstract class Goal {
          return this.getClass().getSimpleName();
      }
  
@@ -56,6 +55,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        // Paper end - remove streams from pathfindergoalselector
      }
  
+ 
      // Paper start - Mob Goal API
      public boolean hasFlag(final Goal.Flag flag) {
 -        return this.flags.contains(flag);
@@ -66,51 +66,52 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 -        this.flags.add(flag);
 +        this.goalTypes.addUnchecked(flag);
      }
-     // Paper end - Mob Goal API
  
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
-@@ -0,0 +0,0 @@ public class GoalSelector {
+     // Paper end - Mob Goal API
+diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java
+index 9338e63cc28413f5559bb0122ef5e04a84bd51d1..88e7e245824b670652878e03a2142a13e97508fe 100644
+--- a/net/minecraft/world/entity/ai/goal/GoalSelector.java
++++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java
+@@ -24,7 +24,9 @@ public class GoalSelector {
      };
      private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
      private final Set<WrappedGoal> availableGoals = new ObjectLinkedOpenHashSet<>();
 -    private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
 +    private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
 +    private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
-     private int curRate; // Paper - EAR 2
++
  
      public void addGoal(int priority, Goal goal) {
-@@ -0,0 +0,0 @@ public class GoalSelector {
-         this.availableGoals.removeIf(wrappedGoalx -> wrappedGoalx.getGoal() == goal);
+         this.availableGoals.add(new WrappedGoal(priority, goal));
+@@ -45,18 +47,18 @@ public class GoalSelector {
+         this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal);
      }
  
--    private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
--        for (Goal.Flag flag : goal.getFlags()) {
--            if (controls.contains(flag)) {
+-    private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> flag) {
+-        for (Goal.Flag flag1 : goal.getFlags()) {
+-            if (flag.contains(flag1)) {
 -                return true;
 -            }
 -        }
 -
 -        return false;
-+    // Paper start
-+    private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> controls) {
++    // Paper start - Perf: optimize goal types
++    private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> flags) {
 +        return goal.getFlags().hasCommonElements(controls);
      }
  
-     private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> goalsByControl) {
--        for (Goal.Flag flag : goal.getFlags()) {
+     private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> flag) {
+-        for (Goal.Flag flag1 : goal.getFlags()) {
 +        long flagIterator = goal.getFlags().getBackingSet();
 +        int wrappedGoalSize = goal.getFlags().size();
 +        for (int i = 0; i < wrappedGoalSize; ++i) {
-+            final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
++            final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
 +            flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator);
-+            // Paper end
-             if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) {
++            // Paper end - Perf: optimize goal types
+             if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) {
                  return false;
              }
-@@ -0,0 +0,0 @@ public class GoalSelector {
+@@ -70,7 +72,7 @@ public class GoalSelector {
          profilerFiller.push("goalCleanup");
  
          for (WrappedGoal wrappedGoal : this.availableGoals) {
@@ -119,53 +120,50 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                  wrappedGoal.stop();
              }
          }
-@@ -0,0 +0,0 @@ public class GoalSelector {
+@@ -80,11 +82,14 @@ public class GoalSelector {
          profilerFiller.push("goalUpdate");
  
-         for (WrappedGoal wrappedGoal2 : this.availableGoals) {
--            if (!wrappedGoal2.isRunning()
--                && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags)
--                && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags)
--                && wrappedGoal2.canUse()) {
--                for (Goal.Flag flag : wrappedGoal2.getFlags()) {
+         for (WrappedGoal wrappedGoalx : this.availableGoals) {
+-            if (!wrappedGoalx.isRunning()
+-                && !goalContainsAnyFlags(wrappedGoalx, this.disabledFlags)
+-                && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags)
+-                && wrappedGoalx.canUse()) {
+-                for (Goal.Flag flag : wrappedGoalx.getFlags()) {
 +            // Paper start
-+            if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
-+                long flagIterator = wrappedGoal2.getFlags().getBackingSet();
-+                int wrappedGoalSize = wrappedGoal2.getFlags().size();
++            if (!wrappedGoalx.isRunning() && !goalContainsAnyFlags(wrappedGoalx, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags) && wrappedGoalx.canUse()) {
++                long flagIterator = wrappedGoalx.getFlags().getBackingSet();
++                int wrappedGoalSize = wrappedGoalx.getFlags().size();
 +                for (int i = 0; i < wrappedGoalSize; ++i) {
 +                    final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
 +                    flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator);
 +                    // Paper end
-                     WrappedGoal wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
-                     wrappedGoal3.stop();
-                     this.lockedFlags.put(flag, wrappedGoal2);
-@@ -0,0 +0,0 @@ public class GoalSelector {
+                     WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
+                     wrappedGoal1.stop();
+                     this.lockedFlags.put(flag, wrappedGoalx);
+@@ -116,11 +121,11 @@ public class GoalSelector {
      }
  
-     public void disableControlFlag(Goal.Flag control) {
--        this.disabledFlags.add(control);
-+        this.goalTypes.addUnchecked(control); // Paper - remove streams from pathfindergoalselector
+     public void disableControlFlag(Goal.Flag flag) {
+-        this.disabledFlags.add(flag);
++        this.goalTypes.addUnchecked(flag); // Paper - remove streams from pathfindergoalselector
      }
  
-     public void enableControlFlag(Goal.Flag control) {
--        this.disabledFlags.remove(control);
-+        this.goalTypes.removeUnchecked(control); // Paper - remove streams from pathfindergoalselector
+     public void enableControlFlag(Goal.Flag flag) {
+-        this.disabledFlags.remove(flag);
++        this.goalTypes.removeUnchecked(flag); // Paper - remove streams from pathfindergoalselector
      }
  
-     public void setControlFlag(Goal.Flag control, boolean enabled) {
-diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
-@@ -0,0 +0,0 @@ public class WrappedGoal extends Goal {
+     public void setControlFlag(Goal.Flag flag, boolean enabled) {
+diff --git a/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/net/minecraft/world/entity/ai/goal/WrappedGoal.java
+index 4bdbd323b642ed3422948fe24780be8b503602dc..5aaad796d2e2f7b57b1fd911a1498cdf235c36be 100644
+--- a/net/minecraft/world/entity/ai/goal/WrappedGoal.java
++++ b/net/minecraft/world/entity/ai/goal/WrappedGoal.java
+@@ -69,7 +69,7 @@ public class WrappedGoal extends Goal {
      }
  
      @Override
 -    public EnumSet<Goal.Flag> getFlags() {
-+    // Paper start - remove streams from pathfindergoalselector
-+    public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
++    public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() { // Paper - remove streams from pathfindergoalselector
          return this.goal.getFlags();
-+        // Paper end - remove streams from pathfindergoalselector
      }
  
-     public boolean isRunning() {
diff --git a/paper-server/build.gradle.kts b/paper-server/build.gradle.kts
index 9a2fab0245..4844881912 100644
--- a/paper-server/build.gradle.kts
+++ b/paper-server/build.gradle.kts
@@ -149,6 +149,11 @@ dependencies {
     runtimeOnly("org.xerial:sqlite-jdbc:3.47.0.0")
     runtimeOnly("com.mysql:mysql-connector-j:9.1.0")
     runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
+    // Paper start - Use Velocity cipher
+    implementation("com.velocitypowered:velocity-native:3.3.0-SNAPSHOT") {
+        isTransitive = false
+    }
+    // Paper end - Use Velocity cipher
 
     runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6")
     runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")