From 6100e675af9d85358403cf84893a6bdaddd2d519 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Wed, 21 Aug 2024 16:44:13 +0100 Subject: [PATCH 01/38] Switch to alt throw path for reading login message IDs (Fixes #1370) There is little expectation that we should get a -ve number in here, but it is sadly seen in the wild. We will use an alternative exception path rather than returing a magic number to indicate an invalid value. once primative classes arrive, this should probably be written to use those for passing up the result. --- .../proxy/protocol/ProtocolUtils.java | 23 +++++++++++++++++++ .../packet/LoginPluginMessagePacket.java | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) 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 622186a99..053dcd65f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -156,6 +156,29 @@ public enum ProtocolUtils { return Integer.MIN_VALUE; } + /** + * Reads a Minecraft-style VarInt from the specified {@code buf}. The difference between this + * method and {@link #readVarInt(ByteBuf)} is that this function returns a sentinel value if the + * varint is invalid. + * + * @param buf the buffer to read from + * @return the decoded VarInt + * @throws DecoderException if the varint is invalid + */ + public static int readVarIntSafelyOrThrow(ByteBuf buf) { + int i = 0; + int maxRead = Math.min(5, buf.readableBytes()); + for (int j = 0; j < maxRead; j++) { + int k = buf.readByte(); + i |= (k & 0x7F) << j * 7; + if ((k & 0x80) != 128) { + return i; + } + } + throw MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad VarInt decoded") + : BAD_VARINT_CACHED; + } + /** * Returns the exact byte size of {@code value} if it were encoded as a VarInt. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java index 682785eb2..213492cbf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java @@ -63,7 +63,7 @@ public class LoginPluginMessagePacket extends DeferredByteBufHolder implements M @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.id = ProtocolUtils.readVarInt(buf); + this.id = ProtocolUtils.readVarIntSafelyOrThrow(buf); this.channel = ProtocolUtils.readString(buf); if (buf.isReadable()) { this.replace(buf.readRetainedSlice(buf.readableBytes())); From 67fb3b70a4e27d3babd3eb603139dd2dcf046755 Mon Sep 17 00:00:00 2001 From: KoutaChan <76502083+KoutaChan@users.noreply.github.com> Date: Sun, 1 Sep 2024 06:11:54 +0900 Subject: [PATCH 02/38] Increase readComponent string size limit to 262,143 for 1.13-1.20.2 (#1409) --- .../proxy/protocol/packet/chat/ComponentHolder.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java index d833f1300..a4d914634 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java @@ -54,6 +54,7 @@ import java.util.Map; public class ComponentHolder { private static final Logger logger = LogManager.getLogger(ComponentHolder.class); + public static final int DEFAULT_MAX_STRING_SIZE = 262143; private final ProtocolVersion version; private @MonotonicNonNull Component component; @@ -282,6 +283,8 @@ public class ComponentHolder { if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_3)) { return new ComponentHolder(version, ProtocolUtils.readBinaryTag(buf, version, BinaryTagIO.reader())); + } else if (version.noLessThan(ProtocolVersion.MINECRAFT_1_13)) { + return new ComponentHolder(version, ProtocolUtils.readString(buf, DEFAULT_MAX_STRING_SIZE)); } else { return new ComponentHolder(version, ProtocolUtils.readString(buf)); } From af629cf00048baea57738b58f4d0f2d276a48cf0 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 31 Aug 2024 20:30:47 -0400 Subject: [PATCH 03/38] Fall 2024 recompile of the natives (#1416) This time around, instead of the very ad hoc approach I have historically taken, this time we actually have a convenient script that takes care of basically all the hard work necessary, including cross-compilation. We compile natives for Ubuntu 20.04 LTS (OpenSSL 1.1.x native and libdeflate) and Ubuntu 22.04 LTS (OpenSSL 3.x.x native), for both x86_64 and aarch64. The macOS natives have also been recompiled on macOS Sonoma. --- .../build-support/build-all-linux-natives.sh | 32 ++++++++++++++++++ .../compile-linux-compress.sh} | 8 ++--- native/build-support/compile-linux-crypto.sh | 32 ++++++++++++++++++ native/{ => build-support}/compile-macos.sh | 2 +- native/build-support/ubuntu-focal.Dockerfile | 20 +++++++++++ native/build-support/ubuntu-jammy.Dockerfile | 19 +++++++++++ .../velocitypowered/natives/util/Natives.java | 12 +++---- .../linux_aarch64/velocity-cipher-ossl11x.so | Bin 8944 -> 9080 bytes .../linux_aarch64/velocity-cipher-ossl30x.so | Bin 70688 -> 13048 bytes .../linux_aarch64/velocity-compress.so | Bin 58336 -> 59040 bytes .../linux_x86_64/velocity-cipher-ossl11x.so | Bin 8712 -> 16864 bytes .../linux_x86_64/velocity-cipher-ossl30x.so | Bin 16224 -> 16232 bytes .../linux_x86_64/velocity-compress.so | Bin 61176 -> 78648 bytes .../macos_arm64/velocity-cipher.dylib | Bin 34034 -> 34056 bytes .../macos_arm64/velocity-compress.dylib | Bin 86468 -> 0 bytes 15 files changed, 113 insertions(+), 12 deletions(-) create mode 100755 native/build-support/build-all-linux-natives.sh rename native/{compile-linux.sh => build-support/compile-linux-compress.sh} (70%) create mode 100755 native/build-support/compile-linux-crypto.sh rename native/{ => build-support}/compile-macos.sh (89%) create mode 100644 native/build-support/ubuntu-focal.Dockerfile create mode 100644 native/build-support/ubuntu-jammy.Dockerfile delete mode 100755 native/src/main/resources/macos_arm64/velocity-compress.dylib diff --git a/native/build-support/build-all-linux-natives.sh b/native/build-support/build-all-linux-natives.sh new file mode 100755 index 000000000..32397f591 --- /dev/null +++ b/native/build-support/build-all-linux-natives.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -e + +# make sure we're in the correct directory - the top-level `native` directory +cd "$(dirname "$0")/.." || exit 1 + +ARCHS=(x86_64 aarch64) +BASE_DOCKERFILE_VARIANTS=(ubuntu-focal ubuntu-jammy) + +for variant in "${BASE_DOCKERFILE_VARIANTS[@]}"; do + docker_platforms="" + for arch in "${ARCHS[@]}"; do + docker_platforms="$docker_platforms --platform linux/${arch}" + done + + echo "Building base build image for $variant..." + docker build -t velocity-native-build:$variant $docker_platforms -f build-support/$variant.Dockerfile . +done + +for arch in "${ARCHS[@]}"; do + for variant in "${BASE_DOCKERFILE_VARIANTS[@]}"; do + echo "Building native crypto for $arch on $variant..." + + docker run --rm -v "$(pwd)":/app --platform linux/${arch} velocity-native-build:$variant /bin/bash -c "cd /app && ./build-support/compile-linux-crypto.sh" + done + + # Use only the oldest variant for the compression library + variant=${BASE_DOCKERFILE_VARIANTS[0]} + echo "Building native compression for $arch on $variant..." + docker run --rm -v "$(pwd)":/app --platform linux/${arch} velocity-native-build:$variant /bin/bash -c "cd /app && ./build-support/compile-linux-compress.sh" +done \ No newline at end of file diff --git a/native/compile-linux.sh b/native/build-support/compile-linux-compress.sh similarity index 70% rename from native/compile-linux.sh rename to native/build-support/compile-linux-compress.sh index bf2c336c6..b03ca37cf 100755 --- a/native/compile-linux.sh +++ b/native/build-support/compile-linux-compress.sh @@ -8,18 +8,16 @@ fi if [ ! -d libdeflate ]; then echo "Cloning libdeflate..." - git clone https://github.com/ebiggers/libdeflate.git + git clone --branch v1.21 --single-branch https://github.com/ebiggers/libdeflate.git fi echo "Compiling libdeflate..." cd libdeflate || exit -cmake -B build && cmake --build build --target libdeflate_static +rm -rf build && cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -B build && cmake --build build --target libdeflate_static cd .. CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -Wall -Werror -fomit-frame-pointer" ARCH=$(uname -m) mkdir -p src/main/resources/linux_$ARCH $CC $CFLAGS -Ilibdeflate src/main/c/jni_util.c src/main/c/jni_zlib_deflate.c src/main/c/jni_zlib_inflate.c \ - libdeflate/build/libdeflate.a -o src/main/resources/linux_$ARCH/velocity-compress.so -$CC $CFLAGS -shared src/main/c/jni_util.c src/main/c/jni_cipher_openssl.c \ - -o src/main/resources/linux_$ARCH/velocity-cipher.so -lcrypto \ No newline at end of file + libdeflate/build/libdeflate.a -o src/main/resources/linux_$ARCH/velocity-compress.so \ No newline at end of file diff --git a/native/build-support/compile-linux-crypto.sh b/native/build-support/compile-linux-crypto.sh new file mode 100755 index 000000000..221656647 --- /dev/null +++ b/native/build-support/compile-linux-crypto.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +openssl_version=$(openssl version | awk '{print $2}') + +# Extract the major and minor version numbers +major_version=$(echo "$openssl_version" | cut -d. -f1) +minor_version=$(echo "$openssl_version" | cut -d. -f2) + +# Determine the appropriate file name based on the version +if [ "$major_version" -eq 1 ] && [ "$minor_version" -eq 1 ]; then + filename="velocity-cipher-ossl11x.so" +elif [ "$major_version" -eq 3 ]; then + filename="velocity-cipher-ossl30x.so" +else + echo "Unsupported OpenSSL version: $openssl_version" + exit 1 +fi + +if [ ! "$CC" ]; then + export CC=gcc +fi + +output_file="velocity-cipher.so" +if [ -n "$OPENSSL_VERSION" ]; then + output_file="velocity-cipher-ossl${OPENSSL_VERSION}.so" +fi + +CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -Wall -Werror -fomit-frame-pointer" +ARCH=$(uname -m) +mkdir -p src/main/resources/linux_$ARCH +$CC $CFLAGS -shared src/main/c/jni_util.c src/main/c/jni_cipher_openssl.c \ + -o src/main/resources/linux_$ARCH/$filename -lcrypto \ No newline at end of file diff --git a/native/compile-macos.sh b/native/build-support/compile-macos.sh similarity index 89% rename from native/compile-macos.sh rename to native/build-support/compile-macos.sh index 732c1a164..7120cbe5f 100755 --- a/native/compile-macos.sh +++ b/native/build-support/compile-macos.sh @@ -6,7 +6,7 @@ fi if [ ! -d libdeflate ]; then echo "Cloning libdeflate..." - git clone https://github.com/ebiggers/libdeflate.git + git clone --branch v1.21 --single-branch https://github.com/ebiggers/libdeflate.git fi echo "Compiling libdeflate..." diff --git a/native/build-support/ubuntu-focal.Dockerfile b/native/build-support/ubuntu-focal.Dockerfile new file mode 100644 index 000000000..058a14f49 --- /dev/null +++ b/native/build-support/ubuntu-focal.Dockerfile @@ -0,0 +1,20 @@ +# Use the official Eclipse Temurin 17.0.12_7-jdk-focal image as the base image. +# We compile for Ubuntu Focal Fossa (20.04 LTS) as it is still supported until 2025, and the crypto +# native is specific to a given OpenSSL version. +FROM eclipse-temurin:17.0.12_7-jdk-focal + +# Install required dependencies +RUN apt-get update && apt-get install -y \ + libssl-dev \ + curl \ + git \ + unzip \ + build-essential \ + cmake \ + openssl \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean + +# Create a non-root user +RUN useradd -m -s /bin/bash -u 1000 -U user +USER user \ No newline at end of file diff --git a/native/build-support/ubuntu-jammy.Dockerfile b/native/build-support/ubuntu-jammy.Dockerfile new file mode 100644 index 000000000..f2224a107 --- /dev/null +++ b/native/build-support/ubuntu-jammy.Dockerfile @@ -0,0 +1,19 @@ +# Use the official Eclipse Temurin 17.0.12_7-jdk-jammy image as the base image. +# We compile for Ubuntu Jammy Jellyfish (22.04 LTS) as it supports OpenSSL 3.0. +FROM eclipse-temurin:17.0.12_7-jdk-jammy + +# Install required dependencies +RUN apt-get update && apt-get install -y \ + libssl-dev \ + curl \ + git \ + unzip \ + build-essential \ + cmake \ + openssl \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get clean + +# Create a non-root user +RUN useradd -m -s /bin/bash -u 1000 -U user +USER user \ No newline at end of file diff --git a/native/src/main/java/com/velocitypowered/natives/util/Natives.java b/native/src/main/java/com/velocitypowered/natives/util/Natives.java index 6dd204abe..1c1e26cea 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/Natives.java +++ b/native/src/main/java/com/velocitypowered/natives/util/Natives.java @@ -103,21 +103,21 @@ public class Natives { copyAndLoadNative("/linux_x86_64/velocity-cipher.so"), // Any local version "OpenSSL local (Linux x86_64)", NativeVelocityCipher.FACTORY), new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_X86_64, - copyAndLoadNative("/linux_x86_64/velocity-cipher-ossl30x.so"), // Debian "Bookworm" - "OpenSSL 3.0.x (Linux x86_64)", NativeVelocityCipher.FACTORY), + copyAndLoadNative("/linux_x86_64/velocity-cipher-ossl30x.so"), // Ubuntu 22.04 + "OpenSSL 3.x.x (Linux x86_64)", NativeVelocityCipher.FACTORY), new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_X86_64, - copyAndLoadNative("/linux_x86_64/velocity-cipher-ossl11x.so"), // Debian 9 + copyAndLoadNative("/linux_x86_64/velocity-cipher-ossl11x.so"), // Ubuntu 20.04 "OpenSSL 1.1.x (Linux x86_64)", NativeVelocityCipher.FACTORY), new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64, copyAndLoadNative("/linux_aarch64/velocity-cipher.so"), - "OpenSSL (Linux aarch64)", NativeVelocityCipher.FACTORY), // Any local version + "OpenSSL local (Linux aarch64)", NativeVelocityCipher.FACTORY), // Any local version new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64, copyAndLoadNative("/linux_aarch64/velocity-cipher-ossl30x.so"), - "OpenSSL (Linux aarch64)", NativeVelocityCipher.FACTORY), // Fedora 36 + "OpenSSL 3.x.x (Linux aarch64)", NativeVelocityCipher.FACTORY), // Ubuntu 22.04 new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64, copyAndLoadNative("/linux_aarch64/velocity-cipher-ossl11x.so"), - "OpenSSL 1.1.x (Linux aarch64)", NativeVelocityCipher.FACTORY), // Debian 11 + "OpenSSL 1.1.x (Linux aarch64)", NativeVelocityCipher.FACTORY), // Ubuntu 20.04 new NativeCodeLoader.Variant<>(NativeConstraints.MACOS_AARCH64, copyAndLoadNative("/macos_arm64/velocity-cipher.dylib"), diff --git a/native/src/main/resources/linux_aarch64/velocity-cipher-ossl11x.so b/native/src/main/resources/linux_aarch64/velocity-cipher-ossl11x.so index aa0a489e9668374cbdb89ac4d08855b6b02425b9..d79e34a2c4ba249b9566d1ce5c409617ca8a1be1 100755 GIT binary patch literal 9080 zcmb_ieQZ429%%; zU1z1-N!Or^8dK7x*70XELVaJQVDo6Z1p`yIyBF;!&;PVlOT|tZuh_ga$@yR^ha<*z z3;Iy-YT2<1e!lMSPoxqgtaU`&n1qb8`7VE92b-2b53m(q*~1i#*) zHn(-O20PW}&~7!Z?-h`S?ix2Vso~z9CQHZiL{1zgy@rlsvN`V6qOk=gD|{|&yn{v} ztS6JUJh%LD;iFDJR-@jl$ng7eWbA2N4ocvUQi0@e zziYxk#&=^-S~Izo*>HP4)i#{gQf@13xIG__4VP0O zOxD`)a(05W!G_BvKuDWxc%@C>XT#-IDWtReULT)TU~B`x;n{t^ICff5&)dvk8s(z^ zqs!)6($$x?jCtI(=W9L6dwikn#4@)pUF`-T6Z~T2SKIh< zKWF3bK>m!4zZ3aOHhwkoZkLEZg1N#}OWFr;@yT~%_~H>vt1W2_^`{lr1mO6{#;IQi@Yd=ZkGHd%EZuk$8{o`mKm~Kh;2z&Ru z{p`Ni#JtW_K>85I><;)h`&{rP^g9YyTGB{QiWqBr=~ptDBa?V}V5GA<;MzP>;Y-i% zJ0tGJ2RH`Gf2QlT@qZ}pi5UeZ+~7+GR`}9$=-UTd&ZFOxVY~+yQFckWWnxOXd14NI z%ygYO7H;yTrxiuy#Tbomrt9VLi|Fq#=5y_S*Z4J??@z<-@u5{vbKoy6>A>+o`U#Bp zVLhZ382Q~?trfG@2M=|-Aw;ooeHnGpJb1igbtB%fCvYr)x`)yJV!bc@IObLd?vLDv z`AZ>m?SB9Gr{Tc(TrK7Jh8X833atAU+I@;+@GOFp-DvZna@)F#%B|>sq;rv{Ih|e! z>GbDZV-)kUa{I)yICc&7h_4%MPJ58AT?Gck-tU6Y0Q#djxvZ>M_aW+E!fQ%dKG9|2 z>$a4@L&W2~52aW7((mG2_oFOlOjGq^b=?hPI}j(or1u-t%hBYPD2<=Q5E zxhdZAxvD#_PvIP2R2EHKO2XLXT6pPRl+Tq0uG9VC_mJPgu-MK#@r3DV+~nyQHuWS> zdjR*?`j{5)t8a_N^gb=-H~LZodfd!z!$MDs;TDX$iN|tev)h?6clK`A2NK3`&@d7> zThtP`A19MB5ADKHvAeUSB^Pm%dN^g^zFyy=$8|%CbxGb9+qF{pYCD?H+WE|lOeTW# zEYcyuW;2#KfCe%%KNFDPM|%S8uPU0+j-jKsa0BJ{yU!cj3K$%+%Dz3udkFm=YEIed4K2Q zBFnGMWfG(^E#vW?Jt;R=-W%8A;zEu0zH3sS_dGF2DCa%-irl}Y8B7Xm0J_iAc&}e> zv9Lk{jPI6#InUElkN0GbH1b@N<@nF9N<_x%gt>`<#oH0zWUgc-j1Om5UcU7ncLicdmZLeBC7%uLOP% zbMc$tAio$kfr2M|u2o4-fvg;xl=64t&r7_F7cQy)qM-Tn z{Ib;N{X(k4IqbM>^Urs|@{|8O{7LBNZ(Jh<=LSmu$TyH=imf;|)&=?k?iTiktu5S@$u=i;UtPd|KjNMO z_<GYoAQOO&QD z484XnpsSJ8z`!t?*f>$8%u#&+O;ctxhM7^TWSr z#kbkplZs+VIT``M^=rv~@J5E?XlYT?uq1nQBS{s0p(Ys>WriNp$Uyc6VYUx4&vQ&q*p;zQ^(tyiAH}}ip3fspPw~VEt8D&~g#!+I{+?sXaaiA( z|97PQ2C2a3NT$|zC|hUa^KvyU zk1)=k&+8pU>>algF3b4NakBjvIk2Q`Z>Rk1*;Pkm{1JKnrn;pcFV3IWPdX-Z?sj{S b3U4}u9G7+Jtl>}*qBmt>^lz*vsnh;n{X&C# literal 8944 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 L`A=3<)NB7A)<-Hp diff --git a/native/src/main/resources/linux_aarch64/velocity-cipher-ossl30x.so b/native/src/main/resources/linux_aarch64/velocity-cipher-ossl30x.so index c86f1024f61d0ab416581879431833b725a2e28b..814b22a60f4a95fb039a6f353480ee895b4f9753 100755 GIT binary patch literal 13048 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 UOYizF6(MSn75^72O6s=%4=dANR{#J2 literal 70688 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 literal 58336 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 literal 8712 zcmeHMdu&@*89$EmaNG38>*(q}pm$X^x{bPaOVjL8YA1Gj6VfK+F}1YW?TvktxHxvW zzHXZ`ppY?Yg`f=f$0Q^)Dj_s9p-MxDF(%Rn)utg72Bif}S~^rI9))GvgR()*_nmt_ z=la@_kUH@fC%XR5_xm2_JLjHz9$&s1?QE-Zxfm5U`wTB$-!;&Wf%O z72ROfpoPy%LhhuE+zVqwQ5Q@1H8YO&=@pzUqYqgyBT9Ck13QfKzb#&&*dy9oYFxZx zJcx?!@Vg>)2zF zdl17Ry&axYkDmJ4FaGkKM_Z$puK)7KKe>M8i~BcxzIJ(2YWJDz&riKA{q^^6iO~mT zN4W<4DhGxvwkKo7Q4RtxJ1}JLfJerPqih3Sc3{YM!n3IY{s+J+#dCHE{M{w+CtQ{N zq`7`QJG=}kPg`c8SRfk8@DK%qihN-Gd9ZIAUOl|Mm ztHyOhA5LUU-RRrfnoOtkK5a0mTe8X$HFjJ>2U;?50u(!rsy)I(Yf{T(^b9jcjP$YS z@t8hlCekU^p&ixKSb9`FswdO2ggHKzJ_fDE)s$u?j_MgzPsNOJuB >8ajKvNbU_ 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 L00z919WCVnF@hzz delta 95 zcmV-l0HFWqe&BwPC;{NHDX#()ahgqWf64Eav+M$VEfX>>FfB1T zbz*gHbagQ)ATcp6GLwKX9kZY?=nVlTlQ9Dmlbk*z0f3XiK0g7-lL0?J0nn2{KPQxk BB*g## diff --git a/native/src/main/resources/linux_x86_64/velocity-compress.so b/native/src/main/resources/linux_x86_64/velocity-compress.so index cda376fa2c627f3457d3fb5ef79b2791f47118d7..68ad4c73b3fdb39b26e3693e8f393b9f3af127f1 100755 GIT binary patch literal 78648 zcmeEvdwdi{w*Pc8KqA2o5+J$=!5!Dk!n(wTH6x&D=z$*XK;$K^58wh3RumR81Qj)* zXA)|g*0|!jd#}59-KY1id&R5r5+nnd0aVCCUOq4&!ZhI#qCyCe{Jy8Udoqb(_ul>e z{`)z6NOx6NSDiX_UUlkJ_0xWJT#_KToL{o*b{D0xzH8;=*$>taX)lxK%60X0<+%pB zuIFXlcA2g&&FNOIb9LEis;wJ6%A!B`W%0l9YxoT-?OYSzHH+uQ zyX)%w-VXb{&R389o^z!(=r{R`qB;Ledjl|{%Lkvy7{|{ zj~@(0@YvNG|I_fF$`j6eySir`cL_7y%PM^-CBjUXkmV{X&AKKty=>-+nK``d)hH+8 zKebQq_rZT^t1td%;{Uby{~i3l9{;oO{|5Z`;Qx*I@5TRVpNv@l*{WeVH%%(?2-|Ni z`}%Lq58Y`z_Uv0XOqqE6p6peh&Z-UkT+?!`{DS z70z3jpY+K!wa*PXJnYlLL9_cMJ+k%C+$TPjKgoN4&Drn05c|Wxdq2GtUb%c_>7qq_ zMo;+R56aGrxUa7F?Gx@m(^veJ0CCrJ@s#?Q)kXd*KzLW>_g*2N#;vP*c3+|V)mMP0 z>I&sITp@qx71|w#{2TDU^RF-3?Tt~4Wf8;{P4Fv{*SM~BRs5D`^D(BoSMZ$m349bnd9eb zSEXHWgq{Cg2Tr@)>+JlCcK!7cE8~ZDzSqv5xy8yLdZyoY{3kd|?0Set=(mI8=UUe# z8$SS#|GtMCD*rnh&i;1(BQ~7rc7J=>`NM5I*LTA6Pi^>1YK|sqGse!3 z+T{!E@(%t#vHMl>E34qY+JG*z;Vh`M^84BK%(e4p)^*I6?fgo+U*&dr!>+&5#;4QY zu-)G^_P985d@9}}`0KxGRiN1Q+-;X1X4B79^oM?5*!|){18E2UNA2=?Ha<1LLBAJm zI2Tp&q-(5Qk88$^haOq@=!_*rvlbW4nBkfcn36wZ&fLXwADX|UXzt=E`C}I>e01)V zS^u(NuJx?zCo^U*n>C|w{-d)N%zu2Yt7zWhg~k45v*#`}j#XUuH}Mw!S?~jo{yp@} znd>0+pGT0>LYD~of7!s5P}JTA$P^Sh`{5b0=RG{5aMt_<$arM#BTMEMQH~3@k1Sj| zm#(uHJw_KiNyK<(nwc?oS;xooA9@r7`3NtU_yj~ZE4ZM@elnvl_~`7e9&?)M;vpws zXCx9|1f%d4JLC2t>QMqaESL+XNqo`a0;kZ7e_0rObWUQ4Ww}3|F?&&PM&aC9MM1Dt z;xpo<8Go;UQ++$?JE7{E1rqDG@ZA3L!o{6lpEYN}+{Hh-CGnjO-0;or@W<^ncB+pk z0K>g-@nbW1+p~&5?_jUlH!oRu^N(HQ)xemsGj6&0M>h|T-{-X7w%^@yGigW?mdX6D z8-J6y^_b)m{;&SIU3(#W9lcX@?fg`-N%oN2d|^@ zfQwIlFdwn!A&%atw)KU}nP*ty-Bn@dS9j8%PWnKn{Kn1Ji;bP~D{HO%9fK@|K{mXt zQuu@FgnY7To!^0ke6o3+UwuNp-6j4gmXPo0GJ4RMknh+wl;50?-;*-zpJOi&w2q3S zM~=Nlc|9n@{$<+zr986JonKZ$zHM^xN7)JaH#miH9hQ(!cE0n=O~{98j{ou!@*Nw4 z9x4g>Ac6R=AR+&I@m!Z{dO|*pjq{tCke_IW&r8Usd5ZH}l#oBz$;5S8LcT+4^tdD; z-?1Sne`P|xL!*>mk&sVw9Ow6|g#3X{Ca%9r$oC}VzmkxjoshpKA%9Rp{`!P`FjD+i znUFsqo(uamA>Xk@6CQti`z;2(#lW{1_!a}-V&Gd0e2al^G4L%0zQw?|82Am`ig0C7Qd_m6ZBN<8``Rhgh(iD4X<9r+|Kn+LFrokPG&zvae=eH8} zWf#Xk(hqb>PwkZMT;AX5`RGpR5uMUM=#;*(Q~LT&>As!Py*j14bxL2p+_67jcS@h? zl>VYq`qNJ7y`9n@bV|S5DZRN<`mIjszd32e7+r&V#pt$+F23fXzF?JSE?KOS+Ey

sb@vn68wkoVqiJZPei8LoGLX}d}T9knbf_4QdZS{qGT%GrcpGrnQ zfOlQNAyXA?^dyvYDeQ})G({gh5cx6la$8$$4v4d=+X$qE2k>sZeR|(4#zPw(N=s7$ z?8B)FyKFIfkG6tg#OEG`RVljPrLrRmJEQ1%i-_$XknRUeH;aQp)Ge2tg(j>t)kjsa z{irNG)(bRBb)}$O`UBevD-@&ef4q&?j1dvKpk5|>Qesm9v!dg!VP9c;<+eOg*nTsF zio`*AoW79`%&t~vqqxF$t8Aw^4NomRIqmb-&rq#s{VJ< zStSh*-~uL$z7JT1&TPamjsGU5)aK9+&nZA8X z1~#aiepof-`QBSH+Q@K8EXCJUJW66)S+#1Um9uI|Pxb9v`iX3e5mZ)*qUC-)Q}VsH zG%B-$`7C0+ym*_$GVkN?=Cd9AUYa87GYV8=Vz#V5G-c}KvBuB={_s&TR7S+Xn(0Z- z$L;XTdS14k;ul}rCW)`TFSD%@+vVNm_imMZJ0x*Tt)f-Bg*w%@Be>T;|KlB~M2a+| zqWmYNt>R5CSMl|wm)qd*E$+MCmF05D>?3bX5ntP?L}I-pVO!*M8b`e>zIIF&K9_wT zFZltWXm4LnkD^X+ML{+-74SuZC)vU&IhE1z1(bgu=zCiN2`Hr}GclHh;+e`hitfIV ze9#IDc~X#>Qz?4g9?>g%(&D?*G60whJ{S4+QrQ50Z+q4$< z(rdMr)Z)QfbGMD8j>Yj?8+NZJ#oZ9qn%x^oWmDcpt+~fW(#MqeTkLNaz?kjWC;oqy zVyiGFIkiOh$>}r<3dZ~E$cd|RDo6I$sv%UO$l4QZl6VVR^Tgt8a7Xoe3^>3|(ptsh z{#vVhX+NzswfF`JK&>aBWN|!k)HY0^`qB%@RQ0Z$ojH}#dNNN?-Rtk5x-?r( zQyr<|+SOOndPTGb6cB{-xe^24^UWCVFzrud?{>~IF1h@ ztJU1AL48%h6RL1rF=UV1pL5hMr>Dy-i-uYfJ|qG?lT+K^1$@38Y!9foIZ}UBYBix* zAHGR8#t9OW-vW2LHz~foMSXSm^Wn;1omA!SXN`q#Z?Hi!#<#Max;vx~U*)X|7z@HK z_N8Jxd8wwM=r%_mv5#VA3#4*`5d4bhjWz1|F733SK*Z_kEQL*VZ3ISLv29eURR|tZ z^pJ=84K}dYJ=hm)K&X*fJ&6bD0qH?$# zTP>osgocczNm`X4`C5yo)8nC<1apBm;8Q&*i~CFL{RxKW4}N`=M=Cweq4Y(U4l6yv z8x*$6?3|psOJeE#J<60*p;r7Za|X9iWa0ylF6nNM^$s&p-tKO?By2Trb;{|>JsCuD zS&E^0JhBc%s>sU(`;lQ2Ii92#!+nJnQNEZBLW6+W7d$H&(=LxG_52&P9hE&EdaBOe z8m@ipyA4G|FB6QkmD<)MNzVb%1hg0kGe>h*B>BUY%O;P}hij~<^ccMj>7{2pkH4~U zr{HJ1q{xx0|k6*)PI@>W)<7#U#{bw$xnfL7H*h`Rd_w?Yu~5E_0D`N0(rSNRPGV|RSnLEBKh zQx)SGiltE4*8$czSz#>-yZ8=G;I@z?C5tzf3#K7ERxyk$Of41Tc@mVW5vI$1@6haS zOBVh-U|x-q3Ce@ZSH*bo-!XyY3(vL`KrDhN~^R^K9O2NCkUJ>`N{y z8ewmp^+xhSZDNR&o^~NwEdMcXd{sq1klCR;aqS-bZzGAG#=z45?WTH(Vl_cvk6$91 z1>cT^+^Gy!sNULlNU?9BIkXaK0h{Z;0-UO$skpG`*8`s_+ZCG(`Th=dU?YtureS^F z0%2)y;xT{K7%zIO8u}<;ai&u)v{X@Vac#jhC7b$CjV>dtdr`gq28~~7D`8j+AEK&2 zIUR_(1eq?)?5SONS`x><&mq?o)BxGB+mKUwva?d;ST}D((so}Xv&EOR3s)8I(Jmy3 zWzEnDnwhL|R70U}bz83_(+eoxn~IyYd@f}1GocH1o!@w(l^56COrSh==OvDRnO*Qk zfMY=NfdxrkJ0!^okU&2ZBuT{|n2QtY*PkbX-jYvdi@j@ZmC_ zzy!#$e>M6NT-yIVD%sAIwzsLdfi!Wqm28bw!>Ol^uX(qCyd- ztKNFm`;p29cof)|O0*JYWt+TG^$b!(6#^b|KeiaWslQDl2ixqNst@o$q+S08#%;>e zy%X)I;VyCQs$?2c;jH-DDo*m;Hr|EG`vgDDseFg%+vun6~T~V+^kn zb}6GPP>jl>U$9F<$>~6jzDk=1rlg{nghY(Aox*Ne*ovwb1wTS5tptT~c4}2Uaw=gu zE(aA#LbbTIrXiK+oaAW|wO5K_GAbJi86?!8)t!>CgC0cSO|rm6W{t29HD4dxkyFd7 zd-6?4ixk2$g!46MZdD%|F@4qZub>_ffFKDGt+E?H+X?9W!VdTxw3dOvJ?k-4ZIba~ zEBtt}w^6I_F~(4vFDk5BiPWbn0$oIYiTzOWHNudtGTbFb21Gn`z2vKwSdCvCw?okb znPUz4>K+P9^Qhj#kytXUd-2&+2w|Bu(^zGrw5S;nwc=XYGe~AXm)b;=@{7(njipIHtwO zcv{#8vm+l?mUaY4U^M}jjE2w%tI>}1fG`aR@4-2s8aFQCb)iR+eox)T@aZ}KT6r@7TWZaWeB(B_oUF7O;m zViG6~alg}!9uR%t%IFdH2B1#Oe&A<|&a{>uVp$cE#+}W?7>3$YAUPS(wh7Y8v2F1C zqy&8L1+0>bw5mAeMx_v=0%o75!W&oS%q_8MIdqb`R9oL+Q|)x$W9=8(>Q^ml2TU>a5Dkf{O1@ zfW!L`6xsg>UUM``@>jNZMo`!I`3;J}0<45aP{hCP;1ciO?B3LIiD!4mk$C3Oiv;X) zPd4l+`ERo!1^#%Et_@G>uf9!$L5OHl>b?MRj3nYvQe#35NM&`OmoBVy`SsP$kr6Eo z8U9PiDK)j)PmD&CLPXNnC4-?rG8X56G-wxP*R+{$xSR8R+r`ifbVAY>xk(PDmL4a^ znARqU;fFDBem&!Lyk2*N$ezm}G-*r0^ea}q1y5WJzYVY`*GCFF4V4rS&Zz7Jd$(b_ zYW!Clm;!>a@@=}2)}y7i)M{y^+Y`W)Q_wDSTiR2q!kvrKNrh|~%uvOxu#XC52H~|Q z_64LBRP;_vXEXDCXNqo6*=ZG%1n-whDNyWkJ^gDIW1mUvKt8)%SATwg!@uWLn*T-q zWFleGZwVyP74e!Mqf9>ggqIKEMXP+iF{dZyt?kT`@B7?hmb;zGFaP7pS(~|>SO2-z z3UI!JGYBd)jWtD1B(s`)!)qM6jfBQbX4FlmKtHJARHwp~zs>#F2 z-d*MoQP!F7O85A=y~%cy3v4efb$q25>q;QD$&JtbG?=Mr<=JFsDaO;;v7KkF=^#jT zPkJU#d$(8 zHh%aRBf(<)cm0!$d~4eAW<@*JtVCBV!`r-MbjAB9L_hC^(#Qdn!_#V9=!foOszur< zx`H0j54WF`+HW)&KR=3ltJt;*GO%O!phr5N&uyf6OU^kjd_9}ENgEq-l%8?3c_&t7 zPW}a&#*p)_&Z%tZf&PhW)4GFYEpvI7_?o4{$^L}Z>e$P?+-hE*_}UbWJnQ^1pW+6O z!XBrppZiZTmu&nv^^%z@@OsJ6bzUTs7Irm%?LYqmuf_cr&U<9lTJKqgtpm@%;HNM_ zXpJIdqh>t0>>;=&W(Qz?8cz>XqUDWfi{~;pT=@_Xrb*{ocnjASWf^1I6k|$v!O|Dv z+V14kkTkg@8?KF};AtsZ!f6Ke1dKIAP%7JFt~%3U#smK#YYeap{+u>xTE6i*$xfRP zAxfi^oC?aC91Zb%f+?p>Wpg}@4e8eW`F7O@D`68JK%3^b;?LAeigr3B-aWDMjn!BGIt1@DW-nusGYjN_8n_^ zfXBhR!x{|5So7wjm)bBUIh928=l5aW{_{T*og8acg<}og3eZD;c=q;5T8Qzf5+uLf zD=kz?v;kPYD!NG@=Vn_Yu~hgG!2w!}5PX+An=qiI7*N!JV!?xwR>}wUzJT$%HKfsY zmS1xUG6?>3KM9k3_O3Jq4o)sb;Ov>M|2y#={9QCFqzP;mT)_&P;z?(_FzFdeuCQP6 zM_46Fa0tsD1>WE(kj9WNF%>FZGFE%Ymd0C|r7aKr#qC;8f5h?ClJCRfVKO@`dygnk znuh!V#LkqxDeY72XbI62ekI_g6vekScuMyDsS%Kg8i^+u9+_|ujv^I?`g0W{oPHMT z2e^m-E6f)=Fc*LueHe?$ka+t=f(2Aco#lmUn>Y~Cu+_Ue^Heuu06+B=j(eY zLLul5sU@LsZy5a>OlcWvh*ngr%X+Xo9a8yjJd3h>SU0W28%2RYQ036VMtz*{u3-&xYRU z-lJE3Mu=0c1^!UP5KKlP#2u}zNV$GV|4ZVJ;SP! zU9zvplPhYU00*c))3@9+N-Ud32qfRU%wq~-55g&jwy4S_Xi0|P%fr;olUJ22C^}e& z3TJ`fQ3CTH)iX5qHkgpiXC>}Z7Q=ms>a^|C6HILf|Lao$zy>ty0-=cIF*15UEM;~4 zCRHqd0z%SR)Fi4g-^G)5{H~vZS21L7zGS;0%!?hK%gJw@ME5aUmwfxg^6_9RMYmYW z*u09C4$%Y51x{GP4&0|Oi_7%zYRF!sQi%U_@Emc}suVQmtztXiH(?PB2Js-!^CmED zV=C}D$y3y19)wM1>r>EOWjbC(Q7=8`V;_48ZvixO4GLOxCbK5WB_0^VH#wj1nI zGvI3r?#>rqJ9L+k_HPQgZzG43m57-N`5Fkon^XQIMgp+}29OG@j=~$wV%bgL9b?oa zMbC({PdvUvzlyw;!i9*L1eYeQ*_>+UqdR_2wTxKyyT8LnM#iLMiylF=k7h(2sGAv@48ELl9<6NzE=0Kb}1(8DGtD3VcKlMVui*zPWfhe!kj1Yg^@ zYP_9m-UMz-kWrR>(Up8U4uk@)S^xfb@Y)HLj(oaA@BGi?lX)Mch!ZZrS?^8&Xwx%a z_-8fdtvo9a9#gX##GEheRDHX}6|Z4%RNqcfdx%RElDnHp?pEkIByWX%HK*CnKrVZN zr`yRT-Rf2V^1Y!?+z#y|cWq*+Ink+u`z%@+@g3@5uef}N{OzZ&;*yuhMoyVf6YESX zADch3F>j3eg{>eQ`J{9FgGhxInoHdcn0E(Hw8MNqn!`}M@Ef!$ayz>nURe)u9l`a5 zk@1W1hC2_&8r9QLdF-<3dtNZldi9Ni*%d? zKdRWpS(N7?l_)f!wJ7q$b{COF($Jh_{cQD5bc|59g4_5#C z1sLr67c2WGyW-0cUr(kW2!LMtw1=#OLUCcYC#VBtiLIv`aol)>#i^b}$Qgx+UnMP$ z;2vGGtdd|74uXSo2Rn-Jt=``dHcK+?*KM5woHkRZL|`bJSCJ# z;>!!~ek7+daUvx}fg&OX>%Ba`{!o^}K7;y?{r)4;Neg>jhmloLX9|D^>bO-Z{-P3( zBI4RgWWrXaK6(!*){tO(e?TrmUZx_Rl}H`7a{| zO5}8IbNT0=KD!O*6pIc!Gs3pjZ6vFVMWG+Z~)M0wsv8VSQf?efWFMF=#ycJ?ia&;D+3{; zlhY_8GzJ+;PK&gWLZ81>#7(u3B9c%mkA&J0wMKy71q26?Z9%XHcN|WjJsSqw0*}gP z0N272x!+Tu9k17-9>lQB#0*l_)sdGQvMqb0i&`H;UE!T#=x4ZUrhxIP2F_%kdfVk- zwXAYU7Q;jF5;D-UC(gPvXR z`*tnqEss>Gdp`q@1z0trHo5@k1O|h^IYNVCLvPN(pjoRVG!)N14T}mfQC5!Pt){i5 zL7+#MYcz%FiDmEOWsVuASDZ`odm>XLA{e`M50mk9SCk$O5Y{s z#-b_l^gFH+SPA7fp$X0-6p7dH6Gt9!&L{`${23Twl;qnbYC+_n-yZn$jER?sG0aJ1 z$5(LD0ExKFdY+bbkS-yj0KBJq5g;r3rg#*wd@P1o(pr+mGB55nfXiK2SdR%2^ZL4& zK>n?>t2u!{Lv`myzYxJR^^+Gc@N3<kuj~Yu;3fZ(v3Op~-JWLlbPTm^H7z5VEI%^Zsc-y$M z)f-c>T9VW-hp#KMN`{4BL?)erIHZN(!tpFrOpKm_%@fXe^TapCo3FKE_5#yy9N5jFCzWQ8 zjgYvK%wmwZ5=mShFrWw*Ek0RsAWnZ0`$QgsStaI>{g6_Ye6q=U0rD*wRXz*1=63-Tl|#P+`QkprZ(5ZhoP+;1bl zf&W2)mdpzn&sX;E?y3rT_M@N+?kB1A3ET36mBdX^NgOxU11Ve(Ftp14DQL&>FUGE9 zgsSW_IHD1h8}q~Ja2%4V{wfF5F)q=8UPO2K#v6R?0m>Xpnh4uKL)da7p670 zS-ma55!tec5#Cm^$wmi&Srohh_NJsy&4S$Aa~ zO;~}D^l^}=M^$((RCZq0$7ZRZx@@*}ekECms&_y9&1BvgTS#ls*yhf!rALuiIzr`S z&lI-rP9x(Fk<;m{5m3Q4hr0;Et&Ht}Y#T>MMZo}qC8& zB+0k`$pwgTK%iNU>}y z@dPY|Aw`(&S+=D34DA`9KIW*m`O2gVg zz_%@Est~@!*<1oR>~67mcrY{!bpk%`ReVK;=nbI$GxjNMT!DSc#rS$x384&55^>yC zRe)Cxq8iV*94TK9?TSVV*narpJmi#X#uygh=wVV!E>c#;BEfr>PuQkm93=MjoqF02 zC+b6QR(+-zrU8+Sd8w3%=z(}XR#hp#cjtVFLdw6kV}9@{nQiiv;F+;#uuLYgWp_yI z-43lCBR*-=s-Q|+mmUEfA*SAXk41cqi~BSTwCoN;f;x=|zTHcEVZD6l)oh2HJ5(%+ zVRkZX@%IWVSR~GWW(bH^b4;3E?!=mr;m)B_OAs^zNkTNMdJ#)-cM5I;Nh4_omG&0q zrC?=t5V#zc&+6JMyA_oIZYQ`h1xPTkPM-K;6*{NPKIiZc+ii0YGKjt~1p{}*d6C|N0<_uB{8!$(4C)N&q^U9V zC1wv~La$BXI)L`}frlMd3}Ep%UKwXeBEexqBiV^ex#D3|BGGnBOKSxz9l1$W_)f|p<)$*nEQ;R^OjMQ#KN@Z5ZiDl!4`oASVo%Mt%G?9Ii$yFJoAdKq58>^ zDp{5VPfrA%bjl%9Dq$@sK%R`!?J?vg7@Ki<<4`{*ee*hQfm?J9%4AjM4CIhL$i-ql zkAlQl^~Qn>=|0^4EQu>cf^3-C3@eeSWS$|lsu*0Z9H%RW+#`uw14T56B+J1W=}gOC zmq%J*;u5oa1Y`?#Vv16rgosfw;>v7stXH)=}+D(!L?H~nMIV@`;_Di52h>btDvPtDR+k$ z!bTJqHr5=mv^1>q9gF{rnn^)pcT*t?LOO0ssCX-Oe_0hT|BJMN>tz_!P(^;02IS50 z6hJ9YbrAz{Wsg}UO_q>};Fj@-8LV;_vT<04BtrlrlP!iPlS>B!2a61HfOcjTaCHiD z76J&G#{MxrxaNP*Trj>~B^!B|y`PmS=zh@Bpr1t{R&Sf2kGezkT@b^Y(J|RrMne3y zeAXP#|C5zZLcBvhEtH`=3Gsyd;A3cJa6^B{Kg9PdI(8cBX(CPk!?Z6IjY?W2=8KI> z>;1m-i!;qTkZZ~70J`n>wTfj!k%c(Dv^x-#JtjBpwxTb%+>qhU(s-y`Bt9hO&wvYC z->Nwm9Qi%Qa0?B4q9M#aECJ}qw*nJUzc*WExO<68t}t zaAPIl513T%B;XI21mF2C=vVGsux3bX6sgy0SbqI!v=p0p$`J$HCi$wsZXl)cjpiFj zD>)HAYc^Gyy`t<)nt#8O0*sn&n!;M9*j=~2e+?O8(=KQyT?MB?cO3-Dfc30*S$`s{O#Agh z*2CO|crt5p0&SR08$k&a8(5YO!AS; zAgub5*v?j>Rlp18ArVA?4N*PIDxg2}E@2lag$XI%Z2{o`wiX3VnwMpF6#PF$Z5@Ta z>fJ`WGqPgGKLPBhKa1=@uA1OG!?bMTJEV6**VFVA!`khKVSt1;pgt^3@!$+(W140u z5qM4Q_z$+y(ic^6ePJsLda)86ipz6L%aMxX`+@?-W{Rs*m~z~~9h{5(QUU-3b`)@Kq8PlcHtRfBpt6r7RQceS980e~CcKiRw&|nVD~7JOdX# zR!t<c&|t&@8J?Tc&S&H38)u>UgKah+$1nvACkjLIxg^U*C_1L+nbta<+PlnJu+9+8co zlkTvLdNF((F$_v$lw=~je^QQ}2yPFQZg-W?JkL^5W<8`N@RF^aaRX=5wKgnhjE51R z>W^bV<1QF&d&F=9#uFJ>(6|c=8hgahE&vKCyv141AX8e{!_jdKqHKmPP0JUK_z6w9 zC>dZM1-uYswPEbHJA?+|D73uSsxkVGr{Kh{0mG9M`vhi;8U2y#g`6|e?qn@6=m+#4 ztA&1R1=g|m=$?D7C3aDYeBNJ9&6JH9fE6?bU0UE)jRN?lmF|vT8oTr$YZPx38-Zw7 z@v{`8B^gDjZ8*^Y%`Cm!n$O4A2l_!3LqANb1fFR=HVyh=8tDg~fPR=(?wSTD>gx}(^e%g6hu0?k|EW}SPZZjEI!qDk;`IlO)Ol8 zlC+JQzGp(rl?$-D{Sfex@rrw>>Nn^SIS$NwAe7tn>lQc2+Z>Fsf?R;^fC1m)7N4OR z8QLDoYT#EHw+FV}Q1dPTWbrxC5mq+%?C}sMnMpPc?GnSpFRDJtrlDP8Xcm>osp&*R z<{ngM!vV*!zP#sUzi;_fSXEn#2QW>igFs&f82Nm~jR^DuF?OdxJkj`F48=q-_`6Q#6donwRm!`vq?1J0=|alAz}J@sn29ABUg# zE;y;d7i2S11>9BqIWP*9e?OV|+0tfugF-&?TnIMP({6#2=tqk0Y;dUz^{KrJQ|Zi- zFL+su=KbUyYZJ@<19eou2pZax?ZKq>Dlz;ba%Aj?rx~y0+f#f*#@_g$V%cNVCCDT3 znT5DRo#taG35?ra+eKZgvASkpTm$SpwF79)+d2a3p`m|>cWsj0H4IlZ?^=!3HIGHG zfIU$VCLlVv8xJHSfXMm?ti)Z#hao=j65C_R5OW)zBhYSM!pw>fEaBHN3ngKv1XH(c z1(ju=!3P>(EvyA?m?g*pB20$iwY$JOJefL71VS>cVXZLneBYY4*B`87%TM#QJUnZa!Obl$If~Wwz4~ zVlB%9EEx1r*s#3QpF0XjxtVf|d+kyGp!hJ>Sc*pmuxP+8nAhPUsNWu|4~nkl{opj% zzz2%x?``U3!zw&7_aUiU zg{Qzb8@Djh*`_~= zIB~0zOimNW4h4*Say`AheiC&k3ZFyN*>{0>mc%F)2;+jdrSp=fgo;O_?S|ptu^6^#sI{}WGsY1h8Z2UGKpI{ z8fFs1q7((lEa3q%_mJsFE9L13UUEWWO1}U@%$JKmC0ZJ0!V% z*+sxcDt2mGA47QuH6YKJo2)3xL%8RpucBA z)V4L;#N|^L6+MU7De#0*HC7x9nYz9yo#J59pp|drKK94ms(zna#qP(jjF%LcjmSu& zjL=^p4Aq>^z7Yq5!$}YagY#N)#j-T&J|Z622VHQP?=w!m}Nc%ePO}N<3v6~K77X*%e}Y*|70OP#3m4P3?wzhhX7@yN6Z~O zO}b(fR<6AinU)6i?-9#gz=C!;Sqzt>mQoD3D+|O~ZsG;L>6;&yL0SyOfo0lPLckMA z$e66ObMaFcMG0_1cp%pIkZMeZOZ-@kDI3$<*m;MAQ{lo$WZ_vzvly5--$)xj5fN_S zV0iX82gBQd!V+6=UpG)bxiHg_8G4#nSVAZoyF9;*TfrUpH*zMOUA7n)PKM01$H>=^ zx#}_EUrD=2@ogkcD87y2Yb5<~M95rBF3JL#QsjZ2Bv}qho0t7?P=&^!j~w}9@Cw9U z!;M#>|5NA}XXB%|iL-HT@I#DrmNjRZ$>&V>g5mRZ#U0QmB?rj(`7zo+MjA4DQbuSW zGSr-1(%XrAED3co?0Ss;B6On*ioX}x9DoAc*zz0vJTnU;H|JXHR#@Pfhv(XHJGgCu z%s#V%+lIvTZ5P#E!1xm*oy9eguX%i%U&r}CzFUG(Y6y&7+5?d5i_y2o(YIDH{8zk% z4D{`B^sQA4y$|5job%scY%=>C#`d89JizU}F5sDO!!rw4rw8V2(6qG<3-1L@cc9Je z`ELaCGSaY^r{&Q+jnX{YmF8(Yfq7aU&C_@S^E4|4wtfDKzGXRaTu;Nz0$YIw#$~=5 z=TFG449KnwF1!9heI`calY={8;KlJP={!bGVyB5QF|Px0r19u1QqY+Q%lgSe+DB>J zgKcD(FJnvPp9$Z35@4-8c&|UcuvQ>8|$97Kf{jG+L_b# z%{VOtXi=ON!nLe8E%q_@v8zIzCHc#{!}#U%vQLLB@C;*cJTBltTI5bt6dxIjFpChS z#b?&+bZp3WSWCzkLrXvvB~NDI7#^ZvYbO00zAINIe7Bhlb>o~aF+799dLVx{LL#?q z#(((q_~_wu7l&Gm(K(NR{&6agv51q(X{i(HmTfdlM0p;5^06U@KHpck?-cAd@>t%$?=!iMjfo3XGzT(cecSw21P3r z$w#yTheN|$!WSK^!7397B|?O<`kRC@mc43)4Er7PE$1H-%Fd#PxX(TKD>7v5_|2`I z_@2^nzUK*t?|H)J`{vGcQcD|>D4>ta6XFQO^L>L(syUq~!Z!0D*7@4)d#*b8EZY*@ zbHH1q4@hxyH`+||-o*J7JK$|5O>N!{?=7vDfShKM63=qt?cPJY-E|Z60pHlY3RE|T>YOKgx5+SmMv3J<$TM+skta>uT<6dwJE%1c8lrNErkxw)Z89=`uC*kI z&t3*u*uu{2jTia&bd=BAJB<^i>xLWycb~prtz_SABZyV`HB}b z=g=aEzBmKQtq)gjK*(!ug{>Mn6@Es1Z= zdN$W--33B446ie&j2m9}d(u=^C&TwPs@?i3UjazI*}VmnyTG%+qL~cr{RO?;G64=G z6*CtepNTmid`Ob-)RM!AgML@d5_jeN#pFIR1y>(WGxrXY<&Pk+(_ar>_e4SgeHvC54_c!iPA7W-;Cr;2-g%t&k&MP zC5L$@j+`To%d%Wmy%7&`CNW&|e)a(zREW@AavX9Xi=FV9;IiM~Ek_?3KZW1L3fC;7 zY=mp#6bFjd{K5<(17;qmWfG{R28U{piO2hr3FrZZsl);B0Ir4#6bEi60k^OUL&)N< za9F=U-{Rcqt@E?@%==)D&{2|E+~r6PM97ANcj*}^6ZEv6h3D=H&<>K0M`$Ha@_kE0RCL5;47fE$p+ko|NQ7Wgi}IK01vtOzC)H@XeK)ptNkj9uW(iwuv-A7i)cREF+@A2RF9in zLn-a9q1O@6>dY(C;2P=pkO7)@)9eM}Chj4c;&GEvg5@~3s9l3@bO8pfn{Y6ia1*Cj zsNR$;yoH=;0AijDi9Y=|0`;5bqdMKoP3`za<`kT=eTC~T3Mj+{itkz@z4#y3O_BL=is zq}_8gk9`@-d6{!-FE*z=c6CQOQQnL!^M0rTq9<;%(0)7kjj)t_J?@BX@)0X((uTVf z^EUDm#qFVX|4}97S3n`VNfcmbLZjtGN&z@-1VR>w)8dRpNCIpN#XcC41&_<@Jjnuh!pYSHl$3uCOmkOLE9gic z$umQ{iF|24Ws9F}ll7}`0yd0PjK9?iDfwyWIowPn6^58w@vfzpyWpGW;6~!^o|U+P zziCH6Fr|?`?r%!7CKpqZ=ohS!v|8cVH zOXB+|r$7$;34=^>K-C|H9KgOK73d~8fDFiiGh*lskOl1litiA$_EEO@eRbq!;^s{O zx*fnVpsem2y?d%3qN}cPfHoI6^FNw_FJcE}?@sc?xfg#|+)SYsnc1iV zza;7edQSS^&@7M03CBhyB!dCTvLEoN0-!6&aC3p)N;MFJ4w@YH^aZtLNbG!?}u8tG-)#x`T z?CLJy@!9a?;0nNTDC+3TasMgzKlQhKoNf?3;^7{&{7_`|r&yj!W5ioO!Mk1@Ydwzp zo??6xsWIZW_rVDS*kP>U6hYd=LR1bt7<$(7LPhA-@YNdSN^Ae&QA&d zlf8e~_EUE0f2z0qlyE~?eoBds?gFL*!XeAjDE}p9)1+P9`RpsqhGh2n5L%wbj4_iP zL@1Euu*B@41ehZN&!erSzAr4l<)PS;*I>^3z7Rw8VBC@?vf#GdMJNkbP!rznV&!u$SpUV@DKbI$Le{OXg zPdT+PLem`od<(n+Fg^e({PTB{FOJU}Ih+r{vbEv7AGZW2-}~TzDRQHU5MrqgFo?^f zv%A~vBXKRxaK+pwn`|f2|L*^-C1}9|w#b4R>%$B z!@XW>?k+xa4dfSLk&Lck_*4bR7KkKU&pu-yVxKVp{}?vo>?Da{`N#CDuzLre36WS5 zmTy(o;JgvF-#9L$MzI3~t9qd=wo8q-FR`mk9Y!ho*#-T5-U*+Sj^mRj9DMS`KfvcS z?nA?wmF5w&SOJ?P19KB=UFe4tEE!)HngL3s{nl<8@q_Rvl3t|puwhUhHq0Jg9ySd3 z5#0(xsel>I9mFYS5N{M#khcn-vYO*b>);=rs1F#({loOhjktgKL-G%!DEAM)56{){ zI3KtB`!0!P)yPILWybBaDw@mv!<&&s@Ie(JNbHHeTouvwJ5DGZzC|FO3U<(2V;<%- z#rcTZTgI0y9QgKj*e@RD@Ewsf+Am&D@WJJRP11gR6lQ=q5fxedA{*FV!uN|ei)FdU zra(9NBU=#ZM*hefd6RiiXpMZjVpASA{$i&X7(K4JE+U)*%WLC1=&fT@Xw8IZ1A1$l zb0PxiE$0vf6$iCb($Za(+C@4C0ggTua$p$3pTb#?6)0exhk&Cs7^ggb8p2cV)b6x` z5*^2-60Jm+ouorsF?N~8*d*DQe3^Fl(%Rcm@R2e361qW}Q<0e&FqZcdXa@E2G(e8H z&8CJ&+xZduO00DiM1PT5TipTl}a5nf)87I!r&<`<8!0>Y&#m zCRc0NpDu<*0v>B=#o9y)K>j&?X^hKjTd!e1PQtnh$wLT%G{;Y$WuC*qdE{?tFm|IaTrTDkZ%n$?=sY)!D)l2LH zSS(}tCh@T}pTKGDoOSYreP%s?bNJR`So2O`Cc%0!Wyn5S1%M}upVqL9fFP+uK#Two zF#jQ!CL@3s)7eHm;81b7G!#(Tk0G}3_+gL$2a`Yk6rIN!!7F$tAxxCu;XX(5eel(U z6f+rT<Y!H6X!EHLmaK00`DJT9}{|B6qa+}>7Sv2iBOUeYcKPeYPEnZg4k*VofYVfdsUS`U^r z*zv|WNAG(8#~I^ml3b4Bo36huLrr?FITO8x61MP-{P{fJ3j4;1LI3(TPKv=WrMBM3 zIY6y-^(=y~T#$U{pA(Z-zUiiepm8Yg88QkKt!-e@jgp?R5$@SEU&Ag5TJ^Z6LtU2m}g5&YGboTIB?Edm(W31!-jglJ}&uPDw{d{G|=Sa0b z$5AIp(tm1$Y*6YBy+PLs`$`|@qxA3SiWbSJu>B2D$i@!vV5=Ft8?mF$gXFC5`E3iJ zcbOQJwm{VFL1~xyJtWXoapM(+;=3qx7ye@?M&?u7SX#qSw5e!{L!5<&1Q88I0H=)5 z08k#})&CIxV(4XPp7t@oj?->uaI!0*URggKC?QPpmd#?6#N({joX6y*fm6qRJl%RM z#2;hE;DS%ce(bXzANeomZK9lk@yFL$kC(*T9*TC`+ot1i(Y9RUygdhRu86<=iS_u# z_+vty6o0(gdfYw!xMSPDwH}wRzrr{;pkN%{z<4<0P>g$pe?}9aBG+LFN>L;^<rdLPAKYgkt!B(0XvVD=MKu zM1>4OUhH91mhcHNj&E$@3AfP)ub)maKgM&6BcyBzUgi2iAD3(UZvf-|K%X0U<`^a#dVC{=?tZMWUAOo9K-caV|geeVZ^QFZd!wdQ-S1e6oqK0=-anZe9w;6!7Ya~ zDt=`5LtIM-CkUFnIvqjM_+IK5e1tI_2m3#6y)TiEh&(RoQr+=<_|{Xfd1bsQF)yg6 zfa-=Hb&7TYQSc;xWn)$=pC_mWHnL;mzP3Gy!{OJ{R>;C0$;h}{V#~|i)M&uBJ$PKG zZO8{+aa5(P`tnc#{Oa~wFpjCA0y@Iw5L&^}RH77p&`! zuo`Sxk_LY$rM|D?KP$H1Vghx;va%t<9CpWNX>I&c zji^B^YanE^m`yz~NK)9;CIG-!W~!k}RMyN<#7yk{k@#bLHHuRg$pw5u7E6_9{P>LE z8GOcK@DZu*u-~^;49~{c!kZzkBRWlr98MATke@nbO!BDbP!~f%96w(p;~RTK?>LuD z7Iuzdqn`KeE^fkj$gHjIIJ*Fvl6{B8P#=IL=N#TbLm_V7Ngl2}sGbU8!>Tm+eJS-4 zM#lQg57DJDRls_IK4mpA#aVL--Y0mmkA@BR2p`+1>=%3ZX);(LC~+7CA85jag;x5o zz=zOoK!KTzYn+sAD(*nO<|v4Nnqn_RNi>-Q$T{MWS9c6&zlZ4~^zZ~`0LZ&=OQJbN zTnCfMT}BJ6TyCdutB#pMc)lBt(jd6$SWw7PPbm(p?G^H@K2;?{lc_iZcwLSpAI449ALW0C2p_);#U$~jp$RPsUP7?!5t zfk|+^15AMe>W-b?2TYl5)GhX2LnJ0Vg>x;5V_}I;q{KgR&c7V^t^*?A z189Is#UA3pF-e~iM$b+&d}Xf1Tc_0Fi%-ps6({}e(+>9tcz5RuU(vUp%728H`8VlWIA-s_2aA8Kuw1B3`fH>O zqt6EA(L_gKK1CljOck)fZ&Z#-p9k1ee4L|qNl%rdcEzV4NPet}v;ix!FftqxSh|26 zz{H-zxCg@zfPh9x!ba%jsKUd}AXSXgD`% z8>0GDp`I4&AqaZV@g%<^w2GluF%pVy9q|Ufpc2CrmJ9*=4(f>GKXM`+1~hnsNOf?N zf2GLjGtN_|h@pCb>lGZ31=|Jurmzo)OG3W~gy*WV#PHYXF1{IKv6)q2#7x1L7M1h+ zmBHr~p{0vDUx6jc-#&@Asd8Sina>4d9#&jLWs6|{6!En^_Bbgl1z=?HF{Sae$1-NR zA!nhtxWJOC!dwVxCcZ+7ZLpu=D~9+^#+hC~qjp^6O2H_0LnwlY4&Y%59?0C1N2(zM zIlUG3DHuXp>4*90FpXGdfYeD;*@9_@wv%LMTrk0(aq14(1IGBI^#}}f1%zt)w3dtk zS(`+pj8JASCBgIlm1;K^of~%4aBrqD!^{7Lm%UNre&2bmwNDY9KSXE53E+_=eE$v@ zLw4{aP8F;FK3N@8u*V$>2As7?#OF!tiOX?F%lnWb_?pdAX!)nEjWEbv1+nMg-;}0H z_l7FTr*uEufxtB^Tzn7Rsv@UyoZ;?ok^?7BA6jcVJ_(505QE*?TMW~BDcf%>2AA~U zJ*kpMV+G)GL2F0`i2R;l#SKD?>DXAbf@MX|%&nOZqB<5P`3E z)SdFa2eDO(B_9zHU*vzlw_q_ID~o#J_^s;VUy0$D;gg3=k^57ZDx3xmNipDHk2dI7 zX@dx)NbA8w{+soY$aiT~rOwuEegw zdq2S-;Oo}Sn1l_%%NS>}mFQSu!~|fj0{qfmz_n^Gmv$VKdn!q5NE*9R{XgxUd3aPs z*6?pYP(` zs_NFg4A#2WW?h&VKBj9SVyaigs%zJh%=4k(&4JgIhJy>Fnq|q9+2m7#d>1TG(WNJqcppFnS=BUk#;55^ZnJe4 z@B`GQ=3#1=u~d4&nwHF(_t7t$$x5%lmpthO(KW6mvZ%#}ROt`hy$?$dB<;6}z9%q7 z8cO6wlpQ_C{>!a#H{1Gkf3c6wf_U^7a|cRa!D^D4T8no)@gC95uEgp$7GF!7slH}`?TQO%gyN_;h5B!uAd9w9$+wn{e z6}whOZ5(Ow^fi=Kwldf{+(X_MwI^RZoJ%kBSd?ptgSRnY(r(2a;HxF#A(elDdge zC#}`h5+_+VG6U7Tr}~Ibk)zDod34x_9upO+{b)zzPa=l5Vj0WtUUjx{JGw6QW~W+1ASGWW71cK7V6tLVJ08_1Kl_0?5wGiRu$cUw1okv z92>31Mon@GCu1WhL;D8WAX(Um=c>R3s#H~E*loO{__tB`_mZq}`-FeZu@1u@2xsxH z%r4R2F~km($O@I*)~usIfqSidNMg=q0eRDt^Tv7@PtDEjmj0YT;BQ0~#x(g9#ULyr zqd${VYoEQrWETGq;^A=^9Ttbv0|OBsRf51d!nUF=89oR*rc>lIGSjz-t|Cc{^N3;A z>xZaB=5Evw@&|Rcy1*qA&*aZ8`zVvMhWW;0&2iU1rIYK)N=iJfm6WQ6UCIpNwTe%$ zwJbx)Z5c{3GpZOOiAki8q2xe!%PICp8z$^Jdg&nPYOs}h@8=K*hRZ|NpsX@^>9Wg0 zx|UvAETq39lgdK6my67rowfHz*hlM?G^$=hZR#FpjJN&sai@r#ZK<~Qvpx-MkmU*O%q9^J6lu`6L7aZx{;N5?8+mNji zwGQ@3Ai6}YbJdQDl9^5!3pY8QmU5#vr0Cc2X`klI-a9h~a_G%=_mK7sCdTCJ?=><_ zf-KPyxT^Q`qC&iQp{!3n5-&zkGKnao=yM_FQuNQIV=_56)OJw*Ip}tOAKgD>EOPF^Dj~24Eg-lfnKc&~uKmisBvLm3`o*sfl_YtMpD+o6pHJ+nWOS)9vl<9SF6y zRKc=|Rt;I*u>0$y0cx)dqQ65KF^C?MQo_xAL)b($4`D}?wc=wB zm9^nx50x=?eh7Q0414Gy?4eEAL$VQG)3N%1vWF7ni^2P_J(|QGqQS7ewQ!ul9^&l9 zkUjJ?0xR)Y0!+auqFDwh!{`>#rs0NBLffUqpfHT22?uH8NJ*yIUdlp(M=+4?LTihG z#1t(`0bx)!(i-zz2r&cP?nT?hM&blMK4z+HB-8-&Kp`6`8=A_IiYt(O$|p9G6DtZE z>Gz>=liBI5LbaRxx#W7``Ta-RM2=*Cv7D~MtdW~Ju$sflF!}=}Q>M)e%Cu>!X@NnM zk#S4I?vBGvEOjcmG!v4+(h@n@LPhz5iXuq`@=0d7`=6E(0+`?zaZMXaZ^ zF2cHKOP6ZHw!i?>4pQTrt<*wtT#Jk*G(f(XSMT8gUxMwUp}EM_Ggdw8rvu;_i`}nBGp;co_}&Uk%!bx zsJ$=lX!Wu4`~zy5;e{}J?O2z+CUa_chwL>GhU`dtZQ~fT3mU6Twg)iUcI>l^HW2`h z$R!QO<8ja~4Kq|e-j*guHU*?WQf_6h%|%^mdrjFt|BLq8EV0*y^4ZFey>^_!i1zPC z$F)JG#E!}wOV}_m(pVd^u3`6AM`f1DGS|L^p6G6_{n)W6O3XFBMWCn}GT2O$OA{CI z7K?0NidexIV5v|qwXKFTluX0zEyn*{hTCR>^=!CFyo9#fi6@3zSVJtjQB1gbBx8-w z5$4BdbFb{U&8M^D`eVmU5j##@h#iL&T654sXgdy^Wyi@{wd$w7ksUXqOTRNrU<)>z zQyIsax@{4;&9#|2Nd^fu+ZmssxOC6mn@?OHQ6`_V;Rdc^&)|~Vg64oosYcaBHer1S zmO~XwPccMH*nee6{^{TPDf>zb@wbqFRH%Kfv)%BVEoJ@5m z1U92=%yFTzdt_5IwGh>c!E-(g56HfXmHC%zWYUkU;aO-{83XKVC)>Xw?V$<<{NxYs6oz(wNc20^D z&R2Gh40*@0?kzS?par#Iwae-397Z~-mughKl+664UJtc-G4)uy`jb{ zsy!Q}J+WwZOM6-t%`_rf?I{LCLd{1RHTwf~+M;0+G7yk$iz@Nds?O`@ToLdfM`7ck zwdQ!JDPA7Wbx8(H37HwpX$yQwZ0u{(l&RiDCE5>ffDOil*r4XUn)iiEuww?pUaA;@ zCiYie zw3_TeaLsgif~_WB%bE{{iyFXW@|uZ=i_Ce%w>^+AgI#&LRGxB`C`)H-Rp9CVXSx+H zRh2I|_-3*&(^c#H)t@D}Zs#40wx{waNOeQvY95;Qviba$#L78$x|ho5YrdAR&tnC^uzCHE696fy`WMHnJerIOQYPOifx7jKG8}ksQ>JHnSK5xvs zU#?>3mb1prXtkEbfy^3acaCJ%PCO1r^AU!&8!3d;6?;XVsD)p*t+6c+yra1$U79kU zQypVwU~|rN-AAL|ua=oZ^AYh>KKwxyvQ4(}7i}q6xZ7A&u<+9*g98uKMG8H1d&Rfi znX8&gX3OKkDlN~|o(r|MD#vTf-oMY1!uC?Ik_b^*)5~FvF?P=7j#qi5X#*?TeBiFt zQBYqWUqJ5u(dN>>tbHVgEu4Qq;iys)8&bnc9crI~MQi6ZIbP+HBkS2lT39D1ITf%P z7b~B?kdHR;Y3?EI1)JM!?FD?Kxu&UpUo4;NZXZ%#;Of1$a;Y>!X6+*k&OW770oQas z+RfoTY!8F?6Wcy#^B0nUy1JN*A^*y%xoJ>9R?yqV6Ff{}%sT zWYl!r>1J1aFKFlrw!pH&5Dq@Xp4iKMbbH3YZ5cJG9ZCq)`RCXx?xhPlf~{I#!1Hg; zgsQCnEsg!Dx=OM3AxL}P$(JHzGN<}8XJj*SCYIyZZ_y-G3^)6sBL;}>m2H}%;9_>- z6Hl3tj$g~kSWRprpfTu6dAntoqk5miRedbZ@wnLETsa(%58|r+(~J3_CUbq8qFhH- zMLE)r^R)`s(N$y<@4tNbT8FKL6Mhhx`d!Y9-s^a2m?sI2MQ)V}@6;@o^wd%Xfq#8u zwbOrYX3dA}p5>I8W;phEoFlCRqfHLS-B#V4aAb|mk>0Uz_de2bEo|kVx6O~so#XMo zB&A*ge3@G&UY7hG|43tue+Xs)pZ;XMn+p6HWYx#<{sB5(O^4&zRaNWdyG`jGsw8S< z-tp@fS?*X)V2+-z-V?WQlM!Q7E!-%vGpfIibEI=LPOS4jp3@fH8NZ!Cw7qi%O_R!c zX?>vI!d-&w%~4j6Zc!tnullwDe1(Z^nD+DErdoGVUFGG{l9h?o2c}nd zj-8v}2ET%RhE;t3_4#&|b2d4lq8~qX(f0WcM#P2Jr z{LTIs+lJ1h5~0Zp&99DE*U-nZ)eS>Mn%q>Wrmo3Y7+lHf&fn!v>_|R$@0j#2Ib5s* zL1Tp{dhUJFJ$1Nk?@7HKugC}LZMJ4s&&PdP*Mtf7my5IdB)l z(54vMYA&aC@;50sv9{lN>9(Eews+WPlWyCXW@}8d)wA|K?!C&ld6;keBF(m+O*+;2 zgF2!o)b^WOdlObl3Js6eDe#0iMse{Qm5mU1;S2!*tC{zTMrkd+t%1l+K!pbw1IKiAz`RA z+jdG2ZTrHu33hr6b_&dan^KQ7p@(-e_+*0Ec``CN;u~Iz%tSbA3sgk0$lU4CA#K^I2b7GZAF2aoSLsVmG=Q2MKd+-`eM5p zPa)q`h_>q=^3RY5e*GkC$R}{>EpsbOe*JNXU#|?~R~H8_Bj8`0M!;nsUpEPN4WU7T zl}|Tz%w?~kyVkd@eosR0Z8I!cpNSeqPqIg_aqSmJ8c&vX0{Z~?w+{a0>HyH>vb~p4 zJ8e)M99$Z&9zMCH@y*%m8pj`5-MI7Q zE`m*MZP-&ec51t=u`*{}Lw)+mV~v#rIDCYg%FgD-znwhNc(}8Uyxs=h0d@=X{-W{o z!{^^W7}9MxJo<;?zgvDduCej(_(Hike7OHW@f$lkiT!rNo=3;6vT?Pk@mS|7{0)M6 z+tzrn^X z$}k3hFU_`2F?gfP_O->~>(b?=N0P7YHzTd)6FJwZJ^k3^}iz^ zyC{}pYuqwtiud4S@ufF6{-*pZ(UqyJ_oO!LNgq44#n$+n&N}sUL8oBuYPWe`u1w0V z_io|;U+TZfyQNrN^WT&uW4S6M(0j+DN#EJV6ltsXFJ!vE!88z1qpnLpv@`zTE+U*`|`}B><_(P^1n(&d*^j0iN8fV1Ub8N>lck* ze{U1u` zn-7I&>!S$zp@eCupTA(6Fm9TS>C~HgiO> zXVki@REQ4M#At$DRqCo){kOPlm)RACg%!4`kS*%JRsFZA|7P{yzTBHj%c7DGR5iG= zI=3{qrgyGwaLq(6Kkf{A^F}+zPC8~&f$lo4y!%gh_Y8H8UGTNdyJxU-?3|P4(}GU# zp39tLOM*78xQ&iQm9$rb zYiQ@%#_^qetgQ91#tkU2ot@hoT%%Q4cADyDL+7D}`tjxMy&CEt?Offsq5Md!sGG(& z34M}=d$$wS+1V6Gx8wzNYmQ_@?C6G=QDRdf-H?1)#>Vr;bT{`q1uqX29F6}=; zaB>wp+qxS33%R--oXDfQROHdE{deQRzm4M z0Ut>o6e4xb)w&Q8QYJ!`R#1r}k0dVg=sxkz>#4}2-fM?My0?DOu*;OmwU$h}vv(kq z6633r?i)ig`LW0(2xO9`mHy)cn+U30{V&St1CU(?5i&}6P5!&dsrwW;Wf6cHI~L!e zM0JS)XCrIwQju17XpO-K6eBR>7%%kyUr8$f~B1(J#O>4Fool_f&lDz*Cv!TLS3>qaH^RJSe5#VmR3(?GN-k5C zT&60yOwAU#OwAU#OjUAO`Hc7Wp_1rB-$;C?_uzDQ z`Qcvb@geW#@$T~Wc<<&%-JPoiRemJSd$7{o`L_2U2@#zK^%AA?h`MeH4DLx@Z()D# zzmeBl7&iXj%j*MO@*0}WWMc8M=qz5Fk%`QiOV4BK@;ZXmY+kr4htcA~WAg9k3rjqx zW{qIst~!O5fB*T_yO{@zeS|l*nE-u9PkA!mxt0`VQia)1raWa`Z7r4120qaPUIXS+30C)n>mhb4er|&+S=vmI7xfaFzmRDR7no zXDM)&0%s|3mI7xfaFzo9&nZw;=*{;P6y^GT&fKD+(mc7yD=jW7_f=GsmKzV`&dD8C zlv^@u*lm^m+X}LL#iix*)62_wWV)W>=ljA^(BV`Y({0tgC(VNOPpc(mizpbq9#Pmt-j48KfX5E&Zefu4E zPMbdcu5aIc_dWOAJ7dOu_syJnzsHl4larh4_2%W}<>&i+zJh{+S+i!(o?VE0;DHAo zeDJ{{TrsYsq@)yAChj3zxwr~(esPuJ<|sE;+&tyxo9f_O!ugE3rzQzc2$;J zXu0oNt~%r%54lAlw^+O4gaqf1l+ojEN}Z5CIpfyMsZw#NHkGE%_fun4UutXCRVu2g zsq1LgPgl;YnN-WFl+-9x9aR-o5mgIS2eSfZ>CK|+a+`(L1-6R&gmz1`tI_UB%PqCs zGRxJ5+*4id>5yAK)9FkZmztiDnSICfZ{IWHzWb?gp08lm?7|0V&EgW;kk+H!q`{=6 zq(YB8LR}txlxjTo7&V~ylwAr=sVTA)S4t|SloG1aNy${9RDo1kR4JA#_l$DiQ||kw zdsew0nC^$#{Ybm#wEMAkKe60TE%!6a{XFD;(dB*_aw|BE^0clhwa&RBr!`@Dc1z02 zviA5T?&hQyXSXFjclWljug&X-c|5IQ&6Q<{Ysx3neARd9t?RD+!-M-T`RR=9H~#&RuX{Z)dCj+eJL{7RfAsD5 zuYYy!QODD_Z5sAc=@(c0((|ud-g-=?F1xv?XnA?5%O#w1t`Xm>cU+&?zA^ox`$ySr z@}_B+vqeS6#Kgvm^6G`N-h4ClTJPR{B3$1H*Y6Cj{~4V98#>1~bV3OsL2MjVm+JQI zD>af*OUWel1nthz?p*E8({4b7J3qo*5aBM2aEWJd7oEXfdX( z0yYw{k%)~%Y;iz5z%ghxwx!tUlx!#z`+L-LM1-FBi$GvXQhZ zUmtvVby~_PQXyrPSK0YEj&TWa1#lH`Eie+e5tsK{(E>JV8AT1Fs97|%ilzl(Xn`17AeI)0HFAwOS|E-V=tT?k zq6Ok<0U)|JEda#zp#^|`eQ5z8v7cdc0xA7z0U*Z$GOwaVfG8juhyh}OIG`605A+85 z0DXaeKz~5iJRLv+a1L-Ta2_xKI3KtGxDZGLE&?tFE&(nDE(0zHt^l0CK;TN?Dqs*W z7`Phv7LWuC0j>eA1+D{z0?EKIAO#o>i~vRgqkz%C7~pzfEHDnZ0k{#k2^bID4BP^w z0xn~mIU>Q&gJOw-rEC-$ez6X3C zcoz5p@I&B7z;nQlfu8_B1%3wn9QXzBOJD`?E8y3_^S}$hZ-D;-eha(^{0{g%@CV?J zz@LCW11|w9fxiGR1OE;D75E$Q3h*lM8t^*s2Jm;_AHbWyKY_P^I-nj{1*`@dfJUGR zSOYWzYk{|cb-;RH1F#X;1hfE~fi1vR;2q#y;631dU>ooO@FB1r*a7SWJ_7y)v;w<; z-M}7TFYqz25BLOV0|LOOz<%H};B(*r@CDEg90a}uz5)&bhk+x&QJ@1j1{?>z22KDc zfleS;Wvno8(Lf9k3&a7vfOw!cfEGc^+R#PpKLpM}FP#Tm2wVbOLF}u5Yk`r#jifXY zxD&_&DoC{&cpg{{>>*9jvKDYls^L2DbmcI$eLuqX2kd|YNC3_O&NZx^t+M+RgmS6I zEym59_krPTFe^l$=s+R48kH0c9<5~0HdtWpnvCg)O)cxV?#En}cW6!nUX9~nL} zy~y~fx7^ynm>*L2djvhID=&LUe zwtsQp^UwBw8fg1u-^Y9R?B3P-ua9=_*#6-M+uncg-FLQb+1#>eaYsb&V&lwdAmf@$44hDaQzkEzEC}&hSuMY;-;~Qgx z!B+g7alv4Ui23+nunIp1UykZso=Uky^luFY{rEZf|B(C4V6a8}tYGjn{3?7=5814! zr{O2u6AV5p_q_G8SNw`#@NQJc%11~SzpgeI>=6IISb;~Kp?F|2h1YKL&$4 z@Z0g*@r^$PgA87bRQyEoUxqIDId6gQLr=dR`rxEYLX z5DYHC&)x~WMHTV2vz7QO@OQ{PZh+|-wukR9Z1WOqm-OouzZ^ao1pZRMeI0pmZ`czi z*%PNY`p%87GH$u}hHFL+x>BM_{4`+2Q1TXpc7neTcn*A-#Mf>Vz8_c#Pq_s*$)2z* zD$Sm_IC`?(nbXIf=&~nFu*YZi1!rDLD8mW-5ojjf1bidip0GG7!|t5g$DZJ_$4`V_ zl79hRUJ_*!!c;3eIcjfs6p+lC?!DV+= z$3)e4RX>r(FB0}=!-K(z_{L=Wim0egl5+KxIyZn58C=A*IKgcLmxNB#wDyN>D`2^Cn3wvi0rEL+>#z)Yhxya(#w$arr6tU(HBOO9(hEhC!|*Vwv6=V zp#$#V-k|LlM@_XSR!2{>J2UKwci9teg|PWN`YYj967I@SzJxm^Uzg-N*`695kZPQo zt-Ej379QMsN_nP8d2)bDNq^pr$hOF?s$Y5!`J|coltpik>oFf!-@8tO7rCh-kH^!4 z!I@l#<)PXnU1&)87RTv4Qm3i%(pKT^q1wBdbbdxUzm#-v!ZSO-B~3!N2?Bb;e@BEj zg+DrY){mERL>9%R0{vpBFSwQve@v13B4o8Osw^3yvP3^)v!~n|Q4ZmWY|@Cy2nPRK zhGY>urqY0)bbZq7%WcusJ@Z6{#bYZ-&wocSI7Zq48G1S@`}A$lVvD|~_i2btbdke$ z(#yY(y>~JsQ|a}@vu?9ZS42f`i?_cR<+iVk%ENbw+%4`ggDHKZN5&E)gP>7&>ab)X zG|8t=YJV^o{5G^V^F)QYK>Gb|8eVsoTj6c^(54{o2|6Mem zY=1S@h!b1_^4O!$(Qi*hPYTJIYComF6hBzGN(cR=YzYu! z+TVh~RsF28DIIX@>B?rDwrovOHdlDrCP>-R!pd?=+13f=dMsNC#gU^xmvDsbGhCZ= zqm^r;)1<#lvu8ij$L@sDXdpT%A-1%qd`0gNaYTKH6Ba)Nt{vQkfl{jwRfEtGf79#u=I5?gl4Tp5=15$OoSTe!U>? z5FMuakuDwI)!y`1QTITDbbCr#ckP z&sun<#tw?#RKM@;-cM_O%Za+3d`{)wDSa8G&Cary-DRKcvggS7<|c^Ry!uo+O1@I2 z`}Ff+f}l**Q3a}0Lce)D+d*7C3{~UEW#G1fvt(4FNt?U?z7>2m{l1L1s-vdZQ=+TU z9WwG$!w02j*Avd&6bxP=dSZHr&Jl8|>KRpTY6^HKq$wu$jhavUHm%>Vag!R;UJ4H- zu3?NV`6{}pzCyKI*Z9UvZ)s2I5$!r4pLELB27~)``5CwNkDKsxbox^ia#XLv+gVf&Qjnk1*OE;D0ZVxrZmzFdaZY%VvS%e;d)b9RFr z1f9WBxtDcEaWXY3u7x=^aZK?k*Q)JirU;blrvqXZD<|_6;&d=3{LK7xvPP1=ka-?) zI(fB+OYU1_nIC82t8_)Ja%OA7FcqVml^-vItMaqqNjYYw)x8dGwCnpFnvlozy;V;$ z*8l(ic-=mcB}s9bm{jd&Yd=T(W!kUO{&MZF(Edv8*J-~+`>on<*S;~(P%c6HPVJ{? zKUMqL+RxE`nf9x+zg+t*y8YYVG}DdzuhR=_lP6BR!I?DOTUp|-bdF0Nm7Fqkc%`}< zzF}UW5kpLTWD2L=M;kWdMT_CW9?K4m<{t6UA-)d_4iCo|7Qc7L$A;vhJ3h{^ z_^Ug=Vyu=GrKJTq#o;TZE;_Au5R>*X6S1tbRj=#w0=rMdqi-v;RxQa346qD{w)JzmRha%}I>P_(3 z5p4BGWUHG`>~it@ZnK@RE-66Ek2m_u}r+_|`V_{xZ`uDl|St<1g3vTJTcN zx*;Y)?FHcSCmQd(*2F)fJ3{$RmgL*IS0})|xYbrV$>zOUv*+?7D;+iI&u&?r4w+r1~cMmW7kfc0Dz{LAa&nVRR99`de8vk9Le%(zb zLanoN`6G>YjyLhvy7{YCc#ZGS>1?*}H=776U-^)Slt1+r6R+0Jxs0dYf=_Unc(oqR zWzK2Fo;W*xp|iiub2-CW+K!#g$Ih^*WLI())(^IolIbGgLA`%JuAbLR3_ z8eb-YhEr?D_^)bw)nf{6s5M|NH-eXPrs#57jo zGkvw@$)yv#qJwTniytOweCq)-e5UCcGcCLhU#0OC8lUs2iKyaUTn)bD>->v(uhuiU z{FM%07m;tX4sZ4M&zRASZ7O`CvCU}`K3?P7bUG_^Iu?D7g71kwy<&UDUvwJ$z|-JU zPJ_Pzywta)!7QNFolMdA_C^!0*0Q*q0bbGn3lpDedd7n~{PJcKq1LRp{EiNvs>4_5 z@XK`gs&yv9TC@7S#wTnt@z%Okv&LKFCyUM>sPq$!_6Rx))qA4nJ{A64V`WemB+Tn6 z{2`U^spEm;I^W3tv{#(a=hXf>0le(vjT|RjrNSqM`a7#Q!xaA1e!Lt;iehK3;j}`U zemddLr5%?KGViUPdzr?s)c6THT8qMWPebl@>+p4IlN$O|2Z_Nzk@Q=yG7)Z#KM%Z9 zUx$0cz@Ni-qcx&kGIct2|1?8Cp(7P*d~3al(A|LXgu;iXY+gSP{v5^$Cv^IVk8*G6 zbaLJ@@6XZr9j8gBU5BsI^wH*$F$y|MeN!UnHqXNU+)P;QE#XqWb0pyt2Ag8AD|5bTE2grBOzXV;l&S?jmO8oykp11Bl> zl*T((nfI1`{b!A5%eZnDeZI89YdX-gDrYA_p+kPSU zT*hJW_tY*<@J=&mxBnYdc)L-h%d=iTy%YSE-9nl-r8@j_T~3SsMqas}H)lE3E6>RH zmHTEDR``A89)GbXuc)-dS7CTO`K6v&MWx={B2T`*w7kNTTRG2=m)MJZeqVm_*pxBD zdk*9&C@d-TGhZUdrT!4kH_u<5D@n)`Z=t^{wE3j8pg_`8PdJgl@2Qwy>@6+o3Rax^pwBa# z7AnpyQTZ8!E%(jwl~?#Y{#cd7Dl4df&@L3otbrA^Tjjz?;qm5HKzvO=U0z^Hprt;!g z22U%_*sGG?Hp;#8vd1k?9`SU~gu*!yTp|Ig~JN0QnE`nIm#YdrNMY#`m zWzY>fLQ;`J!px zM~oRU(y*TDR5@HjN@3{gM_@GtBd`{Kgq6WtT&Sv~#L>#)lmhTl&zO-$eqjaeh_IUR zDR+o8g{Z19%8VE}Vw9oui4_8(X#Em~5#>|Clf1G@Pk}F&BRG6=o10(cD<3(c>&i3B zSK=!#3_bD88#~5RQQ;e5l;_U%6q~IveBAKst*9zM?wolM0mhsXVD#`2o;gJ$Mvw8# zDJdz8jBY+v|ME0ce_g;(a8-McXVM+6taMNM)HILBP>NY}c15XYHhc~Tc+&2f>dMNP z2zK(+>7I1Aj^Iwa1JtyviB{CfnYT@FWqNL#H0jRtX`X4W37P30V+yCJAQ8o$IX?75 zp?`i^>0J7fd`}5FWR9;wp3#046`X_OnW}L2qR`W{N+>HY&Ew&JnoNdyCW=xIbTK|( zYU5gu=8Wl6r_pIu%$x9gx5PsX$?*vRS&sl4=r)0Cui zPfRHU$pRmLv*8fN6K2ob#2O~YtL>&`t&t#u_B!5}gy zpiCS`Bz>41TttihhplH(Mzvm8Axti{=uZ0K&rC%Zj*3u{;g52QOmErPD);gEvYqQ-7&RkG; z=i-)+)0JBJTkF7<|CtVM#f!}UR-R+Bh01TOn^}H4^P1wACNgY*72lfgyAzxQvGTXp zoh{#5XC{bpl18|j!Hul>TJf!QILn`=16$>{!dd=op354tb#JZLS-!R25Lte!40E}V zw5|Box}N1*>(*BOk@a7w<4@2DTI=AJpQIC(_%c@z8Gk7_p$XGfrgL`pt^EKRdrJE+ z=0SLTr;cy=Rq`;*S@Q{&FY}M#@vU`7%eTT=Nm%!m|C5OL*7~I7mz^R1=Of}<>u#2R z*GemA)3@@qu)m9lZ>@J)zBLbHg}2(#x_?Q>x7y!m)lalvXHC3mbgK3(?^Q0OidKAU zUexmCo$_!OnSUb>!{b})td@U5gfq-p{9~bHdYRP)C4+*q)(JcMn;A!jx9%-}J8>Lg z@k8sjvOtHk!l|3Edkg;wIN@EbCPM4G@j8B;uD{5iI4hpjjz0h=L9F=JI&ibYj5t;z zg*hv}<+t-JJbq}sIKhnH5*Al3t$3DyEF!)&58k5VC!Hp~g_h}eNy{p~wcgvI<6HSy z;Un8WhGI%dQ>*~mx8h$Lfj4Vq#*s&IZ}DDu(4=$BkR4h8WZzH`V3o(x-%Njo${rry isTWY5jNm^je=DqrhgEkAC;soRF+*xL3_XgB|9=2J-D17~ literal 61176 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 delta 814 zcmXw1Z%9*76hH6YJzeUY!%f5{z9JVA!YIRn>6=Aiun!|F78JVXViVIb2WeP5(+`_O z`nXXdrZKSdeW_S))GP%Q<0w!%p^CkZf^1`u`5+ zv=H4CvSh=+7D=>z0k0kaP%6+aC;)M^1hpE~I|8r+t&yFfvbe$-Q-w-4ON~xT&6}8+ z$PW_-Kb~0snfb9F^CVPl5)%Sb0aNsW&_vlPl@RY`rG`dFJ3eJZe*-En%nNa;MJ@1A zfRvE-8%7VY2u_o8g^YC)eV4U?k9D+6x<-m2!X4L);N?~Xu(pX%!7(Y)Vk@+A=Gpf4 zZ5(cSwa)#A$HHdtrKMJ@;Nq)q!g5>_`f@qu{g&ZbT|huQ*nLPFieqZ!J5*%r9tjiq zl6-%+3xYvS+RLS+aGx-G(}0O~Yp3jLLon- zcaR_Q+#|SO{vFL&wOgO>xX#;Kw8U|p_v-PtI?t!oktKn3jW&y3i9i|tGma9sb_Y*p zrd3kLo{dd!{W@7{&ra39OisV{to7{fxwYn@%GRMaps=9V_+o9XQPfO_%iM(y0y zFS)9=sYjXGjvHdhMCxd#V}hL@QzZ8NLznuzW@$OKm@Bf)r^BBk4}!m6B$7`~pL(@$ GH~kMGG0J!V diff --git a/native/src/main/resources/macos_arm64/velocity-compress.dylib b/native/src/main/resources/macos_arm64/velocity-compress.dylib deleted file mode 100755 index 333c62e6e26a0f2c6f20633856b3d4685a829292..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 86468 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 From f67d85493ee709a52f3ee8cac4ece56af78afade Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 31 Aug 2024 20:47:25 -0400 Subject: [PATCH 04/38] update the native README --- native/README.md | 66 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/native/README.md b/native/README.md index d4efcf187..913221f3c 100644 --- a/native/README.md +++ b/native/README.md @@ -1,31 +1,55 @@ # velocity-natives -This directory contains native acceleration code for Velocity, along with -traditional Java fallbacks. +This directory contains native acceleration code for Velocity, along with traditional Java fallbacks. -## Compression +Compression is based on the `libdeflate` library, which is wire-compatible with zlib but significantly faster. -* **Supported platforms**: Linux x86_64 and aarch64, with Java 11 `ByteBuffer` API support as a fallback. - Compiled on CentOS 7. -* **Rationale**: Using a native zlib wrapper, we can avoid multiple trips into Java just to copy memory around. +Encryption is based on OpenSSL for Linux, which is the most widely-used encryption library in the world. +OpenSSL has had several different ABIs over the years, so we provide multiple versions of the native +library. Currently we compile against OpenSSL 1.1.x and OpenSSL 3.x.x. For macOS, we use the built-in +CommonCrypto library. -## Encryption +## Supported Platforms -* **Supported platforms**: Linux x86_64 (OpenSSL 1.0.x and OpenSSL 1.1.x) and aarch64 (OpenSSL 1.1.x only) -* **Rationale**: Using a C library for encryption means we can limit memory copies. Prior to Java 7, this was the only - way to use AES-NI extensions on modern processors, but this is less important since JDK 8 has native support. -* OpenSSL is not included in Velocity. Every distribution provides it now. To deal with ABI incompatibilities, - the native library (which only calls into OpenSSL and contains no cryptographic code) are available for - CentOS 7 (OpenSSL 1.0.0-based), Debian 9 (OpenSSL 1.1.0-based) and Debian Bookworm (OpenSSL 3.0.0-based) - to provide the widest, most reasonable compatibility with most modern distributions. +`velocity-natives` is built for the following platforms: -## OS support +- Linux x86_64 +- Linux aarch64 +- macOS aarch64 ("Apple Silicon") -The natives intend to have the widest possible range of compatibility with modern Linux distributions -(defined as those being released in or after 2014). +For Linux platforms, we provide two versions of the native library: one built against OpenSSL 1.1.x and one built against OpenSSL 3.x.x. +All native libraries are built on various versions of Ubuntu: -In theory, these libraries can be compiled for any Unix-like system (in the past, we supported macOS), -but interest in other systems is minimal at best, thus we focus on Linux x86_64 and aarch64 as they -are commonly used platforms. +- Ubuntu 20.04 for OpenSSL 1.1.x support and for compression +- Ubuntu 22.04 for OpenSSL 3.x.x support -Alpine Linux support is on a "best-effort" basis only. Using `apk add libc6-compat` may enable native support. \ No newline at end of file +Currently, we do not provide native libraries for distributions based on musl libc, like Alpine Linux. You might be able to use `apk add libc6-compat` to fake it, but this is not officially supported. +In the future we may provide a musl libc build. + +## Building + +### On Linux + +To build the native libraries, you need to have Docker installed and have it set up to perform [multi-platform builds](https://docs.docker.com/build/building/multi-platform/). Then, run the following command: + +```bash +./build-support/build-all-linux-natives.sh +``` + +This will build the native libraries for both OpenSSL 1.1.x and OpenSSL 3.x.x on both x86_64 and aarch64. + +### On macOS + +To build the native libraries on macOS, you need to have `cmake` installed. You can install it using Homebrew: + +```bash +brew install cmake +``` + +Then, run the following command: + +```bash +./build-support/compile-macos.sh +``` + +This will build the native libraries for macOS aarch64. x86_64 has not been tested, but it should work. \ No newline at end of file From f8c3a1a5cbd33e5577ebb7ebce119856dea57c0c Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 31 Aug 2024 20:52:33 -0400 Subject: [PATCH 05/38] update the native README with other OS instructions --- native/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/native/README.md b/native/README.md index 913221f3c..327edfdc7 100644 --- a/native/README.md +++ b/native/README.md @@ -52,4 +52,15 @@ Then, run the following command: ./build-support/compile-macos.sh ``` -This will build the native libraries for macOS aarch64. x86_64 has not been tested, but it should work. \ No newline at end of file +This will build the native libraries for macOS aarch64. x86_64 has not been tested, but it should work. + +### On any other operating system? + +If your OS of choice is a Unix of some sort, you can use the individual Linux build scripts as a base: + +- `build-support/compile-linux-compress.sh` +- `build-support/compile-linux-crypto.sh` + +You will need to have the necessary build tools installed (a C/C++ toolchain and `cmake`), and you will need to have OpenSSL installed. You will also need to adjust the script to your needs. + +If your OS of choice is Windows, you're on your own. It should be possible, but we don't provide any support for it. \ No newline at end of file From 956fca142c60824039cf71ca59f4706f6ba3ec6b Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 31 Aug 2024 22:26:11 -0400 Subject: [PATCH 06/38] Readd accidentally removed macOS arm64 compression native --- native/build-support/compile-macos.sh | 2 +- .../macos_arm64/velocity-compress.dylib | Bin 0 -> 103384 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100755 native/src/main/resources/macos_arm64/velocity-compress.dylib diff --git a/native/build-support/compile-macos.sh b/native/build-support/compile-macos.sh index 7120cbe5f..04e11d60e 100755 --- a/native/build-support/compile-macos.sh +++ b/native/build-support/compile-macos.sh @@ -11,7 +11,7 @@ fi echo "Compiling libdeflate..." cd libdeflate || exit -cmake -B build && cmake --build build --target libdeflate_static +rm -rf build && cmake -B build && cmake --build build --target libdeflate_static cd .. CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/darwin/ -fPIC -shared -Wall -Werror -fomit-frame-pointer" diff --git a/native/src/main/resources/macos_arm64/velocity-compress.dylib b/native/src/main/resources/macos_arm64/velocity-compress.dylib new file mode 100755 index 0000000000000000000000000000000000000000..5ccce7c833353b074dc9579b48c400e6cf509104 GIT binary patch literal 103384 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

Rb;cT_wN}S`_kY=1oxgqg$GQL1 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 z zGxG1$1z%ltUp@T3`YPK%-?$uakuOy>AChPO`h9?^*>5M@u3Y}rjq_Sp41t2Jdy&(ED$fGGI|YMGYdt7Uy0*R1h*zKm2g zgFL3Hjoiq{@fBRtU1jH}*NJX^aUjtZ^WR82b=>5O0NQ!ApexNRRT&9y>|b68f`R zA<5Dg_&kMAyV@_R*#9?4@}qZ2(vag3dw{CaZ=U+QA;%{B#rV`?_F8hhXjegA^$(OZ zPQ9b6I#qpb8!r{h)L~YBUTThSiZ4ekvr9dp{^S=-OVz80w+L}o*r(@YrKPGJcDpK` zK%GeRPVq|cR08FvXBADGn183Q0F~!?i?XKp3hP9jtlt|$4C-e7A%~%kkL*R61^IUl znwsvLScDAfDe7PGgrOcs^XnY*wV@%654jgL*wduA{rlZcwFp_G?bjMR2EN~Jv#Ta( zy?wt{lYBJfkbQOYkh$8(urt9&1I}p8=hM7&^AK@0py}AI?3Jedp=85; z#I!e0Gwk1+_KDLCdkgA{#1}H(uy-@<*DWyYgG~GK)rNhnY2WjhVV`5#J!=j7O4DAo z&aiJa?G;ZM_76>ax3>)Yx2AphyM{f2E=S~b z`0d{q_WMoy$ELm9wExqz?=kHy&KUkrnD$PlJpcj~N&iXHex+$|e%A2c&$MTn_7SFi zVvXUx%(Txn?dwhZe$&3qwBK~j@Gs@uko+&|Y1nTt?L$rbBGbOyw7+B8t4w<9hsru{3^KK4Gt-Hm_}|54MuqiK&e%PY~eum05V zpCfjMQVw&xSZLanY2RS_&oSeF&9v8;_3?yhUuMSVq=S+4i~eTB-_f*xWct6s?{3QD zGwr8L_xnuyepCMS{_rP^_;&c+s}1`RfA}v9`%k95%5-l<-4J<{so&1nvC19YTwBjR zte$;rJ^T22_S|~*$@T0r>)Gekv;Ph$t_A&o2gHK};J+{3NPRbMsc4n@Z+3?Mp-~_F zerqK9!24Sx@sHd8Zan@W{x{{wAIWdZkw20PIjS}8c+I&$YVCVD#*D#`{4x&y`BEJx znO*qDP)5nR5!Q8-iOi+$7f0#K2Cwa0KLSQkeR3fZ8xxk=(feI%PiX47>tnEA`Z}`t z2WY>eIp?@s_=^%=HinDN^~Kl4_9sv;^Sax`p!HlYKXIuo+-0XQwLR#Q8Gqt+1h3_s;{$Mq&SzhO>q)J%Uh{1;~Umz3f-Z()(Iut=p|;xe8(l)>}DB@Q{> z=|6Y7`(?c6`Md?G`OM`I^K#Dlc|42w6B(B?xiB*|jb_5rjsIq@H`|w*IeFZ;TyGvt zE2EZwX8TfPlX;l%d&u;8GkmnWjQmtzUWO4D4@kb+G1iueiEY7npFBtv6<$nJ7dUIW zFV#yNmzBz&Zc)KxA5zuU;kYaUT{@31>p&oiQ-TT|-#x6PF6-B#-Rk3(MJL1!03ynl|; zn0w5!?3)x%XNspg+$MriKuo04mW4&zRES~ywf_}+{672-QJwvLQkRTrR2X%vY}r-M zwLChqsN!9<@x}G2TZV_6{UYlc&0}j8^V8yX2g_$Y^Zcm%-N(nh67aVJ2eNH9jtV?~ z*XFyX-}}K|O9n6K^wh7p|E2BCoxZep|KZN9CubiyRvlIL)c)i5y->9KcbxlzlPjDPsQV$ZEU_4mJRE`0gyz!fWQc>9lorgZ9l`@$E- zy!HN`^t3})JsX^!TQKeYJ6jBPK5%SQ-|p9JJvREmQ$PHea&}wP$st?9_qD(3^{Oj+ z|IpMOt7^!It!`u^Cp_rgvY4@Lj<@sJyOyf);Ww{FW#&i^`g zX=uu0kEC@^jO%kGZsyFeFTLv*Z7Gas@!b=B4kWH0@ZpC?8tnY=_O{>2;{pF6|+0wfOJ#yF7 h8><3clV=V1U6WN4W4dkl Date: Mon, 2 Sep 2024 02:25:04 -0400 Subject: [PATCH 07/38] Add a `MinecraftVarintLengthCompositeEncoder` for backend connections (#1419) This is an optimization that has been in BungeeCord for some time, but it's an idea that makes a lot of sense: avoid a memory copy when we don't need to do one. The OS will hopefully be smart enough to use `writev()` or similar for this. --- .../network/BackendChannelInitializer.java | 4 +- ...MinecraftVarintLengthCompositeEncoder.java | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java b/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java index 1e6f387b3..f6fb43ea6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java @@ -30,7 +30,7 @@ import com.velocitypowered.proxy.protocol.netty.AutoReadHolderHandler; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; -import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthCompositeEncoder; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.handler.timeout.ReadTimeoutHandler; @@ -55,7 +55,7 @@ public class BackendChannelInitializer extends ChannelInitializer { .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.MILLISECONDS)) - .addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE) + .addLast(FRAME_ENCODER, MinecraftVarintLengthCompositeEncoder.INSTANCE) .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolUtils.Direction.CLIENTBOUND)) .addLast(FLOW_HANDLER, new AutoReadHolderHandler()) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java new file mode 100644 index 000000000..18679b161 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018-2023 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.netty; + +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; + +/** + * Handler for appending a length for Minecraft packets using composite buffers. + */ +@ChannelHandler.Sharable +public class MinecraftVarintLengthCompositeEncoder extends MessageToMessageEncoder { + + public static final MinecraftVarintLengthCompositeEncoder INSTANCE = new MinecraftVarintLengthCompositeEncoder(); + + private MinecraftVarintLengthCompositeEncoder() { + } + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf buf, + List list) throws Exception { + ByteBuf varIntBuffer = ctx.alloc().ioBuffer(ProtocolUtils.varIntBytes(buf.readableBytes())); + ProtocolUtils.writeVarInt(varIntBuffer, buf.readableBytes()); + list.add(varIntBuffer); + list.add(buf.retain()); + } +} From a7654af1a3c5885c96769a05d43a5604dc47de10 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 2 Sep 2024 02:57:03 -0400 Subject: [PATCH 08/38] Do some minor natives housekeeping. --- .../natives/compression/CompressorUtils.java | 14 --------- .../natives/util/MoreByteBufUtils.java | 31 ++++++------------- .../natives/util/NativeConstraints.java | 2 -- 3 files changed, 10 insertions(+), 37 deletions(-) diff --git a/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java b/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java index 8230ba204..94cde4bd5 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java @@ -26,20 +26,6 @@ class CompressorUtils { */ static final int ZLIB_BUFFER_SIZE = 8192; - /** - * Ensures that the buffer does not go over {@code max}. - * - * @param buf the buffer for check - * @param max the maximum size for the buffer - * @throws DataFormatException if the buffer becomes too bug - */ - static void ensureMaxSize(ByteBuf buf, int max) throws DataFormatException { - int len = buf.readableBytes(); - if (len > max) { - throw new DataFormatException("Got too much data (" + len + " > " + max + ")"); - } - } - private CompressorUtils() { throw new AssertionError(); } diff --git a/native/src/main/java/com/velocitypowered/natives/util/MoreByteBufUtils.java b/native/src/main/java/com/velocitypowered/natives/util/MoreByteBufUtils.java index fbad62bf9..13f4f0c57 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/MoreByteBufUtils.java +++ b/native/src/main/java/com/velocitypowered/natives/util/MoreByteBufUtils.java @@ -52,18 +52,13 @@ public class MoreByteBufUtils { private static boolean isCompatible(Native nativeStuff, ByteBuf buf) { BufferPreference preferred = nativeStuff.preferredBufferType(); - switch (preferred) { - case DIRECT_PREFERRED: - case HEAP_PREFERRED: + return switch (preferred) { + case DIRECT_PREFERRED, HEAP_PREFERRED -> // The native prefers this type, but doesn't strictly require we provide it. - return true; - case DIRECT_REQUIRED: - return buf.hasMemoryAddress(); - case HEAP_REQUIRED: - return buf.hasArray(); - default: - throw new AssertionError("Preferred buffer type unknown"); - } + true; + case DIRECT_REQUIRED -> buf.hasMemoryAddress(); + case HEAP_REQUIRED -> buf.hasArray(); + }; } /** @@ -77,15 +72,9 @@ public class MoreByteBufUtils { */ public static ByteBuf preferredBuffer(ByteBufAllocator alloc, Native nativeStuff, int initialCapacity) { - switch (nativeStuff.preferredBufferType()) { - case HEAP_REQUIRED: - case HEAP_PREFERRED: - return alloc.heapBuffer(initialCapacity); - case DIRECT_PREFERRED: - case DIRECT_REQUIRED: - return alloc.directBuffer(initialCapacity); - default: - throw new AssertionError("Preferred buffer type unknown"); - } + return switch (nativeStuff.preferredBufferType()) { + case HEAP_REQUIRED, HEAP_PREFERRED -> alloc.heapBuffer(initialCapacity); + case DIRECT_PREFERRED, DIRECT_REQUIRED -> alloc.directBuffer(initialCapacity); + }; } } diff --git a/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java b/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java index f707ab248..04e5faff0 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java +++ b/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java @@ -39,8 +39,6 @@ public class NativeConstraints { } String osArch = System.getProperty("os.arch", ""); - // HotSpot on Intel macOS prefers x86_64, but OpenJ9 on macOS and HotSpot/OpenJ9 elsewhere - // give amd64. IS_AMD64 = osArch.equals("amd64") || osArch.equals("x86_64"); IS_AARCH64 = osArch.equals("aarch64") || osArch.equals("arm64"); } From 862036d42466cdd1f6bf00db0e3efe12d97d9e0c Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 2 Sep 2024 03:00:19 -0400 Subject: [PATCH 09/38] Fix a compile error. --- .../velocitypowered/natives/compression/CompressorUtils.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java b/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java index 94cde4bd5..3cfe0e671 100644 --- a/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java +++ b/native/src/main/java/com/velocitypowered/natives/compression/CompressorUtils.java @@ -17,9 +17,6 @@ package com.velocitypowered.natives.compression; -import io.netty.buffer.ByteBuf; -import java.util.zip.DataFormatException; - class CompressorUtils { /** * The default preferred output buffer size for zlib. From 52ae735ea32911ab4e3e145999a6408072bf663e Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 2 Sep 2024 20:47:01 -0400 Subject: [PATCH 10/38] Change `LoginInboundConnection` queue type to something that is actually thread-safe --- .../proxy/connection/client/LoginInboundConnection.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java index 93064a790..837376f95 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java @@ -30,9 +30,9 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.net.InetSocketAddress; -import java.util.ArrayDeque; import java.util.Optional; import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import net.kyori.adventure.text.Component; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -58,7 +58,7 @@ public class LoginInboundConnection implements LoginPhaseConnection, KeyIdentifi InitialInboundConnection delegate) { this.delegate = delegate; this.outstandingResponses = Int2ObjectSyncMap.hashmap(); - this.loginMessagesToSend = new ArrayDeque<>(); + this.loginMessagesToSend = new ConcurrentLinkedQueue<>(); } @Override From f034c0277d60e9f0a80e484832aec831cbc298f7 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 3 Sep 2024 00:33:57 -0400 Subject: [PATCH 11/38] Have one `MinecraftVarintLengthEncoder`, not two Applies more to the margins (such as login phase and server list ping), but every bit does help. --- .../network/BackendChannelInitializer.java | 6 ++-- .../netty/MinecraftVarintLengthEncoder.java | 31 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java b/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java index f6fb43ea6..d60c64775 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/BackendChannelInitializer.java @@ -30,7 +30,7 @@ import com.velocitypowered.proxy.protocol.netty.AutoReadHolderHandler; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder; -import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthCompositeEncoder; +import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.handler.timeout.ReadTimeoutHandler; @@ -49,13 +49,13 @@ public class BackendChannelInitializer extends ChannelInitializer { } @Override - protected void initChannel(Channel ch) throws Exception { + protected void initChannel(Channel ch) { ch.pipeline() .addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder()) .addLast(READ_TIMEOUT, new ReadTimeoutHandler(server.getConfiguration().getReadTimeout(), TimeUnit.MILLISECONDS)) - .addLast(FRAME_ENCODER, MinecraftVarintLengthCompositeEncoder.INSTANCE) + .addLast(FRAME_ENCODER, MinecraftVarintLengthEncoder.INSTANCE) .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolUtils.Direction.CLIENTBOUND)) .addLast(FLOW_HANDLER, new AutoReadHolderHandler()) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java index ecbcc8b84..115d8b472 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java @@ -23,33 +23,34 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.handler.codec.MessageToMessageEncoder; +import java.util.List; /** * Handler for appending a length for Minecraft packets. */ @ChannelHandler.Sharable -public class MinecraftVarintLengthEncoder extends MessageToByteEncoder { +public class MinecraftVarintLengthEncoder extends MessageToMessageEncoder { public static final MinecraftVarintLengthEncoder INSTANCE = new MinecraftVarintLengthEncoder(); - public static final boolean IS_JAVA_CIPHER = Natives.cipher.get() == JavaVelocityCipher.FACTORY; + + static final boolean IS_JAVA_CIPHER = Natives.cipher.get() == JavaVelocityCipher.FACTORY; private MinecraftVarintLengthEncoder() { } @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { - ProtocolUtils.writeVarInt(out, msg.readableBytes()); - out.writeBytes(msg); - } + protected void encode(ChannelHandlerContext ctx, ByteBuf buf, + List list) throws Exception { + final int length = buf.readableBytes(); + final int varintLength = ProtocolUtils.varIntBytes(length); - @Override - protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) - throws Exception { - int anticipatedRequiredCapacity = ProtocolUtils.varIntBytes(msg.readableBytes()) - + msg.readableBytes(); - return IS_JAVA_CIPHER - ? ctx.alloc().heapBuffer(anticipatedRequiredCapacity) - : ctx.alloc().directBuffer(anticipatedRequiredCapacity); + final ByteBuf lenBuf = IS_JAVA_CIPHER + ? ctx.alloc().heapBuffer(varintLength) + : ctx.alloc().directBuffer(varintLength); + + ProtocolUtils.writeVarInt(lenBuf, length); + list.add(lenBuf); + list.add(buf.retain()); } } From 46f29480bd62a556976a728246ccdd6fab03c774 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Tue, 3 Sep 2024 00:40:31 -0400 Subject: [PATCH 12/38] Delete now-unused `MinecraftVarintLengthCompositeEncoder` --- ...MinecraftVarintLengthCompositeEncoder.java | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java deleted file mode 100644 index 18679b161..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthCompositeEncoder.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2018-2023 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.velocitypowered.proxy.protocol.netty; - -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageEncoder; -import java.util.List; - -/** - * Handler for appending a length for Minecraft packets using composite buffers. - */ -@ChannelHandler.Sharable -public class MinecraftVarintLengthCompositeEncoder extends MessageToMessageEncoder { - - public static final MinecraftVarintLengthCompositeEncoder INSTANCE = new MinecraftVarintLengthCompositeEncoder(); - - private MinecraftVarintLengthCompositeEncoder() { - } - - @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf buf, - List list) throws Exception { - ByteBuf varIntBuffer = ctx.alloc().ioBuffer(ProtocolUtils.varIntBytes(buf.readableBytes())); - ProtocolUtils.writeVarInt(varIntBuffer, buf.readableBytes()); - list.add(varIntBuffer); - list.add(buf.retain()); - } -} From 784806848d486fd3c06c3f63a8c54483361b9241 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Thu, 5 Sep 2024 00:00:34 -0400 Subject: [PATCH 13/38] Do more speculative VarInt reading optimizations (#1418) --- .../proxy/protocol/ProtocolUtils.java | 75 ++++------ .../netty/MinecraftVarintFrameDecoder.java | 130 +++++++++++++----- .../protocol/netty/VarintByteDecoder.java | 68 --------- .../packet/LoginPluginMessagePacket.java | 2 +- .../proxy/protocol/ProtocolUtilsTest.java | 4 +- 5 files changed, 127 insertions(+), 152 deletions(-) delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/VarintByteDecoder.java 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 053dcd65f..797a58223 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -104,6 +104,7 @@ public enum ProtocolUtils { .build(); public static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB + private static final int MAXIMUM_VARINT_SIZE = 5; private static final BinaryTagType[] BINARY_TAG_TYPES = new BinaryTagType[] { BinaryTagTypes.END, BinaryTagTypes.BYTE, BinaryTagTypes.SHORT, BinaryTagTypes.INT, BinaryTagTypes.LONG, BinaryTagTypes.FLOAT, BinaryTagTypes.DOUBLE, @@ -111,13 +112,18 @@ 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[] VARINT_EXACT_BYTE_LENGTHS = new int[33]; + private static final int[] VAR_INT_LENGTHS = new int[65]; static { for (int i = 0; i <= 32; ++i) { - VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d); + VAR_INT_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d); } - VARINT_EXACT_BYTE_LENGTHS[32] = 1; // Special case for the number 0. + VAR_INT_LENGTHS[32] = 1; // Special case for the number 0. + } + + private static DecoderException badVarint() { + return MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad VarInt decoded") + : BAD_VARINT_CACHED; } /** @@ -127,56 +133,29 @@ public enum ProtocolUtils { * @return the decoded VarInt */ public static int readVarInt(ByteBuf buf) { - int read = readVarIntSafely(buf); - if (read == Integer.MIN_VALUE) { - throw MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad VarInt decoded") - : BAD_VARINT_CACHED; + int readable = buf.readableBytes(); + if (readable == 0) { + // special case for empty buffer + throw badVarint(); } - return read; - } - /** - * Reads a Minecraft-style VarInt from the specified {@code buf}. The difference between this - * method and {@link #readVarInt(ByteBuf)} is that this function returns a sentinel value if the - * varint is invalid. - * - * @param buf the buffer to read from - * @return the decoded VarInt, or {@code Integer.MIN_VALUE} if the varint is invalid - */ - public static int readVarIntSafely(ByteBuf buf) { - int i = 0; - int maxRead = Math.min(5, buf.readableBytes()); - for (int j = 0; j < maxRead; j++) { - int k = buf.readByte(); + // we can read at least one byte, and this should be a common case + int k = buf.readByte(); + if ((k & 0x80) != 128) { + return k; + } + + // in case decoding one byte was not enough, use a loop to decode up to the next 4 bytes + int maxRead = Math.min(MAXIMUM_VARINT_SIZE, readable); + int i = k & 0x7F; + for (int j = 1; j < maxRead; j++) { + k = buf.readByte(); i |= (k & 0x7F) << j * 7; if ((k & 0x80) != 128) { return i; } } - return Integer.MIN_VALUE; - } - - /** - * Reads a Minecraft-style VarInt from the specified {@code buf}. The difference between this - * method and {@link #readVarInt(ByteBuf)} is that this function returns a sentinel value if the - * varint is invalid. - * - * @param buf the buffer to read from - * @return the decoded VarInt - * @throws DecoderException if the varint is invalid - */ - public static int readVarIntSafelyOrThrow(ByteBuf buf) { - int i = 0; - int maxRead = Math.min(5, buf.readableBytes()); - for (int j = 0; j < maxRead; j++) { - int k = buf.readByte(); - i |= (k & 0x7F) << j * 7; - if ((k & 0x80) != 128) { - return i; - } - } - throw MinecraftDecoder.DEBUG ? new CorruptedFrameException("Bad VarInt decoded") - : BAD_VARINT_CACHED; + throw badVarint(); } /** @@ -186,7 +165,7 @@ public enum ProtocolUtils { * @return the byte size of {@code value} if encoded as a VarInt */ public static int varIntBytes(int value) { - return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(value)]; + return VAR_INT_LENGTHS[Integer.numberOfLeadingZeros(value)]; } /** @@ -210,6 +189,8 @@ public enum ProtocolUtils { private static void writeVarIntFull(ByteBuf buf, int value) { // See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/ + + // This essentially is an unrolled version of the "traditional" VarInt encoding. if ((value & (0xFFFFFFFF << 7)) == 0) { buf.writeByte(value); } else if ((value & (0xFFFFFFFF << 14)) == 0) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java index 94baa2ffe..7bf7563ea 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java @@ -17,7 +17,8 @@ package com.velocitypowered.proxy.protocol.netty; -import com.velocitypowered.proxy.protocol.netty.VarintByteDecoder.DecodeResult; +import static io.netty.util.ByteProcessor.FIND_NON_NUL; + import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -29,53 +30,114 @@ import java.util.List; */ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { - private static final QuietDecoderException BAD_LENGTH_CACHED = + private static final QuietDecoderException BAD_PACKET_LENGTH = new QuietDecoderException("Bad packet length"); - private static final QuietDecoderException VARINT_BIG_CACHED = + private static final QuietDecoderException VARINT_TOO_BIG = new QuietDecoderException("VarInt too big"); @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) + throws Exception { if (!ctx.channel().isActive()) { in.clear(); return; } - final VarintByteDecoder reader = new VarintByteDecoder(); - - int varintEnd = in.forEachByte(reader); - if (varintEnd == -1) { - // We tried to go beyond the end of the buffer. This is probably a good sign that the - // buffer was too short to hold a proper varint. - if (reader.getResult() == DecodeResult.RUN_OF_ZEROES) { - // Special case where the entire packet is just a run of zeroes. We ignore them all. - in.clear(); - } + // skip any runs of 0x00 we might find + int packetStart = in.forEachByte(FIND_NON_NUL); + if (packetStart == -1) { return; } + in.readerIndex(packetStart); - if (reader.getResult() == DecodeResult.RUN_OF_ZEROES) { - // this will return to the point where the next varint starts - in.readerIndex(varintEnd); - } else if (reader.getResult() == DecodeResult.SUCCESS) { - int readVarint = reader.getReadVarint(); - int bytesRead = reader.getBytesRead(); - if (readVarint < 0) { - in.clear(); - throw BAD_LENGTH_CACHED; - } else if (readVarint == 0) { - // skip over the empty packet(s) and ignore it - in.readerIndex(varintEnd + 1); + // try to read the length of the packet + in.markReaderIndex(); + int preIndex = in.readerIndex(); + int length = readRawVarInt21(in); + if (preIndex == in.readerIndex()) { + return; + } + if (length < 0) { + throw BAD_PACKET_LENGTH; + } + + // note that zero-length packets are ignored + if (length > 0) { + if (in.readableBytes() < length) { + in.resetReaderIndex(); } else { - int minimumRead = bytesRead + readVarint; - if (in.isReadable(minimumRead)) { - out.add(in.retainedSlice(varintEnd + 1, readVarint)); - in.skipBytes(minimumRead); - } + out.add(in.readRetainedSlice(length)); } - } else if (reader.getResult() == DecodeResult.TOO_BIG) { - in.clear(); - throw VARINT_BIG_CACHED; } } + + /** + * Reads a VarInt from the buffer of up to 21 bits in size. + * + * @param buffer the buffer to read from + * @return the VarInt decoded, {@code 0} if no varint could be read + * @throws QuietDecoderException if the VarInt is too big to be decoded + */ + private static int readRawVarInt21(ByteBuf buffer) { + if (buffer.readableBytes() < 4) { + // we don't have enough that we can read a potentially full varint, so fall back to + // the slow path. + return readRawVarintSmallBuf(buffer); + } + int wholeOrMore = buffer.getIntLE(buffer.readerIndex()); + + // take the last three bytes and check if any of them have the high bit set + int atStop = ~wholeOrMore & 0x808080; + if (atStop == 0) { + // all bytes have the high bit set, so the varint we are trying to decode is too wide + throw VARINT_TOO_BIG; + } + + int bitsToKeep = Integer.numberOfTrailingZeros(atStop) + 1; + buffer.skipBytes(bitsToKeep >> 3); + + // remove all bits we don't need to keep, a trick from + // https://github.com/netty/netty/pull/14050#issuecomment-2107750734: + // + // > The idea is that thisVarintMask has 0s above the first one of firstOneOnStop, and 1s at + // > and below it. For example if firstOneOnStop is 0x800080 (where the last 0x80 is the only + // > one that matters), then thisVarintMask is 0xFF. + // + // this is also documented in Hacker's Delight, section 2-1 "Manipulating Rightmost Bits" + int preservedBytes = wholeOrMore & (atStop ^ (atStop - 1)); + + // merge together using this trick: https://github.com/netty/netty/pull/14050#discussion_r1597896639 + preservedBytes = (preservedBytes & 0x007F007F) | ((preservedBytes & 0x00007F00) >> 1); + preservedBytes = (preservedBytes & 0x00003FFF) | ((preservedBytes & 0x3FFF0000) >> 2); + return preservedBytes; + } + + private static int readRawVarintSmallBuf(ByteBuf buffer) { + if (!buffer.isReadable()) { + return 0; + } + buffer.markReaderIndex(); + + byte tmp = buffer.readByte(); + if (tmp >= 0) { + return tmp; + } + int result = tmp & 0x7F; + if (!buffer.isReadable()) { + buffer.resetReaderIndex(); + return 0; + } + if ((tmp = buffer.readByte()) >= 0) { + return result | tmp << 7; + } + result |= (tmp & 0x7F) << 7; + if (!buffer.isReadable()) { + buffer.resetReaderIndex(); + return 0; + } + if ((tmp = buffer.readByte()) >= 0) { + return result | tmp << 14; + } + return result | (tmp & 0x7F) << 14; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/VarintByteDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/VarintByteDecoder.java deleted file mode 100644 index 06cec7350..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/VarintByteDecoder.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2020-2021 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.velocitypowered.proxy.protocol.netty; - -import io.netty.util.ByteProcessor; - -class VarintByteDecoder implements ByteProcessor { - - private int readVarint; - private int bytesRead; - private DecodeResult result = DecodeResult.TOO_SHORT; - - @Override - public boolean process(byte k) { - if (k == 0 && bytesRead == 0) { - // tentatively say it's invalid, but there's a possibility of redemption - result = DecodeResult.RUN_OF_ZEROES; - return true; - } - if (result == DecodeResult.RUN_OF_ZEROES) { - return false; - } - readVarint |= (k & 0x7F) << bytesRead++ * 7; - if (bytesRead > 3) { - result = DecodeResult.TOO_BIG; - return false; - } - if ((k & 0x80) != 128) { - result = DecodeResult.SUCCESS; - return false; - } - return true; - } - - public int getReadVarint() { - return readVarint; - } - - public int getBytesRead() { - return bytesRead; - } - - public DecodeResult getResult() { - return result; - } - - public enum DecodeResult { - SUCCESS, - TOO_SHORT, - TOO_BIG, - RUN_OF_ZEROES - } -} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java index 213492cbf..682785eb2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java @@ -63,7 +63,7 @@ public class LoginPluginMessagePacket extends DeferredByteBufHolder implements M @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - this.id = ProtocolUtils.readVarIntSafelyOrThrow(buf); + this.id = ProtocolUtils.readVarInt(buf); this.channel = ProtocolUtils.readString(buf); if (buf.isReadable()) { this.replace(buf.readRetainedSlice(buf.readableBytes())); 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 f476de67c..1ed59cd83 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java @@ -70,7 +70,7 @@ public class ProtocolUtilsTest { private void writeReadTestOld(ByteBuf buf, int test) { buf.clear(); writeVarIntOld(buf, test); - assertEquals(test, ProtocolUtils.readVarIntSafely(buf)); + assertEquals(test, ProtocolUtils.readVarInt(buf)); } @Test @@ -103,7 +103,7 @@ public class ProtocolUtilsTest { "Encoding of " + i + " was invalid"); assertEquals(i, oldReadVarIntSafely(varintNew)); - assertEquals(i, ProtocolUtils.readVarIntSafely(varintOld)); + assertEquals(i, ProtocolUtils.readVarInt(varintOld)); varintNew.clear(); varintOld.clear(); From 525ac2712e8e090ed7f1366fe0bc499f3b6f5b74 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 7 Sep 2024 12:56:31 -0400 Subject: [PATCH 14/38] Also provide musl libc builds of Velocity natives Reference: PaperMC/Paper#11367 --- native/README.md | 6 +-- native/build-support/alpine.Dockerfile | 9 +++++ .../build-support/build-all-linux-natives.sh | 11 ++--- .../build-support/compile-linux-compress.sh | 8 +++- native/build-support/compile-linux-crypto.sh | 8 ++-- .../natives/util/NativeConstraints.java | 38 ++++++++++++++++-- .../velocitypowered/natives/util/Natives.java | 20 ++++++++- .../velocity-cipher-ossl30x-musl.so | Bin 0 -> 71528 bytes .../linux_aarch64/velocity-compress-musl.so | Bin 0 -> 75752 bytes .../velocity-cipher-ossl30x-musl.so | Bin 0 -> 17232 bytes .../linux_x86_64/velocity-compress-musl.so | Bin 0 -> 103696 bytes 11 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 native/build-support/alpine.Dockerfile create mode 100755 native/src/main/resources/linux_aarch64/velocity-cipher-ossl30x-musl.so create mode 100755 native/src/main/resources/linux_aarch64/velocity-compress-musl.so create mode 100755 native/src/main/resources/linux_x86_64/velocity-cipher-ossl30x-musl.so create mode 100755 native/src/main/resources/linux_x86_64/velocity-compress-musl.so diff --git a/native/README.md b/native/README.md index 327edfdc7..81e4e3b6c 100644 --- a/native/README.md +++ b/native/README.md @@ -18,13 +18,11 @@ CommonCrypto library. - macOS aarch64 ("Apple Silicon") For Linux platforms, we provide two versions of the native library: one built against OpenSSL 1.1.x and one built against OpenSSL 3.x.x. -All native libraries are built on various versions of Ubuntu: +All native libraries are built on various versions of Ubuntu and Alpine: - Ubuntu 20.04 for OpenSSL 1.1.x support and for compression - Ubuntu 22.04 for OpenSSL 3.x.x support - -Currently, we do not provide native libraries for distributions based on musl libc, like Alpine Linux. You might be able to use `apk add libc6-compat` to fake it, but this is not officially supported. -In the future we may provide a musl libc build. +- Alpine 3.18 for OpenSSL 3.x.x support and compression (musl libc users only) ## Building diff --git a/native/build-support/alpine.Dockerfile b/native/build-support/alpine.Dockerfile new file mode 100644 index 000000000..c67cd822d --- /dev/null +++ b/native/build-support/alpine.Dockerfile @@ -0,0 +1,9 @@ + +FROM amazoncorretto:17.0.12-alpine3.18 + +# Install required dependencies +RUN apk add --no-cache bash alpine-sdk cmake openssl-dev openssl + +# Create a non-root user +RUN adduser -D user +USER user \ No newline at end of file diff --git a/native/build-support/build-all-linux-natives.sh b/native/build-support/build-all-linux-natives.sh index 32397f591..0e182da69 100755 --- a/native/build-support/build-all-linux-natives.sh +++ b/native/build-support/build-all-linux-natives.sh @@ -6,7 +6,8 @@ set -e cd "$(dirname "$0")/.." || exit 1 ARCHS=(x86_64 aarch64) -BASE_DOCKERFILE_VARIANTS=(ubuntu-focal ubuntu-jammy) +BASE_DOCKERFILE_VARIANTS=(ubuntu-focal ubuntu-jammy alpine) +COMPRESSION_VARIANTS=(ubuntu-focal alpine) for variant in "${BASE_DOCKERFILE_VARIANTS[@]}"; do docker_platforms="" @@ -25,8 +26,8 @@ for arch in "${ARCHS[@]}"; do docker run --rm -v "$(pwd)":/app --platform linux/${arch} velocity-native-build:$variant /bin/bash -c "cd /app && ./build-support/compile-linux-crypto.sh" done - # Use only the oldest variant for the compression library - variant=${BASE_DOCKERFILE_VARIANTS[0]} - echo "Building native compression for $arch on $variant..." - docker run --rm -v "$(pwd)":/app --platform linux/${arch} velocity-native-build:$variant /bin/bash -c "cd /app && ./build-support/compile-linux-compress.sh" + for variant in "${COMPRESSION_VARIANTS[@]}"; do + echo "Building native compression for $arch on $variant..." + docker run --rm -v "$(pwd)":/app --platform linux/${arch} velocity-native-build:$variant /bin/bash -c "cd /app && ./build-support/compile-linux-compress.sh" + done done \ No newline at end of file diff --git a/native/build-support/compile-linux-compress.sh b/native/build-support/compile-linux-compress.sh index b03ca37cf..2222c4a31 100755 --- a/native/build-support/compile-linux-compress.sh +++ b/native/build-support/compile-linux-compress.sh @@ -16,8 +16,14 @@ cd libdeflate || exit rm -rf build && cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -B build && cmake --build build --target libdeflate_static cd .. +# Determine if we are on musl libc or glibc +suffix="" +if ldd --version 2>&1 | grep -q musl; then + suffix="-musl" +fi + CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -Wall -Werror -fomit-frame-pointer" ARCH=$(uname -m) mkdir -p src/main/resources/linux_$ARCH $CC $CFLAGS -Ilibdeflate src/main/c/jni_util.c src/main/c/jni_zlib_deflate.c src/main/c/jni_zlib_inflate.c \ - libdeflate/build/libdeflate.a -o src/main/resources/linux_$ARCH/velocity-compress.so \ No newline at end of file + libdeflate/build/libdeflate.a -o src/main/resources/linux_$ARCH/velocity-compress$suffix.so \ No newline at end of file diff --git a/native/build-support/compile-linux-crypto.sh b/native/build-support/compile-linux-crypto.sh index 221656647..d29a96918 100755 --- a/native/build-support/compile-linux-crypto.sh +++ b/native/build-support/compile-linux-crypto.sh @@ -20,9 +20,11 @@ if [ ! "$CC" ]; then export CC=gcc fi -output_file="velocity-cipher.so" -if [ -n "$OPENSSL_VERSION" ]; then - output_file="velocity-cipher-ossl${OPENSSL_VERSION}.so" +# Determine if we are on musl libc or glibc +suffix="" +if ldd --version 2>&1 | grep -q musl; then + suffix="-musl" + filename=$(echo "$filename" | sed "s/\.so/$suffix.so/") fi CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -Wall -Werror -fomit-frame-pointer" diff --git a/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java b/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java index 04e5faff0..852a1f062 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java +++ b/native/src/main/java/com/velocitypowered/natives/util/NativeConstraints.java @@ -19,6 +19,7 @@ package com.velocitypowered.natives.util; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import java.nio.charset.StandardCharsets; import java.util.function.BooleanSupplier; /** @@ -29,6 +30,8 @@ public class NativeConstraints { private static final boolean IS_AMD64; private static final boolean IS_AARCH64; private static final boolean CAN_GET_MEMORYADDRESS; + private static final boolean IS_LINUX; + private static final boolean IS_MUSL_LIBC; static { ByteBuf test = Unpooled.directBuffer(); @@ -41,17 +44,44 @@ public class NativeConstraints { String osArch = System.getProperty("os.arch", ""); IS_AMD64 = osArch.equals("amd64") || osArch.equals("x86_64"); IS_AARCH64 = osArch.equals("aarch64") || osArch.equals("arm64"); + + IS_LINUX = System.getProperty("os.name", "").equalsIgnoreCase("Linux"); + + // Determine if we're using musl libc by invoking `ldd --version`. + if (IS_LINUX) { + boolean isMusl; + try { + Process process = new ProcessBuilder("ldd", "--version") + .redirectErrorStream(true) + .start(); + process.waitFor(); + try (var reader = process.getInputStream()) { + byte[] outputRaw = reader.readAllBytes(); + String output = new String(outputRaw, StandardCharsets.UTF_8); + isMusl = output.contains("musl"); + } + } catch (Exception e) { + isMusl = false; + } + IS_MUSL_LIBC = isMusl; + } else { + IS_MUSL_LIBC = false; + } } static final BooleanSupplier NATIVE_BASE = () -> NATIVES_ENABLED && CAN_GET_MEMORYADDRESS; static final BooleanSupplier LINUX_X86_64 = () -> NATIVE_BASE.getAsBoolean() - && System.getProperty("os.name", "").equalsIgnoreCase("Linux") - && IS_AMD64; + && IS_LINUX && IS_AMD64 && !IS_MUSL_LIBC; + + static final BooleanSupplier LINUX_X86_64_MUSL = () -> NATIVE_BASE.getAsBoolean() + && IS_LINUX && IS_AMD64 && IS_MUSL_LIBC; static final BooleanSupplier LINUX_AARCH64 = () -> NATIVE_BASE.getAsBoolean() - && System.getProperty("os.name", "").equalsIgnoreCase("Linux") - && IS_AARCH64; + && IS_LINUX && IS_AARCH64 && !IS_MUSL_LIBC; + + static final BooleanSupplier LINUX_AARCH64_MUSL = () -> NATIVE_BASE.getAsBoolean() + && IS_LINUX && IS_AARCH64 && IS_MUSL_LIBC; static final BooleanSupplier MACOS_AARCH64 = () -> NATIVE_BASE.getAsBoolean() && System.getProperty("os.name", "").equalsIgnoreCase("Mac OS X") diff --git a/native/src/main/java/com/velocitypowered/natives/util/Natives.java b/native/src/main/java/com/velocitypowered/natives/util/Natives.java index 1c1e26cea..8c99fe51a 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/Natives.java +++ b/native/src/main/java/com/velocitypowered/natives/util/Natives.java @@ -83,11 +83,21 @@ public class Natives { new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_X86_64, copyAndLoadNative("/linux_x86_64/velocity-compress.so"), "libdeflate (Linux x86_64)", - LibdeflateVelocityCompressor.FACTORY), // compiled with Debian 10 + LibdeflateVelocityCompressor.FACTORY), // compiled with Ubuntu 20.04 + new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_X86_64_MUSL, + copyAndLoadNative("/linux_x86_64/velocity-compress-musl.so"), + "libdeflate (Linux x86_64, musl)", + LibdeflateVelocityCompressor.FACTORY), // compiled with Alpine 3.18 + new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64, copyAndLoadNative("/linux_aarch64/velocity-compress.so"), "libdeflate (Linux aarch64)", - LibdeflateVelocityCompressor.FACTORY), // compiled with Fedora 36 + LibdeflateVelocityCompressor.FACTORY), // compiled with Ubuntu 20.04 + new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64_MUSL, + copyAndLoadNative("/linux_aarch64/velocity-compress-musl.so"), + "libdeflate (Linux aarch64, musl)", + LibdeflateVelocityCompressor.FACTORY), // compiled with Alpine 3.18 + new NativeCodeLoader.Variant<>(NativeConstraints.MACOS_AARCH64, copyAndLoadNative("/macos_arm64/velocity-compress.dylib"), "libdeflate (macOS ARM64 / Apple Silicon)", @@ -108,6 +118,9 @@ public class Natives { new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_X86_64, copyAndLoadNative("/linux_x86_64/velocity-cipher-ossl11x.so"), // Ubuntu 20.04 "OpenSSL 1.1.x (Linux x86_64)", NativeVelocityCipher.FACTORY), + new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_X86_64_MUSL, + copyAndLoadNative("/linux_x86_64/velocity-cipher-ossl30x-musl.so"), // Alpine 3.18 + "OpenSSL 3.x.x (Linux x86_64, musl)", NativeVelocityCipher.FACTORY), new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64, copyAndLoadNative("/linux_aarch64/velocity-cipher.so"), @@ -118,6 +131,9 @@ public class Natives { new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64, copyAndLoadNative("/linux_aarch64/velocity-cipher-ossl11x.so"), "OpenSSL 1.1.x (Linux aarch64)", NativeVelocityCipher.FACTORY), // Ubuntu 20.04 + new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64_MUSL, + copyAndLoadNative("/linux_aarch64/velocity-cipher-ossl30x-musl.so"), + "OpenSSL 1.1.x (Linux aarch64, musl)", NativeVelocityCipher.FACTORY), // Alpine 3.18 new NativeCodeLoader.Variant<>(NativeConstraints.MACOS_AARCH64, copyAndLoadNative("/macos_arm64/velocity-cipher.dylib"), diff --git a/native/src/main/resources/linux_aarch64/velocity-cipher-ossl30x-musl.so b/native/src/main/resources/linux_aarch64/velocity-cipher-ossl30x-musl.so new file mode 100755 index 0000000000000000000000000000000000000000..1c6b876a63e81cbeb9745c492d3cb414eebde6ff GIT binary patch literal 71528 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 Ai2wiq literal 0 HcmV?d00001 diff --git a/native/src/main/resources/linux_aarch64/velocity-compress-musl.so b/native/src/main/resources/linux_aarch64/velocity-compress-musl.so new file mode 100755 index 0000000000000000000000000000000000000000..28137308cf6102491058512659e62bdd449e60cc GIT binary patch literal 75752 zcmb@v3w%`NnfU*nb7m%!Ou!@{AW$=tfI_qsP}{Jz&B+8bfFgu!b!~smWfBMua@96m zG?R;p9jzQ~*InDsFM)`KSbBF$y8CMqqEuzuRoLzBzq?x|GvPWQASxt+`G21?GcZ!! ze|P`C_&J_A?|I+ny*>BmIWJA~?z%(QG?n@@)GyVQC#B5fiw5tNQd1Qx7jLdtpRwyY zQ;k32K4BsVjd=XeZV_3DhrAHFs?eavSE^AnQ zxBee_2dtY0X8z~0{J=Qo@)5PtM-(-D92C|J8q9d~D0+EzN7zjQP?% zpZo0NxnEvX!Vvz;KLbqpx75O>)imtY5{$U=cb><*E033d{g!fB+pGUZKBfMbs911y zWuA-Kw@-q9MH}xYz$yHcatFlpN$@j1rT(E$={NIJ;EaG+p9H_|Q{Yeh6gXX< zvJNGb=ki~UVqpBG=z-}2;mev%wd3jU%b{OY$8a8m1VoJSc?N3tNbZc~%+RdSs7 zCCBr}rakAeRllzid&y7@*@#iGRzc>kJQ?mZzR}uwg0)16h5`In+ zA5w7IlHQ zKKV^mps_{()T~^wk&0;$s+O&*d9c1}#j0hiDNnUdFMX(C-Rf`7YYfz{d1%GzRcb-a zH)^T^s~@cTM*YgwffWyJShM<@jHj+@Rn0>yzEQtk-mO_zzkWTHs}|X1_pe-0JGXw> z%9@9yqZJP+p6im&H7kK~xy|Zz|9{~xTUTG74qu-CFM?mQZgqgUrT4Ir*<9}7e>(j6 ztNyFysjE*d>i>L!Qaya)0{!3Y;M11qN*^HF`iE))!Ky$*uxk0kHS6lsgY^%tuYc%c zFP7D;SSjTXuKq^7+yiSiq%Zr`h*MSH`0>{(mahUUAGGVGzX9`V9$xuS@=evUhgStY z>9vhGpYYPg;?#DeH-xtA9R7 zHU;8Jh4ki$rMmKN^}4H?*VL`7Uw6yw^fn*E>OUQk{rU>TtKhMfRqWX6bsMVezH1%= zZ_|l>-GfzuH4j%UtFL+JVR%LQJ7Ex2HCIJ0M0z(R{ zT)o~V`M`_^A6~!mvo$sA0u7(Pb;kPDGiHe$kYJz&d{zG55+#_wV^@{0NtUamyh_zi zJx{o*{Jnoqz}YjBfUf~R*@gY0H?2Hf&wo9wJTn2p{{LHPl2(3s z=)63ZR-Tes(&kKB`E@BE+%Ke+=cJXV3UsC|B`ai9aDdMK1!^E&LX7j19{h* zR*opV{P%iVdBNpU_9w0U=CtyEODj)`O#5vltsIr|^53zvaGFC)TDjOtsXuR8x!6vrziDaZ>3A|Tt=yBU%)L0R zd{kPwnO1&7T6uX|d0|@l;00u-A+6k(R)0-e`RKIr#i4N8XDB}d`sUt$=TOexzp%V|{_pd>#=oR4``y=h7frmgpKE#6zi8LBpO^7m zkoNol&;GRMRXo?GJ+I@rCGGheJa12X-pKRbwC6{8j;1}g@T}i{rT@oyE=YU+7SH~) z=Wp{|oA&%H&n;=sf6epuwC5Li-kbLPzj=aKG#S6C~f`6?i{MM+v=Cw3XWac^lKY0&w13)Ir^+_G3Ow%ZUgRHDstygh&FZ~ z${c!RWaRmO)VujRj8u6Ma?s{{|de=Eab?J=3KfxUOh_RZgSk~Ar8 zS);A2@m7EDUsyi#i&implnNay}H?aF3J@%oLyf@)=BmE z`Tw%7FWP;5=BmDA{sMdCqbqB2jQ)se%#{8wO@`A9tv3^g9as*rG8{gpAnBihBnO!coGn}s(!R@-Ll)6Ua)IHL! zU1`B`%3{np%9Y_Z!bW4jhOy$Kk;)c_%FP6>>%Q7Ohm;ZAa%rSe>ZRay^8C6|-LmgT zSeMjqWAcLOgRGkq{5z%$&u<*+)@9j_(TBOv->!)!-J>JJy)OFzgKXi^hKG0TCqB4Vl&1#Z&bN14Kk=6>` zD&457wu(EHdtuGy%%!@z;d&@M=eyvIi2Xf3go#lbz zvK+(H-{{-pqo0zChP@6k+Ubnf&lm%YRoY5BFYBk6pDNaJ%~Tu4ChIO^8z~#}_K`vj zxvp$JdOl$6IuE^FfJSZJ_aAq{d$e8eJ^JXu_rI#cd$g|ip<4}JHn3bDl713=%hfdx zy7|Bu-*p81tI@}XkGj;7WAG#yhhv_(r(ImuBk99-sq`6?pnIN0GpkHSYs%A#nSF zyZwIOo_sYv&KxT1HdgO>@uQL3@`K)Ddv7SO0nR_(_l-S$_buP!Q`rlDxZvh+C|A8* zv{_qvLQ}U!jjSRK9<;!z)^4NjprI8#rkUNW`H}+0XV90Eep)rveV%!jz^i1xQ)?Do z?q_Y__l#NA2)m*3(k63P#PAfITUxs7{jd6Woez}lV*M@Ihv+5v9qV*y)6aJuMt1FB z-9^U8+Mk1l-eXT>{blXpCtb2nA26l?_UT3X8Cli6=T}Bi?ZyO7jsWuwhpK#uy%H_8HeXRurau20ey}rXRx0=dyqa?E|?nLO<9kY6`Y@=Dy5%*%JTRL zyA-hQgNBX2U!&H(##&)FjD^P*srw#1*sD~yjrsT->h1o< zTH#Gg;5Xg9;0JEe++J{GK)>(>8I!p=YpLL38}|_N*DJ@}B5Ov>A<6s46%8Uc{J`i% zF7zW8T1Q51O~I5t(&5%z8u@&mdwe*?e1zB8^bWnCSKNyI?pZ!r6SaG2cz<@5xQab<4R zU4m{0ja0s8j4fgvZGO}7Kuz&sLoIA+tlm?mdhcg0xq&IKO%%QuNa9DkuGuo@3aywQ zqm@2nemad@+Vopn_Yr*90u9RE9tL*>4_x4f;6gug@N3BH5Oc5A&2IK=9UNk!&ODlQ z)$L<_w&`k)+-n{v*t6{cZ*heBEsWdCCAxqH4v0Q!OhI;Ro?)fv4?U$w^c3TVUJD`dZPN2nt3(F_aPEUNukvJH3P5gmE(Bicl1 zQ%Zf-|LrNXc}Op^@~LWM8Esdz1_Oma-imC_jcFndRRHsJ*|J) za$u!ygo)vLjvCpbFI5D&PFxeJn6HN_KFeh)ZMUh^ZnL-bcG<_r7t|LI7|tRYf9rx8 z1tF`VdRf&$+RRUeIwv}$pxp1EQhOpbI ztS8*=a3}c29CACs`N!eYBg$d(*2BQfr%free^h1f56y2mpe=1VP^YW#(To1)mj7ye zcR<&|)r@t3vG0Y429SRNkry7{b zVOpws!JOWYMsBTP%}Qh~GvI5iTeII6u4X+2r$n!>x;QdEaI;!cx1gfC>7uqau=QIL zg&(cDS>@I($m`yrMti1QYZS>^3^J#tX=?2jXvAI7;Fp_4pY$^$vI{??v8Slc|=JHLemKkz3X{W6u%l#8}Vee#XIC2eyVM z9+hzbbGbh;jzGhtr2#d0_Kl3CRo0jO8yL&?fGv1e&s}h74foXiM4rf=%O&z8g~uYh zkt1D0LC3C*S!VZ#>z%tctP@!zas?XzyT*sy5!td=g`SNC*;C`X{f8#2v&F{b{e{Sx z7;?sdUOoUPHetI=_<73$&ZMr+`cV>Aq+54lt zPHT&v8TQX5K$hee+KTF8SHtq+$KV&>w&hbUPl4MxDgQ2I>opGW{V`}Gv|U-v{+lgp z@{5aPUfUggw>u}ByP*NA^hNjQTiatuIn|-t zeQhr-s{YOo7WsMS*}GU<-3p@}@3~rJP;i zU)|d+&rj&%tVK&zZi%7CYZu5kbc?urypZwbtLx$sk()FBYS{gGRoT1tGkLE)Vl16% zIK#ioRC&`4H8g!{?+aU4<0HoCB6q119A~dq)D**atxy{8Qr>+p?1T2D&nk4IW9&^; zrdn&l9~byS6Bj^7rZ(E+hVO{ow*5}VLf<_~#mi=6CzmlU<&5j0dBdHvRHb4pF2?eb za)sTDWtU2fVvn2 zvQ+rp`Rj^(@K|+M1@hGGQT94zcJ1)?y-==e9^kVVtWlAwJfud)(Hkmzy~;YIu~wg@ zOmoG(v+o#w{bp6EF*e26WL$4Bu6^hj)2H^mfWB=tq3^wpuGZYwa^TyH^%>;EfMafV z#Btx8x1oc#N?lg-=)CU77;p9M6XpoMZ);dpT;y=%MpRbVK-ann8AtbXI+d|-pz(n{ zS<2%HczY)XlvdP=eSTua)MBB9o^0$$*7++pt6w}tU#B{C%PMtSTcL&1G2J@ZuUqdL zhSiHs;ki%cjov(dY5wBt*O@N%o_;%YbIvPy%GwsKD(*Z`RgCT)9zs41DkB_$r|n=2 zUGG*Ew{hRWnw@W~*>iz0#_$8QLAO4ma*nLy3C5^YW|vPnJZ{Pc3toL8;C=0dfO2#p z7rW;A`d)Brn&(Mn*lWBU-Q$oNb$N|@SYx9=RravPp;>nf@AQ588V8|)rcr9`wBeD; zpi|u{bAb2Tg*MCt591TsRP8Mr1+V=f@BCIOqitj@IIi#{L zkGq|5mrYic?TkCZxQ|Wx^l`7sQ@8eyHa9fuqr-xe`?Wl)9^1S(%iM6@TNplpZ8T-( zeRF&p1AAgE_swY&997!F!}x8Qu@hKJPu7A5ieL1;`ht%&_p#o}+YhZfx-ym4RjwHx z!Qbe$GRA8zj}e!6ybwETgfW_ezvDiAjIS`Z-xnzBA(789&H6iiRQTe*Yu0-Qv2DMg zhKR|GjTHQ0;@sN_FCjPKUq$4s&GBU@t7^Pji!YvJ>NxF}SW8gN7cd(HH$4n93Tyg8e4=w3Ghqeoz1T{l0T*K z+dZJ}vvJ+#W#EGB{od;oJ~+dY--YOp(nqw=u*{iv47+(QSo4{@In3?MdmTT zAvI<13+SLjHCd{ALzWtfh))?kD!TmRcFnfWeB7=X3eMg2;re;#qzU`R%lyRue1>&- zB}-YiCf9Vuzy9gmK50FFDt=b>;CTGctN)k4?}tBEDI>o30&O1g{_{4Tk=ya@zfC;* z3KP$JUr#(wXZ@ehKW9z%?YCv6$V44_5nLCUC_F#arWKvWmXAhKKJuKBNjDg-u*hn` zgOrSHWBlRKqr$?6gU9c)zIEb0>lt`)F!wg=DfsaM?x$LH>rAI^y%W%_&w>Zy8~&Nf zvzn0+t;jMTKCl41Vb+>ubDB4<+jCos!wSF?K2Wp5&(2?49DpaB-LQ6#(EqFO2;l+8 z8y|-E<}PeQcQ4l+oT4 zcuuq1>4om`qxN7AYVhEzd9UzY;lI-Fc5LCRWoiGrBepEGG3s=wFic=VNF6IrXK_*J(3^FQsI%TKmS)k;*wjuPEz`uGIDC^4LNn?=`*UijlnBjb-4 z&Mq6ryGJTr*l_!x#eMJ@(Q$Sqc}x#9o8mFA*=?@t5?qsZEB+6CjYkKhFBg3Y9$x7y z(XRX&_d=;3q3y*BNuNN+Sg}Vd;A!_0M0q*sP2hAxbZ|5*GA+br_o$nysN2YFwMK8vndRIBub zxH_I3;H$A#;?Vd;AHyC!{EQlM0&}EE+a>!T>(6+(3@!(klgqWcx;XTu zl8YHQY**p6b4<=?KYbwk6Y68~f^d}fyAEw3)F zx?Qam8iV$?ip?E+#vCeOKKNDRI`hF-g}?ZK?f0t6fkvoXH3i$Bd;VB_(t?ZFR3a;? z`TdU2E%lm{z3(U3VMzfo> z-YRQd;&!YRA2dF_F7XqH?;!d`HRSi=qu<&xao`zz7){8&-{60F(Q%JYQpuNb=7AZ_)I(193jLx$Q7u2w`gnM2Pj zWB)mHOM`LGHH>h`&~}H$guYbOqYu5m9^d3M@!d;%W?J% zbhG99g#E-ui$=WW+G_N;qh51KEjrNA+trw+G2jQf;psU|2N&E}6u!r}CcL23#NJdz zPTudUHm$*f4r`$K^oDlu%j7=T@30Oxe{Mrd$Ui)fd+){@8UxEk-*MP7ynY<|BerD! zGFACNp1Eho`R`Qh*w?x4p8uut7Chpdbee0yBRNaw`OMrSkK`^r&9%d9I(S2dD?Br} z=W!u^&oQM(}C$$Rj^@pj(ZJhh{DvJ{C}wO}fkOuMQjP5Pg?XHb~nQ z*jw$i^_HV+_xr+!JEa|ZNvL$VuvXwXFYCEIv}>(Vl~eFVPSG3-+P`ML+kBgGy~HUZ!jNc5*gfa)dliuC<;Bh|_4weS`FZ$IiVCLTLv}xU&@@yS z-)?TfG}Wz7o7cUKamlkj&Drhc-n`U~32|559r&3eCFT(R=6K6YRcSusCtjFoiLR~l z9HMUi#f-I?9`q$}gWUN2ku@y`ny@b(#Lp_Y`vo;-{|NPe3$6|jD~cGCyV}4lXn9>T z>sJPj2~KQ5-;F$Db_-s7h+l+QS5R$zeqscAOySAEJIH)!GJ>3SWShHnN9pdi?CM1U zE8PeZHkGmp8(I4cV6H0jsI&70b7(6 z0tKIo(7&wHc-Hb2o(CAW;K3m4b%c1N_`E~Cr5oS4MOB_9hNSg-!_!Brw+CAt)(n*y zKD^!m?vDo777h0=Qk5SSx~z{Ds-cfMoz_S9aPQ~7R1G~kTUj67tA>JOY(M^zQ_nf9 zGfN%T$$-OpFXptq2~K~6EfZ&5;)csQ@k`YmjeB-Q$r%{F*Blz!pS|ndd(5HJan(Jv z)EpYknyO;+Sp~iJZj4~dWdt7_&7O}|x5^j`7~>3hcYwIT*~$4F8cInkmtA+fU!FX>q*;`l7r<<=FI9K;3*@ zkGI}|ZvncUPW_hVrNai#O?q~`7Cdd&#>ZRPhgxVuU=3g!M#E-z)n;Ve3~VM~G|4zx z{KI8)WgH0`wP2>$sCvA!44Z4AIW*lgFH{kmt!=({*r04g_t@~8w2ARnKk)tL1>nbP z(8Z;#*dBghc9sgvyu^4HW6uE7psZb=7@yv2UMOh2f4J48j&Yq`r?w)|HdX@6y$P7g z3rtOa^V{H`U-kaMpTmW2`8!Sb9MarDksVR+=MeMT3*H<-P9DRbclCaqqF%pJw+dc8 z2kzN0p_w?pqw$6K*{|;!+JD!s6TdXOPsh#fWx5N#?kXDiIculf!G?dwX2Lgf{13=U zZ>XY8sfEzXl6~0sZJR%{)R^+>#5Uq@rAd8aYRF^d`;aNmx4$5L`pO%MnTsWP0!}_e z@i`op=~3Z2^;CJ$z z0k7*|Z=ZmF$@3HNI`uVok?^~hvebqbN8?Yx_U(Z`XS?BBtIZ)7u?~E|mZ>HzoQjO! zhWrp-Byxy3hmXb_);8n`IgevK$f(oCHAUsfE;n-PIC5)xjcLJWt(O}=*C;Y8GSfet zh1}|DoZjd`Ub*pirtrBO{AwqUAb`)a9Q+jiat(6I{fLH~(k5jgr$j!9EsqY3-_BfH zgFoSa!WShsL3rRlv!ABV%pE}HD&*eG;I<1hgB=%y|F73`!xE<#IS~6N_7lBQ`by`M z!vBR=K7Xa!72Cw^uM-)y9=Js&YU{%O>CJic49(y z@3+^(6OT<(mHTb__5}N|y=&l8CD7z?pQ`Mm&R|&v>!!sI7iL%@R|7X*k*j?(g_mgY z{u_{^@Uek_)OE+bGs}njpi?*H`C6jC2Kb=8{>aoip;5-+!zUmxj>TLSasD{5Gb@D8 zs(gy{;kN0pZ!x@ucb$c*a)k{eGu{WicFdeV?53p>EZm{4mwlVxP@`O3$gQud0zO5PuY`%K;;E27B+FR_8&?#d% z-s-f3znlm-Eq}9rICj0@Se8B3f;I3J-jz|dit&_aqvHi;=>>E4qT!wRbq;BncD+^W z67LMkR`^TdXPNO{aL%tX-~2Xl&ExEK<1sGFPd$;@QNz06xyzy^NXih>I}`KQuxW|G=2P#W>GG zv!@NG^*((aK@Ls;KP9G7pj_b+cy45tf4H1|uTr@--1+dK5}p3h|Agkne~P>r>cC$j zI$_Gk`Udqz7-s+-_6^?4IEg(}zC#Yq0-wsQ(k2DpfL@OvD-ZFz<)GxrjV=?AwDm;nl}D0FNvKBE*|c5 z**HJg@3L|A7&MmBGy7O;p$BAft*<%<8 zxU7Ou`EVyZS?J8*F5~NAe0|_R3-9_fuF!Ekcp!8pe$P7aK*sI`5A1aU4{l)WkFj3y zLYH;C%!QugvIGYt52sH#!X@-k+l>Bzd@fNQ8wWni-Zp8Y;;rbeb@(RQp=lp`T+lps z7#plo)=_ZqX~7lhl~LBld+A%__c6m+w8N`%dsG4k^4WiVKxn~T^cp?{(dFvMIq6Z3 zU}=P$7JKhCd+*6n$i2jxJjQ+>WbCg)55jw*#P7w|Df2qZ`_}goxmSWumo{zN<-j%; zS(R=dZ+^nG#QwdOdDdYclY2#;uC}Wk`*XXL+gNPn2>RdmjeING zgq{3VW!w1C^9kEn`un(jJlVF7AHtTd#y0*HGV?pwx!1Ag_W|=)$jcAVLnG*%)vfra zs>pdpZ|cK_@L|*H*sc+M%>I1vRQ&YFy0Avu?9lTsoV-QanL}S=uBP~>@kK~HLZLrI zu;(M|4J(5F9D&BkeHw~D=MnONBG7sST~}vKB2}V?PwqN-i#c@S=Z0{>#vp5!4!3QdIH_%O?$U;@ioTh^6_H0JnmgWqKf$UmK6)@*{OG|36gVV4$o{=Ig%4>4{I6_yT1M}Nn=;gf*Y5*nGEN!H5nkzu z7g82PX7*^FU@y3z4~*Yx345)7@3gS^h4!|V4adGBZLha+w^rJsi|V^=v_$+XI8%7DCmy&*Rjy^-{SS zWoys_nzWpF4SaqrFydv2c4rDRENK_JE3Mt)M7uJ|1ctzm%vv(M8eL%LLaCSFC2K1Z z?+nU<=m9dW>d%ossCnLe20b9C_5LB4%SBxIwC3^1-bI_phwHs(;~VVZ>+E0jIbu8D zj$e3RxxK&k9(u5ys^D3LlzD8siT#H!MbS&~t?oYszbW{Px{ch*@o~z|v>H}$lzRIt zbTdlxg!A=L@q(7ShYRmu9~r;!wB5igpE+i<#7AkHGVI4S1jfLu^#3cPi$zwxj>_V;{+Q3&l>Nos2Q3 z&%978y3v>AU1DFvo|1NDl*zuxSbPF=2{8Me_PaBkPR8vYE~LCFX35{cSOO431PO6*S2|iNL4H;Tx|*BVxZEzX^IocMuvscBk+wJ+9Dm zj*;s@8+%P_!EY%qqb!0QUz?|_2Ixb|BDB%5zXQ-rc^#k=d1p{2V|q~FATw*B6E*Yh;c{%E9&lT5>cNV{yH0GP2KJ;1 zx#vUoZU9EK)d3E;&^mVcG=9_mSRuZ0p5tW>tC~4%A@@V-O1s*vGA=zHBL}1w7(M)MKo=35 zDx>W3T%S$MwUhN1o%CsBxU>u9gIDOKvbSQV%igAJb^jX42@-vJ)Iy1q2e6lSllOB^ zj>`3v*B0-d+CNczLqVs*3VJ-@obm^YMFy>%Zgv|k!*dE-Y^F=)u9>d7gV@PGMJEXw zp75GobGvh-e3zqpr(10}20wlo{@t0aHW+H${vL8+ga1p)RQCw$cr!X;J>RKBMxLo4 zrsC-Vw~oVq4#T@&gl}*E1V7x1_~EwWhuc9uTMu#nK4J*Mw*$ye8RI_jgxfAT=bh4s2SeAvjvj?Io2Vw*@oM~ehw7&`0nuO9S*4OMd%rI`1Jz#^=i}ndgYXhuV)ZFV&JRnk*+U191#6= zQrEw{FFw8TaW8gAgm)@r&gmV8Uk{yUA9m2%#loW!a^!T(Wks8jKgh@uJt3>sf_o$I zUW2lftWdL;46gxy8^kV3$r12QWK2Icknj`XSH0|yM@NppPgbE5$~yN7ANVv}1E(bS zat(c+?R3HCbK>!S7x=z-*gcN5Mh?nauh}XxASWKcE?PssBk=6iHoh+&?ngFC-*!D4 z-*e)Hlm)?ek!>B=5VmYX_nyy~w!*vO^!0AQWu0wxS?6!b=35O?@^%s~fF%B#PnUJ3OK%Kn!3P1<$w zX7s1iMov*q0Xf1(j-AVY{+7FTnuD)<|*;e21QPZ z?nXx_AKr=IJU=A|8NaNjL7C{jCHP;E z7d1u|?Z)2BH%8g>%*Tc#ri=}u+j-O26U@gQ%mh}jp`kbm@2P&Ep&0*t(2K440XVu9 zKJ`5F2qy8S7Mg)It|Bt^oN9LKm{XA@<51J9P1D#x8U* zROqzMmN}sfr;R6Z_DpFW8wUzn*?)Y}l8YiR#Xl;!D*fcVRHK(w37)J7UML_(s*p0$ zRPB1Bq@L(+*o&iVJQ)?QfgYk)(SzVgCgswnL-Y(APdvf-_%KGT2!bab@FZB=lf)Bf zJJ?U&hUg|Mk~s4g^YB;+-K-^Pp9Zy2Z$3`B*bF1+G=*nJD)&=g)>qb+@2$rF^BKuc z_&>Ww&wb*+sJ18Um~P^mA@W!^KXOnVyU$up9*t7BSy^>Y92lFU>^y>p_&)3XF>2^E zIW>ogrz~=Gyn8p-0xrHA>Xv!D#D4ZbGm7;#BUO!e@x4e{Z&hQ|$nJWvt!hu7&l^_v zjbExb_N*pL3*W%^Asw{cr^ulp55Hx4;r;-7!@6`eIL!?!G;>3B!L+dZuV_P_j#{&9 zj#8t-)#R3{9DH`jv(nPSa2?~$(2OE*FIV8c*ic9Ag4wOHq1f!?#c075>?1z-q94#- z3EwICk(cB(TJ7N2Ht;7k_EPd|zKcCozg=0coVf38?AoqZX5aR<*veuX{|)Q+{xuJG z&BZ2`7x&KawQ z-@KieR0LVl;X?{$|R1yjX2u}#PTMy2gBqR zO1%Gqk=He_-@VI+pZ5HZ)zEp?p^|>zL!TIUSq*LLzz_YL8j7OZ4G`lWM30E#t7yje zVJZi9vg#5YM`AS^aegU}G4B?og{58$KZh1=I}oFt)MIV$QExTBP>nnh-^UqV}Gx%?M~rJZ(it2BjogWz?t_7i1q(C8S6)#sb|caZdTn)$`i z`A%>jB#!xhypnJBRMBzZzlY8v`y+9Hy?lEmv4Q}8GZ|Ba_+WH<4&M)v4@|xZ-^?zm zV!V=%qLEjkQ^)7{{O5@6EK>6R)@`f&Z5^w`zL5M1Gd=g@dv@;0)>Q7v=gB>3zg?|8 z_ATT-`66!ahw>EP9fc}>|7>WYj74;3$$=PPPUOJtuhBKzrM@kv8G2=8^!up4csFCjy{JSH1&&32anxwd-yQ=tHgOuLcbpN`YB>P zFQKpcsITzdoI=OhhR)i9zedB4bND&*stq?cZV#0W_y30Y68dn;FI$7(sE4&ND6he9 zv=g7%PW)tZzNYego89QO?n$?A&RIGQ{kFN#%oRO%r^_Ao5wpqBLPI}!VB3W%@(dcV zV-Bf|;BO1Cp@?%1pkEb)mJXM_iXNpW^yd0J+aK_aIO`+++6P&W^teyQ zhD03r>8J3E!beQ*?tU-%gzmWbcFTH1|4EF`fd9!i#ht%al?HxRsI zf2k^o30ecK9(x|qfCpJTZ@BRP$ea^)opYARAZNVwAFyGV?~zWAb%^I|{0Ifq?f-kJ zn_Q3CiS;O?tOh%_opET}Sr1?glt~{hVoM%kOLq(#zmhdc#Hn3NWY1jjKsRv#U{vut z8^1yU_4@Zny###m35cCiNLdXwtiV^>ROM5eX6r3aq1QZxPSb4bG{k*uJ6;P$Yrw-~ z9NpZMSiid|ll4>F&myNKo_-VY^utSC*3HTjK2qjlOxf|hp}U4Vf1xT{S+iItK3n#M zc1zB%H}4j@tX1q!tT4-peG`5X;Epdg7R$86w;MBf-lT>C%t>;hPTfM@Kv|ZxDUWCL z^bh`?IDfzDzPO)@@1)OvlNjJlqjsGjui^N5HFSK_RXG63z2AL5W4}L<15n7AZ&pTF z#w2<49W(D5p7vwW;S+f@VfI&Wuk}a73DMia;6V>Kp=Os2AKMKsC--0_cq#KOqbvYk zHi0YRbLwG@{p0|&{t$a98B;H$ZWX#->}A=TG4Y$IznvU_0MAY6N44;BgM0<(BmkZ<*7&w1=v&x`(!PY}+Gc#RFA3ZP-4!ktx-;Tsl#S?F@tg3G z6f`dw?%#orAsMR+2!2tgkg@>yCH2iZerv{?7Vi zW^NVRK~S{fW1rj zi=Ew3WQ-RbqjYgXXDD4vo{z?bA1I$aSy2v;{dOYXXapa7^iG~>qo^-AN5|3OL`JQ| z$00dK?bNxOvH^JgP2|Zm@jOaPv>7n6i;ly;mz_@J9UW)fHSF;qGOrSy^8?@x8(Cc< zm)}Dd{16y}=qpj=T9Yv*9zlnBAOAp1zmo40L5ETJ2Fj70gXk_XbeAZ)%YgU{&{_PH zi(f$UectPoIK#Dk3q6r@PR85$KEFGQZvfb#$CRD#V?LJf3tU1U7I`IO^DvK_8K3Y0 z;j41?fVbDbaSL&wdC&{rG*ld74|kr!k8an|gVX)YnYJQFjxx^yVk(L=03rufclGCq zSu7<60uQM9Yk1mf@(;hE>^O1+x+msl$21~ixI_Okjsd=ptoaIc1He%k`{U&FoiVOK zzZqBbu949te*Njt^MIPL|08TG`G#{TV=F;^_EPRce%_Fho5Vuu$uAg0W@?m)+-x&V z>lpG@{3CAUW+!qppEyK_yU5IDu7P{ap*VB#Hco4-ZT|Xj;kTh%BPox@B;--+)0|b3 zme<#eylmU-oaE#BKJs!ZF_$*v1;Hjx8d7Kv? zIOjxWRt$HdQ&c5o<{&X6iRBrT-7UOus6o3XWQXKaG7EJm@6;WOo1PD!JKf(;@ZX_vqMbd#e+2vmLo9 zanDxpD3!k;GO-=L7(;(?3cu8xw%imMApC2^PX{_uMr zmz@c@?+Wh57u%Z5pWDlLXQ@Q~TxzW44~t(gp_j}+PWKQG?0g6tpL#RE*LL)e4t&^d z%Bqrky;$&*7)D?{ITGOKp=7*9a5hT(yK|k$&V;Y2fV$Z3af7mU{Bbj=-!Zddc*|O$ zkt_Lg2G4%_6IgcsTr814_r~Hx{#=QCd)i7&X0?oClD*FbGofY53n`0!(;S+C?3Dbu zg4TP7TUUwPOZYpY;EJ@5JxCwm8-2vL;cI_q6Y>Zc^8C*8*dY@0St;-n`6H4OBku|+ z6BuWxKLa@`zMAS={KE}FshfyFMZq~K?|*cu{x+p8tj5PIIc_1h@=O(Z$T##)O%pu6t_a^IyxCKvyvQLnX<@0v z^6>#EVh42@YKi36Re3opXiD$ILShI5ISU!A2OYzOevW##C-(L*i zuMj)ZN&KJz`(AkVgyq1faD zp*jOcuF{Hy|k!v;RmH)lC zb=KuPNH|f;(=_*gpmQIRgGj z*%a_ca9!~2CUnF;@aRwR#~ojJ2l5cyc?mu(_!HONVX>X7$=6KakI}VExxy#0d5)sr zh)pfInxm5V69s?#=py)BibPfj{yey0PNU5O$t7HbKC)^m{GkK@mum&1MqcSa>~M{+e~PJ)B~9T+J;hUCK^ zC4WK#p7=9%ZWyT)T~gx6?NfLrCV6O_TH8Tc%7^mf4^w)mD@gouqaQgGLr*z|4mcAX zRrJ#b+sSD|M-|_J;!F~oR|4Y|-i!Sb??;C~rl0Q2B-Vv}QzH6CLar395xIhWLs{&r z==In#BJ&H0b=5ZCHQY)pUVNcKvn$t$&X*mJ5i^%~m)J-V#uHGUH!b2_`{?^caHo{- z)FaO?+*N>$LSmx$9_8E;EAqT&S7bG|g#1Pajx109M%R_^Cem#gkrVRWL?mI$SW)!n z0&rREm;!KEzM;^Nb1AzfK|9L+mO|;+GqzqnCYV`@UJjlgg^zt`7@VIsrbzT@(VwTi zME)1`6$H5rZLqFM74a@I|i{ow|8P>KlmvPTtHF96FNx2EyL=PQC|Yf0na9 zn{?O054igU54dh&zh&%v55cz>9=^p0+qneAl#3jFr!2#krHT9(|8VQ9E3&hIy%gCQ zn~9u7j(&hF7Wp}gz3iRsAD%{eN+w5HdwJJNEZ)yvme9`3hL&{XEPHtaW4Z}=CtEXZ zUwAB#Y1I;MF=l`}N&WDqt%57$^n4Z^05=4WZXzZrdU!v2f#8wgP2gG4!?BMk3lMve z_FnRCZ9Your5L<liwn#OaP<6BjBsIiKW9Ve=bB0(YDZHwd3Q zL>wnDog4%BPKiP8Hu04xxd{Ep<@{vs5zc;@n z*f-IIb?ot0d=B(0e$|^B0>x*+o8SY1V(1{~Yq9fvt#mnyY&sg8`KF2%a< zX1o&f`k<3IaKFpCNMEsAv~F4V82VEI{vm0W{nPhK1$L}}U7x66)7zy8FG?v94+%a58JpNEpe{ro%_#0cX zXO_V;d(cy|$vG0=V+`9S<$J7$E=sV=$|$RW&P1;9O<=}$>LqilV)RrzrrWVC@W)q#4h$_VZ$=!SXt7BElk+3g_Mg8 zEBW1r?RHM=#CgLG#$BE%V@}wuhrxTXg*(x0%Vf-yoA4XkZe`3B?C&S)fapb{|BW%H zhHnRN#12rbeKR<5Dv)KxTeGZpSjPl?=tWM=2xH^Nt7pv{E_X`*iCFVX;C&B%RP=7! zcX|lk^+Wbde2WHU!hd9K>#5fy^8p{~c`tL*fFtK{p~JzKG@CvI?^i68TxI&?J8XF$ zuyd7-;I;*cTxF>%v?20vF?-S}HahqFK!{yDlXqV0t zL_eq}ZY4UxNBAwTXYaG^HA0SY=7Ox9@SMG@oxocU?-zeZXX8ladSDvI)APT7W}laq zI1|m_8)QdUKQtU5J}NQM@zwGjzqae=lU1I?M~BfV-#b!jpQR?>Dyt53r1lKHsn)wB zZutHa?p@^e9{U#}*XnP`y9$wi6(e>kX90hg9IHO;?DviAU1|#7gP$0AK1BXiEBRM) zCaBaI;CGjFrhU4Oy%&?;P$t%Y^R}+J^Q_$&{gC0u6-n*H9Z_y#8OW6s=UU1S}qgUR!;SPMO= zKkDd@GA5xHfz#ZXW9d%EB2D7Bb{us3Dj#uB>0jcYUgDs{{D6JK_4#~v?czL0`5u1@ zq@cxJ$zH;kv)_1enOm8JgBsAHD@|J&RN9{-dAJinmN?N_rf_#9;sK|c;)QkW^x{0 z1a?bF;_Tu!&WR9s-iEs3GT@}nE)F<2L(Yc59T@3n8E*rI0Ur~5-T^It7g&GGSm%r> zuwL;ytTEu$hu9%m)M?3WJ}|(ycw!5lIy&nipH3`a z-hT*hxfWXC9MGbH6Q$s(<|*NulqZR=+TZnI_ed-sJew1FnQu7_739ph$^pUeA4@z! z3m!+V+4$d)!2coo7hkK;Ke8k|k+Vzxzv%xKaB>hC`W!q!XkTbv=>1*BKX}bTI}b!` z2%+&)l*!m-jz#<)CZ-`{94s?f6E(*rYxYiQcY&ciRccId1@c+&QsVwHro-#~bELh{ z!>ahm^XM{(@0O6$>AFlQ^z=!3%&}X{OP4nN&dysakuxli^(ENL$I!i!XILEI42u^e z-&##*Inc}+NPi>n1zCUD2YG(|gOTU|K%FMHBe)%0iQL%!f(EUXIyeWkZRwl>-hIgW z4~cBycRRE$^#(X&!K<1M`cxy=mHOkTlfn_f55Wn+6~PI?jped0^sCup(!&3K)p%ZH zJgu?&&@->?lD6_~XJGDw-GR-Hr4LV5x$ncrz9KNvc-^BsZycp=6@2JH=65{XJW=@G ztLVSgtU3JXtpR@LC(iJ&*X2KX_BlRIx4tCZD(KdxS;v=cdToMN)IhJz(5vatJujuv z>u;dfDMGK%pX{&NtS|K@&lT@Qrq{?>#n5Yn+z^{qlXNZhsyV;7O=L9Zj0>H860Hiq zzM5VWyg_JH{}`?I0b>H|-wcfkKk_j)SwrDTnXFOzzAs>%Q}nr!J@_ly{dZ*GXTkZD z3>5h%axh&6PEFEB{yQVj`@EqFIrmuP-dxr_oo?lv71^tR?9XKTOci^79+v%KKd=|$ z&CEsSBlD2+-CLl2S(hvORFdSYiF0&>ucr1$_DA@tVsDz+r&4%}j~HY#d(;Qs)**xD zvUfiAXrR2NxH%{BzJ@cES!XMsy^(djdT#_?8ErLqX@K+JYm)6sQ|&lwM+0Z%`(qt^ zGHp443=mn6#4C}dDLHWUc!f`2kpsHD*CGRC-mfO*KpSIbjPQQlGlT@(ZKZa-GIYGyKPAq|9eKu>qdwsb9PxW;jBOZRK8dv&*(9;_NnJz4 zmFrH8JpUo@g})5KTiz!QF6Z(L^ZVTLQH*_j(KzIQoSCC@KZ5-eBOmVYz2;CWId6T+ zxxZsdsin|EN|X`L2tEEnwan=^s@t!r+iKB?2B;&skVhH&Ah~Zppig;!RD8$C2svXu z0^AYe;qPuTyHD_}DPzC*FU2n>{H7YeX9@neOne+Gg-38s$O?WR@8nxTzO!m4&*GbE z;{NE3?GxL{FTmG5aT$HjQ`!Tk3(O^@&0-fOzMT5vDq`}C|7Uuk2kd8FFwT;4w<<}xsURU3J2#CAJ9TWIl-4N z^bx;U`=W?OWBwH}#0vc8NGvE&5l!_+B=1_Uu8!@`D>| z@l*HU!;x=PKKpf**Sbl3DhXR~3H9Y$f*3JRi3R!@D|Le%_Y!~8MhE%#E-QQacTEIN z2{6Rhvt@&{PkhU`koG0`xMGbTZ!h1q2_3xy4y?Fyb~y4EO$VoIrB(rP86R^Rp^rDI zlhbQjN-oxappYEsPKPD_$jl5CJ{NOXIT<30Z!sSuyIoMCYC-VDy77*}+n z(7kJra|jN@_e4kfkncT?_JarD+bHw{Kd?1jZNNTde?|o*UZl1@JuxSP{n!|7w8z05 zi6N{FaWCiJaj8d+lp5~v6v;JI&R{Qo#?sr>7=LloL9y!^(3u3TPgCSBc!GSRS(FJp z@tLMzhN;8%GkN%gH=Mlp&Rr)8%Xf*4AAF9O8Zz`K@ft7fd$nu#FXKFQHCJESgWs(T zUtts9l$M$3UcWKBg+6@OsN3XhQ2CvSz9f8W#s2B6i=(K9Gci6eGKzeQYpp5M=Otn& z)1BRQn-ah4HaWYe{Nsj1`9IF-?xoLv$yL@qr`qt^Xmy+96GZW=S7R3`b*&0%jf<47JtG3^u@nDvA+abx?7&{9Y>5&UB{rwC*WlgCzNx4 z`o6|DJN#QJ@~#HkLGf>?Xvu#|g%ct!VRKy++9`#XZ+9-3UX860;F9y`0@yLwYn~ow z)^x!qEqg9&LJkpSTDGG*pEJcp*O#^@p>?787&$#c>nS=PAb%zbpNgPI3cnLLe_k9V z@o&rEhd&F;Jb-`xg<C~vR=j;AjgM&iQo?=hqvoQ zXXdWcg_N;g@08(@VO`D`E^L?V{imQqJ9bg#w9W^d)}!!b;d33>7B;`kA)k+L1cYBc ziT^CZH!kuGbv{0mkT!9DJv_1?GHjQVJi=~RPY#M*krg4D@K~@r9T3gM%w7($SI={Mj7CfT_-%K^} ziWJX~XZg2~glC`+!86>!&@CD%;CjGvT4u9@W)0STsTg`6lmklA# zGnDh1d-moxc0}_Vn{-FqB!(vYc+wCVmu=&{?AJEF(TFAS{?EgA{yiUbk&okhc2`JC z!*_gk>G=LAI<3v$iBSuG?{KLN)vRX+eD6n$Q}}*5&ku86iSYY@R&BP;_ptG`pm&e( z!4wbF|81mlZ(mhomAWo?_zUe5tH^f{*~_*lr<~A6o8es8P7c(Q_?rg}XHgM4R2BJ@wJi&V%gT^re5WtCxqw(r6Xl(L z&WlUdnbJ^qAu@Bpu$!`~_0DR{~%1K?Q#GY~WK?r6s<_7x;$^;1mD63?IAOF<0fD4LB@;f3npeevy?>=%73YNo;OoEV-;a>> z=z}k!izd(cm~5Z*a{jC0OPL&OA&*%4Ey4zfz}M@!S83$u@tu}!ml_V+ABg^w+s7Q! z<%Gxv{H=3@|I55ZF7z>XkqaWX1AEaikjoDsrw8z9hQ{ zydK#r?;=K4*Ad>~1G%LBL=KO!WvTyD7Q8;Ed$IU>na3b?+tD?& z0#(_O`u^~rKJLqD{uE!%rA>dZ{W!9QvVOnD$1Z!+rpCR=_eDi=mUjdmR?C_T-`WoU z5?;21db#XPp^>}L&IN~$NQ`#2GB}STk9;{k|A8M>CBNaV06#`34|wq@G2Ul2BV5=J zxX`5M#rIOT6Pu$7Jx_3HDRmFQe++$37$N60|3M z9HP98GJ!AsNgq1(IwwoLge@TO^L5};b{Rf#F=MXEJ=gEF1pfP-oNoZET*|gfdwj@M ze9I-{lYV-5C*LEd<`LstX6d_@x^h-i74W5Q8Fh(C*k?6KU-FHT%x3`JWz^AMYofng zeoKE*X+K`tC-Ug+b)&qKSf}(Q{mPuAAA_<~KYwZWbFKY7a%xV{L}hN9%1y+_@`}97 zYeXqxL3|HM{1d(39;1>N4|1^;KVhq^8{_qsue%T>7dA$mu1O_weAZKMgj}pL$|SBY z`sFrwP)Yfy!`Du9&gc6pz33!4H+i%-h_#)iZ~1o~ zP0o-JJGUA;P4os8Dz!vs5dA?Tmr3*(8(!P#4WdU#Y*%=P@C)G`!Y@vspWFPQiu`r> zgZGakzmQz)^U!jHGHByj&Kll7uwL}<#P|D=y;Y4qbRe_byZ^kd!p+Q8=wE1F=zq`{ z-{pd)gtmXKkKcbE=VD6!4Y4kKmE*hCyZqLA&Il@sjy(UcF(w>(hY|+>) zCHo$T*upe3%-b^JboUU9ZL8Sb2uf?WN)}dwiaUu*egE?;NhLt{%=G(xzwiCMbK&GZ zb)LK5bI&>V+~px(;SYIs*B+S{$oh0oL9`hcV}7rao`Z(bn;PAo(lS??l!K1Z_RulDmsV-!{)MiXBE&nU-@$)63Wk%sZO4%bq-A4NvSpoR8=@($#&??lJarkFnfe+>`z1cRT&lvDE3+1d}$_*;lZ;R$W7X7SX9lsUxo27jBqLH~^ zICGSEnGd1o%w~^g=HM~a6Ri=|J6&?#!J69OiwH!rPt{Gh6VQ{?u?D|+dPJa!J%$DB zIoyF?^T^PJRjC&3T#MMggtsp13vX5IO-`}oKGw^%{};@ge!-k+2Xm&zMXuJJe9P1g zyvTg$WTGo>B4-}%1$MB{bcj8oTGl(yGd~&u9OoN=uTjQriakQe(I!s>dwK@8L7?XFnPVw_RAvW8WIKk=Zm z+6zCf88NXfGSQM7BwZ<2BYAjfJ6{5AL>}=Li+0w*+DpIhW=?-g?g_#))pys**j(G4 z{bStK7BA)0+w;T-X;1EI2DhAgo@~~`o?-ua&4`S)6<14GJ-_9nBtLsg^HtQz!`^Xh zVA~y*+>@lyR3-7kT2A=7v-gCvg##_Q2M8QefQ{A*Bcl^C|(iRcOW>4FRUJ>+D6nj{*+lKbF_jC(1rXdrOx&^A(lW>l{`l2~J(DDU)wS3(IZB^X@t7)?h)Twa_;cSt+ zKM}hd>kD#^^QZU9x#8H|-7nF<*S&!*@P-;K<=IYovK8}u#+Tfox5J;^R^QW-o5o%g zX}_W9ya(8)odj+v_JE=650*VZ%NQqBafCD4IfcHK_|35v?)lT{Azk)=h`m$pYXZ;O zpq6GjVV77fEmb|F{Rg&W5x!vQ%r+apw{gCxfP9x&dbd<{mpt{fCa))mf{kv47 zCDtYma&BOXt5xC$sh^ee7gf=cu72N8`c!l{@Xb-+I1iBp4K*#joBKfcZPWMCE`^39 zZha)_YBXeN$@?8=$k~hbGiWG*IaY#kzvv!|#c{xLwRUzvXekYw=<7lGMxu5158NNp zbxi*3H|RjWQKN;9G|x!@LArRz7`z4yD~kF6ZZ*@;>B@ApHI%_E;bj2DY4ih4D`w zF5{eF8akdETm$ixaZYd!wy6eW$SRv*tHhr92XyL2WYRTipmt915A=h?K_;Z$hrAFQ zrG-6SxrsXO_lS)`JICH*w_7{M-s2xW$Nt*O3i{N~W+WKjP!L(P?N-jB?W26i8rh>8 z`2sdJ)=>TMOW6-BVMGSWI(sAaQmXqQ#zXsg_CI2q5&1Nf`;8jt&kLdZchL(%W!BO+ z2!p&3-Qz!J4`ObMJ;;=ClM=X-AKR2wcsa7}HSTMXb)Wn&)tfsI&<}bdKXpFv5qiQ` zlsQ@MQAf@munuf-a_`cY=#@z!_ozGB2cTsApMFSYzfThTeG)_NTRI26iF(Vvt1tdV z&Q)@sI&15)W-T&(VRLYuq+NGH<>oDOjq{&SH++X)a$NLV@|Qk!(C<$|yF%|GgHEHz zIP30zWayu158C||&NxUtIEPsB+F89FM(KS|< z?6`1}dT--C@D+ukb8uHcvdptMXZEXbYyjM~+6=uLvM@<_D>AyON#$MxUyyiesjhRJ ztqs@Yu-v;^g&rb2U*v@79BJ_S<_4aHpBp;ILBDy{lDmgDqH}!fdZ~F8?QoG?iYU4L zC#+pDhf(-h$fh)GD+O{l!H4iR_E1VbVaTH;v|H^5`WZ|1PilIHuNV3uvLWTy$Tq^9 z@WddGY%OP#Vp!|)v?aYS_0`L2yI#sl{g5MrDR;9khBVpt#2L$Fa(BRARPJ#@t_>a` zaxJ_i30yL=WC?iA@aEb{gY^yK*;`tg9f1Pc!}gxEhrXU?zk_nJ|JC;{@kk?$=Wy+e zWky^5pCz1LpX8}hAMAJFl8`CK!8^Cg-S3wM+Z)_h9h?_ zoJ6OPy~8K(!mr2osn$W%=gZ@)GczarjBua%T&)-S#_SRu;=*j!G3ej()5EpaEaEJZI#v-NAKf0gukFFM%4EY`u9 z$4h)UUz)g-`k_-CFlae*s?ZktEx2ubV=o&TN?bOhtrmG4Z_{<6K@x_zHgJP@FX0>R z;hDX>a^LAr^pc(ECGx%0Pc32Gd#RtPJPEC_7q=vZam(C^GnGBM=q21WPnmt3sTBGW zz2vjz9OlheYu}+c_w79?UmiE<_3Dx&3-TU)rHb>ERv^!Mbq?Pt%E@Cto_3aUJp2KE zt@B>1#(U@Zhy!joYiJ&1K!l1u#2xUdzQxw*!hc@HeCP!G9yugDiXxf^C>$+I2&7dt)ksgiE#tb_! zH@{y{kM5EMeZHn5C&l0A`n=H#%`u;zi@xwY?Hy14Pr*AhIY)ok;_B`>jUFa_;-2FGozi=inxA0L^cb(Q}&3+J5w)ZZQ`X_oIPAl9<`*w_{Uc- z*$6AoHr_SKofX8hD|~45IJUkjZ1A6?-NARK_=Ze{>!okdPeh*(o4`5hsGNRGo9wq? zKbul9IgfFl=LF7E9#2Bva7NdY_2}3p&QQMIi!-^Lt&}slE%c9v^SHmuDjMs?7Ad-` zJKCz9&z()#1}n!QIiGu)a%FNpcQ)k|y;#oVN_%Q$=WOLr;$~8QY(d)D+)V0llC~|F zd9`*nx0(7-wwJEuPEaRzfu=<&_*8hyxnh<31UyyNXH!_`&9g-3G;v;ejwPyE&g-@` zhimJ_Cy#~)Cep^Tek*4y%?o8twu8H5uS56vcwy4Q zYvpWZt=Ax7R zSM*uYV`HF61!!k0JJ}%^Be%d?kYRQ31x+8|u0}uKjd*Pfc^d0%q7MjfO9B@rGC=A2 zKtV6v4iUAS`8i=u)N_uFF>|UmD)2IOuiYf|)yKE>dKus7SKv|@cTLSv=r~a=R!tw^ zTL|z!k>AVUbt$9ud2a0nc)?WH1sQw!_v!jTQv=gt(Wnv>l~sFNE+Nx zkef^z5>Cdv=mc5dq->IYa<2Za79At;u;>Gvk$-?QTgv*zQ}F80^Y2ettd1t=f1AZx zvg6*%*&&dtSLyB8Fi`9eF`6E*3;BBLi!ezX`|(&V|B z*NUe*cGf=PGv>x=9IrDc^iphCRXq&TRkTgxf~A+Mm%Egjr&V(&V`pdzqDc z4Vs^le(k5VWj1siZ;NX22o9N?11;BK_j#>atFS1AI_$uZFXYb`4CHKkXAg`U*^DVL!)QK~11@z@{8|#z&PJvEH zyJacslPwP+`-v~Voupg*xTLG=jM-CleEmoP0~d67@zk^pS#{Dg`@L3U;azOO%82x|)sQ40ICD`Nb!oRgvZAn4fmGF`zTD ze(rIo(VsvgtB~bRjpz4DJprG$!LzOKbr<~G4PWQ(&r};c&<%f=y8w0G%-M4Ip9@|m z^Df~Vg|xX#csBe{_RF~7dxCet^PXlcMd(-L(XxZ?M-uI<>3u6_o8|t7nAGXa9Xq`b zr|hK5H}Lvux(j^cEyBn=TJ~9sJo~TmA32XL^XMhaqqCj~YUhmX!jDK-Yg3;8iTunw z{yXVQdRqFw=6l6FN5G>$#n$vW`tuL9ob_d&KWiZ`MWWYl!zNTvAI$v@St_zjWSYn_ zk!hz5Ic72M1yB(=&B%x35IL5J9Al5hMjtZFZP)LWsN()fkz489DRGki$zvaF27PBk zc0Gq|68S8BlY#s?K|d8BzmOGi1<0->`f>+-DE*s6KUUFy>By}DelMdBwTL$sH57A;-?lW$Y+@Zk5RR zT>}5~fJ-#w*uC1E*^V5u54g*eW9VEp!~g4Y>^L?pvCDLoW1^Fvt%}z4@)ln-b3^XN zLzW+a$I2MaoG;^?yYXhjYwvQkUQ2j6ADPKH?L^Mp*Uv&8Fs>!+wS-M$uSgQO6yA&8 zU&lDu$v0@8=6qiqbSiw~lV&*=9@WC(9qx|iTQY^hSM>GmY|@Z-HgKQAe}=wVkZb=-A%?X-}FoMDy83xk1cEu>wFS^PfbVoHgHnTlO8#z8r5^9&1QV)G^NcKv2KlP}Ghoum93C}s4eHNQ_5;&RHXgrc|l19}`neWJX^RN~d`ficT zcR2s~3gwdgyCDx{z9IVb8R~PXvNjTyMaXHNqZ6F7MF+m7Twfeg+8ODy%!e|ORbKQD zKW9KA@0LE+@5=ra<4V@beKRO4VU8oqn18lPo$BxEsM9EI|4*-$Byh;HRw;LWjNFy& zNG)Oi)DrH!ei=LZ7l)8_Hs)fhm~WDAPH_?UWQV1u;L{Ez`S7ltjEg0`xt zqn>-%11a^C?;d};&efVj+nue~zt!R7+YVXNMGosW>>S!oXrXo*{SPf1=Xo-1n7EX) z|KOy}rH!vyEp+K<@!dhW52tgsJgOxR`SH5QA=>zU=;Y_Lo8)({9$Ueo>AM?hbBhJ59`>L0as>#nuixq5i3)^?7PUB3_Jo+WRQ6H|)kjBT++rT#O%?5{rfYA{!H z>?O7+t^K%rIqrJuAnh(=Z47Ox+3~4Uch0|l!W>@KEI&p5fBR1^?7I=VJsvxzX20|V zb7lS^{P5u2n!Q)E(?^2OAPrfEdyRBbEb0xjjB(^w&3P-i4}RNV?h<7`N*dqjts!iv z4cbe*pA&8u;bcB7<5A|+6@(n` zGq!HpI>*P&F?`b@z0K#Q4932Vbm-=QtZy)PZA5-O!Fop;^qLMIu|n52@M5zQ`go#3 z=C9aiTq<{k2R;Iit&0}hd*mBGgOB`{ye}o=wo;Y}lvUPK8<kqOXCU)>!X*a%G{l-(|F-*q0kw?Cf2j^v~ zUF0F_vy#VVWYa0~c$u<_Ux#n5yU2aorpz7=ZRC;W{eY|!d_y=J`u+*d-;15^RIP(K z2j>IEGJcFXhwVYpcWo`cG0d%L_a)dPqc|Tg_rcX8KbJ5+kakR>%_WT_aB?S$*ez>V zTS?+>NjcwrfjVUo=4^co<1w?~u&g$p$X!EDUCr1(&i>2#VcaRi zn3pm)GNwOfoXTEOba`z||L|+Z7nZPZRstIL2PuK=oh)Z5uky zi-;#QCj3(L#Yp0X>WmAZ;V0k&o1sNJ*<;~sEJ3>ruhT7kxD6A1^6NPMg_>=TFHpn;jto6tZ@k|S`+;|P30UZH3I zCEWr!`|p%Ab$zFZ{udc+0|&2yM)b4)FH=SzJWlNE(x#6Qj`@2_J$n(--ja?i z{KDve;msloWetiuJhij`<0%iiDSX>ja+a{>+5hCwv;XKIVlS_q5X}96@LF5hqO`VZ zYK~<6gZ&c&I_BWgR?^Q^{q=qpJBPGY9p4T;bEVLLK3)mSen zZPf=F5Z&Pvyz1lHNX964C6J%A*Gc9#R!f+6mPfu-kknVw)#sNPw8H_~sOk!p`wF&g z=~FlDA$KLnnb&Pb{<8O1-g&^iQe33lB5d5Z(M5OQo_*RYrexQn+!IAvu$%Qpzo*Uk zE`?RvtYGXp)~R0MzLf(?zb`>_A+am?eiF=mk9x>`2_Ha@+I|S?^qX&VUivS$m z8Wh>}JA8@z6yr^c&wUGgbFptTxC-OG1m8`;+(q=m$*I^{Jo?^^<|Nr4lG)abu6e+q z*^}@!S?lqDdkk5#jpsA)KsVuP8EXe3-<_GR2Ax~&T-Y+dF7|Z;tjWr5ALvh>(Ke;!CN9+k%6!}dmyi%09!I^(!t=$1a);F-^!i6J=OQhwROB7 zy}Xh0&{YFf&pKyaf4Sd4^v(izg0}a<&9@*t^9PTMrw#LIH|F1gZJeV`XFgf5Lk048 zL~;IFVb`!~b`8Z{3NnX2NLVNMJoZCKdQR<*1^u23KluE_o*F(hd^-3AJ9^4p5>tHE z*nsTG%iqyEkndkHKHvYucu6NTe5G}`b_WRG8rWTB?Hy3bob!86&Q$l=z1-R}P_?XI z`+du&z{__e8rTCMXZPjX5p_22u^Rfo>RNduF|OwGx-WwJ7=J6(`H8HlyIMCw7ZRrz z?b|h;*3SN018PR)!b+uL{kvtoApV#Z|J+3FzJQg)CtT+~S{%|xul(owNb3EM^wZi) zmBSxWj@SObDMzJN4*xStpg*Bw`3`LzGztAzL)pPZ`I!A zL9;&dUE#&=W}5GcFMcQAp_RT+`n$u*)sg08^W9u6UZ-~x&3E%Je&;gZG42|I!M(~q zsJ)!y%y$bfem9bL(x(E9-@|fuW>nc@a61?T|t|5oL{9KYp}V;DVw@M zbTrO|+#u_Yu;xSC$w&4GH)8|C?!p&nn|>*m`jo?VJ~=lFdR9(WUTpf4Dc%OceavOjq(@4n*utT)03<&61-3Jc#L zP`%T8EA}0uOWO2rxXPI~8D|;eRZkncJ>w1gcz$=WkMm6lWLV=O3v&d84c*mNg{*3} zC8t75fx@0@A!nF+!lyJ@TX==cCD7lHVJB#hZNy3RyV{zuN#*g*H&QJ;04@dj+l%rZ zV9%e8cQ5kX$T{&H$AasE?7?ebE%>CZ?{2p(?2uRWX_?1;PmSyiXrewg%KjF0mN*5- z_aO89Gsq=LPxjbL+vHh$ACmUj$?t7^fAMwXO9O3_NL<^)fHh%FKRP@{~F@ zuCj0k8ukQ!HwF*zy(#WX=&AP0`n9yHwB<_5X+F!o@kV4wo>DI;JNtZ(F}~n&Dwcl7 z7kX>vE_}H!Ptp>3A4S@EDb+{nVt?^@-M_#4dCkvGeePadx5@S0q@yZQX>(aKuEhU- z(Z6cU0X5MH73)Ew=D#$=Aa@Q4NpyA~*dgUXG?HBzNzBu|Od6#swztp9cu_62Kze88M8DC9w zR{IirIWn;A$UQ(yt@cUm>AVL$DSdbF~p$~bF)hggPi%h00y{RztE z*nZFG{oAa3V;VUx>Aw!0Nd4VQ9z9xPwf<8T^lpL9x!Z+3x0Dx|E9H9oo#48A7R67O zINOQqq%7@x=Aq7f<{*00rS8%Be$k(Fo^wyd{sybMLDH4Me9>4OA|CFh{Ca~~WO{Z*&mIH`m5uaqe?e?LmWXgOOz9&vA2Eso!j-bar-u-_fV*>`0v$)XGe<71~EOthrB?L9}faAzF* z`sI9hD)&dyzt`@UZ^AcCzdc3hKZ9pQz1QIHw5rjx`5nTe1m~tMUd4Jf-(wTKb}7EA zBp+JI<2z;cIO$_WyVXo$pY{Z`{{VbY^uMK~b&@gtA$|h;qb2NDJp1S;x!YX!W=fyj z2u^sH)IV4i{nl*e-rIg@|4hCOOFtYj&%Nk!(6W04YcHA{fp0Yb(*BmuzkZ{)P<_@; zXU&njY(?rV;YT8et`HsrA3|X6&hxL{D14~R77>tdaW!Bsa|$n-V$pcf8TOGi)Z6(E zMh|p-_KmW4LB4g-!rJjivsLS9_Jo}r5WA~ky2w!E)8Yd55YTSBXawM_W)W&xss#U9$QNA<&+C=7I#F4V)Q#O&CN#OD+o8R%1BTcoO z<*$zlsNSBVKd_#VK-pGXExYBe?Fx9{*UeF+*>5*={p9Di$iQc;>zv0{|7k7zRI4Ha zDe#exeG!3ko`^t8Qbb@gb(FoVO+L>1uzuE*hF{A)&&Ys}o>pSR%t^jV^;|Rf^^J2< zFN}%0cHtPSsHTlWA*B8FDJo(_pr5MJpGiu~Ix1_p3C{O6Znq{pxpVM)8?DNE=p=gq5|ZmT4sqS>U<~aRz3I(##!!!m zyX@Fz8N*MW^toE278UFlT_BG(vZ;teD{hr`(${MWXeY*&cCT~+?bO6xL$Qaw&e`6k zdtI$w8~4k~*}(BK$JOWj`OIBKR(ZfR)rSW-|3x3@?<~eU-_7(9@r6FzqNA{;FLGS$ zQH;BmCf2CLmN0m^x?tm-ltKEf$fdvcjYCJNB5h>8IcJXG-2vp0oUNApWlo*Mb3XVc zUw9zgu380O!@6WRxGe0uX=a)8^fK3C%Wd?82Z|!uJ3v^;GlF+%q+8D#sTUb0_sIHq zo&`QElJ)@ClqBVy&=v-+3R~`RGhM%)ZZo#rMjPctw>@B{%R5QerrCK}2NT-`_T2*T z;W3h~wWYB+44bjLEeu>eZ7$`PG8NQFT)x`??sOXK!kqDwy34z8-UTU>N3-+t-4Wg= z%HGT2&~<@;0~o>LTkM%YB|@Tq59%l*X?R`wNn_?=Dv$79PDT)dTg zU5APfUl{3X%i=kOJ`ZBwR3q72GDPU8hZZ(yiD>cgJ6r4L?i%hmHu~DZvfdibIphg# z9-dR^-{XYcv2S~EhB6;?IG)RN#L^SJ30Fz_;KLn{EH78uh-U_`)hMK+XWv4pW=_d;KrAJ zEB=!8GVFG(f8{>nyIidsY**%thu?61es=%fjqu|RXuYCbtyKBIOF`_#mGXh^BE5Af%&whK!jVbxXV-j!3 zIx0L>;wLy?*7zy+%v5(xi)&DsQ0YXgvMo!A?w_4>i)R zdD0#{r^TqxeuiDR30dE`Nxsvr&(rGBDXr|Km3UPXn8U5I2W-mq@HWP|6Z`gSag1Md zaIe@lGie{Y>){5QGj)m6S;G0WTxq)|bRNb1`0>u9mP2=fBVUEQl|0T7U*?pF8R*+K zYxNTH8HzqCGHsNyR$oB3khM7H(cn7t>LVY~jzQ%Aq`HV3KcZg#c+b((>~E$J}d@MIFksMq>}^eW=E&T3@^uTOKkAo8=*e&d~^NJ}=Yc3<2e6LN5+7 zu0?im+uKF5!xs&|tcnU0YqCSte){PR4cE3Pf2gVGlp&8kCWfa%-?I7D@$3A!!zaX_9T9r zcef8Euj8!0RWU!$A|3PldA-30t-V|7nU5T&&E)%fKAx2dt1c$31bk$9H9BtfHu8Dm zIQP9^SALngG%P75h zZmd`TtGPBZuye6xlk|_YvxW8)nyq3V;1cTjQGH~U`Mt-glp0+HWt>8sAikvsU$z=0W z4)h++SCoT(l(BgRery3B2EX4%nsbhlPXn^Hj&=JabjljeWk7eQZBc}G_YiqmH>jg7XFg><27VAS#E0(&6d+}d`lHmDy#e1cA7f0r@r7pO z{{BSt@ATW_rVAgr^D5hPuYdHo%2dhe5>2lnjiU-BMIo>%2~fr zJBS$Hrs3t`7Uk)Z6xz93+p8kH|HksYh zUUDx|Bf7kky%`bk#sb!^&Y)j^gg(6RTkdf~#!}bvkC4SR+_%(N;d<$7>dZcdmt-E& zm$al!Z%|f8B=@|1MBEF+mH9y{GU!|82w&31-||gFu_v7=>BfBRiruloXI$(vfgh%2 zu-`g{^~?d(*~7lOYgOFlcy-4&R;y!NaN<$-K-V+Ku4(VTCEro(TQUc}qmAv+oGpW= z_^rAefBiV$0A-xJtN8XU&r1y5LzCltGrjghy&pp7Yvx={WV!GS=YU6#VqdLRZ>GN_ zvfP6F9yg?JqujB*0-j^Z88h}9Y#36GGtkpVw9l8+^;_l}nyx`Rohi9}R|5N=q^*R< zSXV7S;zTY$fNH<8tMy%!waz?{E{a-NNbcDt*%Gd+O%L;$1=S*%s<7XLjSvTWHbgv4lwR#_OSn0O~r1LfM zJYeWZO`b5~vA+#nr>R=*DeUv4q%FFR51w#Q`hVz1U&W|^?R@;$(0oR#K3YDr;o)M3 zj{3FENAhWFDc=(>>A&yex9A&lWE`?T+s3(E(MLoNF?A5pKYlojJ(}{09+J8^;zl{I zDsrUBr>*MzV^1U!CUmUxrwyv z_Lq%$&iU%b1U2}{Q|LKreAdxd(WRW|sHc^3$eik0bQPy#bhV;iYI-gla|#_Nig4SV zbsLvj6P}c@uzGOa#)Rb8H-_874>?^fY@~dDggtEG=t5FQ=7GB-Y!THd$*!?c$!hGY z@U8F&HEuBW-f+q%d|&Ko7nrjrP%d>+O>ugxfdu?S#&`1xeE9GVd`mL-sN2*Y8$8R( znv&CI*>kha#(jP2Fk@+t(`MV_O!D7i;T{-^-2zzmSo-;I5q{kSuTJv#Z%MMn%}aDd z?QudEa_@}r#0J_u-ih8_2OmX8kaC!M!t?MF(Px{w=o6+6ZidT3M-h4zeZoze+(Gx* zYroagGsB2}(L`R&wt?8`dhdReJqvl%@g(_0jLte5F7pp~OC#@Ib^doc|KWRIc1d9LK`O_SES#4tfS`>HjS`npcj!FQr1@~ z>v75|WqX-&$Qt!2%9sb;nscQunLo&U>4Di1H;yArBQ$A_PnXP(UJyDo=IYR)=p^;O z>KH$v!a zWF3vQ|J|$yya6{eqVqN zWvypeI{cp(2Z)L3pxi9lmC+*$a*0=gy-l@pCd$w`Tq*lhB zPu2r_^9|74Hkm7^k}>;Nb03??cXMsBHHkfZv~B3`-|`#z_n>XcrfKTwRnvTae}7ra zV||TsVsl|FQsxQf+M3jRA9;Vj=zvBqr=%R5xBPz5L9M-{FZBL+ao6Lt@o8iIR{G-% zeQ{=@V!pweTW{>elvC0@-C_xRLBEPE`(mZbNuzMk7SFym+%^^atY zrqNz^&-1a%rX`G5J+mUWm!BSe`_Z9`pWhVuecsVQDl#W5EYq=*yZ)aq9xy%35|+ap zE9bPbqz1>MKVXZ1J_7E3a$k(}RlnWMtHO4j8iQ=5jby(JJoEtM#>p03IzPUCb{cG}$ zX^c(1zyD*GdmwZm`-2>+Rr*urUoO?FT6l(wvgKdRJJz`QhK4qOcB)=ae&LXHg7^#H zFVcC1{8rG1`P-zY^Q7hUdoRe?7hDyz<%6ftCWqbdjXdT`>43=>?DV1VOSetEFueQ9 zX|eyd?8v8TZ~3qvwoQ}xa)!K_@qEY1l^bQP-mT&eEx`s82~BQSan-fRgyo{sDLd;( z`gdg>Tl_DZY{1U%%8vSgXIFL~kmv6{V8{2teSVipmAnV_RBy={$(>(5`b?Dg^apD< z)8x#1H+)%Bll+sI&nED0=wa{J5{un2q`^COkHzK~%o#49igfr?gkzFL{BTE#CBh+P zdCC&*5W98)v^ps)n%~Ni9Tw#%v2-I$jN|%n*XPbChr@!deYvu_F9ca0WWO`*W2b$* z+|e9~ooHDY{2iV3b50qporC9ie}p{9&)L!+zUqY-!yC z&eF}jZXC9#*Nvs)dW|0D=l3@~N0;vFnK;ZBn^@{9c9!-p&MQqrM>q(dbXE>7+u7~W zXVQQDuao!maXnL4RWZ!TH;C{{edvlcRprBgQh(Kh!+_Gfs`#uEc-IJ?d|N5 zx6|a_SE0vYs(ZE2r_iMIkJF&TA<*F@p+D$Q=xPvj$agoPf1UoYD>)*eKdDEFB@8<3 zuF+wNMt^q4(6C78HHI(_$DT0PGxn%x$H#5K7gkUf8DD8JuFuuSw2{`&O6zCA$^6<0 z&1Et^8lbry%5rE3^fqg84Sip;>38jYA3@(o4EPc0Nx4JmHw)Y0GJ6O8CZGcg4G+xK zX;EldXn4R3{dfPFx_@O}{VnqC#mmv>(EkneX*K9Gls0!ln?D-3L!-?Z_!EI#;7;H! zzyo9enZN|V1&qpa?eVSMTzc2qO{Len()Zl3c4Mi~Z11%jN>4AZnVh$FX=%U5pPM`Z z|JYA=P9B8sUjFQ4JHF7T1)gzw`TohzL7!^*a~gg6mcKap^zwh>yB|9z`9FpT%hTab z#hmj$L5GE;_x+;ZYC4dvA6j>>MHU@vo2czmV}6h-YY25#&eO7w;AMPt&hzlol{VR1 zSvQC^v;NGr7kw`KG(|@H*c;2dBsB?pq0C#(qZjD9NdEBd?ec;&h=u!QvU>=(x9 zy0c1^{hm^{uhA|05KeS}E1mu$iOBGS)?U@3OSy>?&v%#Rp?~bND#rot<$4O;^xa>{uh*Iq0wkJtmjmiZh<^a%UI3Q^L*$XYPX$y~Iy@D$(QqrL!J~UXd$0&i9Ls zX*%b#ZrP(G`-HC8y&V0_H#NCBLTqHk?9U}HtBtz4ben!1zb%ojUD(gGZ{RecAGOk^ z`+C}QmSU^iM_Q6j!u-WYr49$NHOifMG;8jOT&I z^W4uZYQRV*VW1;z?t+{f>Eb-E$k3*~o!cm%dYXHCU#Fa=zHaL2bLSKk7R{QQUs>eL zpF6kACl9`|dGpJQDk{p#m3kom$N9tO=9kVMK4n4Wlv%eI%_}Q^IJ3N5-ifNo-{)IUPMLXMQd;&&LM#s_3# z<9e#dh^X-BFo!+H*3H`8(!-2)N#AN?#D+L{B>jWd0fYnc7WT-C?*_rQ%X{e z19|~sG#CKDk5m$H4KND08Sntp0UuBSQ~|X>J6pLcG~(8JO8QEAOLuxcLhzlPql=Tl@VA@{l)vR<5d9-g2H-#9!o#m{4!&l@ zs2j)Ke2XjHlQ|(Pds5DAw@;Zm^^R%NbML(KM|aJbarfQ#+P_wx?-bh}%5pVK*FoGUZ?wyD$Z zyzB0JXWmbReMPfo7teWs-keuTAJTjDn{=4;lvHTZBI@#!pHPh@OQ-?Gr|eR2N==ca zxKdInrIb)Bos>)~lvW_EELtg6YA&F;XEe7;cdIqGMt5rsx6W|u4Y$E?8%?*#bem1L zrQL1qaNF8lO)k4Y+Br<;2jQ{8S7A-6I{PZM4_1HLA{yw5S- zqyM%$UMr^of)pMqX$qWyA4*&^|(2X48fGf#`-OOFVjOUIpkIu#IDrw};P6162^7Fj82~F_1MEN;5Dr8Dkw6p>4LE=ppc~K~=mEq6 zaX?R?7tkB%16%?01^NM30{wvjz(C+C;A$WqZ~_THB9H_m1A~CUzz|?4kOHIv*8tZ7 z*8#(T;lK#sdSE0l3K$LC0NefJ|TlFcHWC zvVloJ4saWAJ1`lT0!#(&0Hy)cfn4BD;77n+zzpDS;2z*!U?y-Ma6jM$@_>Ax0Pq2Y zKoKwtm<<#IbAShc5@0Sc4=4r7fce0KKsitWR00ct9|I2o3xS7$M}S4ZV&Eshqreit z4=e?i0ad_q;4$E*z~jIQ;0fSKpc;4z_!;nX;Avnb5CEP5RspMlHNaY69k3qQ0Bi&{ z0h@s>z*b-zPy=iSb^x`&v%pT^Ip7z-^S~}(H}FruFM&P4KLh^)>;?W6_+P-UfER#$ zz`p@60{elNfPV*m4g3c9E$|<}0iX^z2)qo`1FrzT16~Dw54;Av4!i*z0^S7v0Q?d7 z6L1)K3pfHC1>OeU0geHG2HpkU1O5X16?h-`0Qe9%4m1FbKoigmoB&P&r-0MI8Q?7N z5zqph13m^m0X_xJ1D^q(1784N0$%}N1K$7_fL5Rl_!jsM_#XHH2>Mlx!r1^j5C((; z5kMpm1;9nnaxL&83;fFhKeD3bTG4W?Xt`FjTpQfihL&qX%eA59+R<|DXt{Q@TsvBB z7+P)^T5cFxZdkjPD_q+us^{NyE|sQkRkx_|qPE#TeDWFzKOCWkt6}Oo zbuIa)suVR;4PiDlNF`IIM0lo?@?EX2QUfV#fB0xW%6*0Eqk2;Zwo|~3d%&5yA#WVi zG>TeB&;ntU+eY0i%9VEOE#q&#Y24T`H{LLM)X3{c3?Fvgwb!Jk3>`9fP;ydYf;0Z= zs|F6}e`UYESM=%Kt7lwnkM7-K9MMsc5#eEWo7FOWetDU1ctz3NS;O-eEc~&u46xn& zCN&>Rt@8#2gC}@iiGKnAFn){`jy5w4sgOL3ogVC;F zFb{tv{&M^m(u2Y0g*-gL;G6i)X~AGDr1ExNFgOGMd;#I`oqo!L-|$2**c&pc+eCi& z8TG;7v-mZ?Cm;O1_}}55e?1s1gw*VB1cM9lWAUHDpNe0DzxR)X7kqOtSPVJ##$Sx@ z#9xWO7ylLfI{bI>8}QHLPd!PyLAvwt=i^U39Squ|X>a^~_;vUh_=oXl;Gf5zj~{!M zdI-KH7+jB^i;?S>@*Dqs{PXzd@$=4OEOzK*8>@TD^2ju0UwE};Kny#+!3kYgCee?^ zd%;}`tOPgSz&Y_HuoxP?uQ>7?JlT+Kp}8}Frx*L(LHvHb*p9K zhaYI~CC%kPzhvqx?@42oeYGuft2Hb7?V}%dS00C3^W~+U-V69+39pqSy?g8`(#wqg zkPOouQqH%58N8<{H7B?;KrT2bk2vwa1MNw?~MN7!#}_K&atC!OM8qa zij2`Sj3qx~MB-bv^xt}A#;Sg+`+C+~v9^zAUGMe1tiO)_@Pm$MT3-*8vB(&cF)IBc zeVeh$y4qr`B(R?TL;8ctAjoZmJ3I{9>=u%bTgzud&y8{JO|hGMxFyF7$qNDzVf%gES&ee=4p z>&IklxN+kRwq6$}LbRa(_Fh$aFT96xlg@}O;akHpWH3xxW3^U(_`y5U&BbNFUkx}_~A(w1|g-|?6Poc&IQ@z{NYykBFLX4l5zKxDz*I%EY&h${p} zfcqslm)=J!En9kSjmumWySj&aL-fX|`)Ho`{-O_!32VBq?Y1svy(9hDJL&KKdE(k` zYr1EwcdUz9bnG4XyMMkjS`g`iNOz>SKa+>#x1M~KV<_*sEnBYMdX;QZBD zEGC_S$hf*!;q6k0^zJobnOkzVPIs@Gw)zgwy26d7D+SjfgH~InQ!~9Uvf7^rBI#Tp&I#hkvpA_@46?cboZ04r z7ao)h{tWo}IDp($FH zIVLHuw8(3d0EI^H-w-|H-9NkEa}&_S>x9|W>rLesIe<2>crvhd9B#32sCqyu-kWZqWd)rWyTA0vSBjy~euMqA}K7ie^emJ&jm+ z;W@&aWym91GPn!iM23?_gLTWbTd&DjH+cP^oYg6-Qax*jtQqRrkbL*sN2Sr;%N9XO zeF?ZKb#=;`p=*ao*bT`N?cSqrzjN%pbcvTP@zOUWYc#NeJnZOuHDrNaq49nnr89=3 zVE@PcPYe7{3;ZA60*8C)72s}p%^56BIg#1oOu6>1Ff^Q~(&B19)cq@UPc?j}#~fh% zhRtcNzu})T-kW^PeE-!C!7>RV^9*rDwrVOeMjT>Mb1MuU%5+h4V~l{tw6Wd(jS8u` znjiE8jc7{d^WscdY9z0%Eeig;Q85$0!l<}ePL0gF|kDtVo9-_hM zcQgF+#yB;9ca0wpf)x_&wMMh%fERvnD`4@PL3+xsJpNnKepSPf5?(m9o-|*|9h|y}|A}tr^wC$%nCM^4HGzuy#J&86U393U$UuD3iZ+##0wVo^{4Ywacx}_-M7)7$=?a z4psAUC`Ny_Ar{zg@a!6ydEh zdb{9nFz_R~;PrejriUyeys6LeQ*-z8*AhY}(!S#KbS|dP2Y6?>1lC*%cP^{ zdodp{)7NZHM%v=(;bWBZdv)S`lXW-u!r2{8ggt!I+KSI1yh>MoJWs=ubiyoH5)jO_Z#??Q+2#%@8+@6OlO{s z*KF84t}*cbhjhGVkLK}L20rfz9k1D!c{J&y=D3bG|C;!^Qzl0DO?>R>&iFS--@*!P zoso{sXon9C{NaYs_}G&S{CNX^wUJy;`c2Y_ZPXDa8yaQcM|8nYHtbY6D+m;0dI;Z6>~1|IImT&AnjYpEugaoS*6K6Qk<7jH3gD zztlK2$Iod@ZxdeV*~DwMY+jx=@Kb-Ihcx+w*)Ogx{bFSzDD^e*W_u1Y@Oeh~ z+l-9I8~D8j-f!Ti8Tb)rbcCPZ;>>o~drtq|wH;hv>3H*RG4cLZJNVx+L+ZZSo-2hB zg}8-(C!N@%A%S@`(~142GyciHla6|)b9g-+K3hreggP_*{aw<3=I^AxvP=4#2`~L_ z;++N^{?fqjJ*MYs+I@d(;OD=m<4xP|F#~VT8%_Gx%NeZ>8~tV0!K_!mF7-N1I)v`_|?dI%K&hZ9bY8UFV!OL#+uKNE> zBmKQWgAj~Jdb?dbA1Nn1JLXlMF^)_z|FnUxF%V`y9@X%nCDEV%3O?>)wEEacM;UrW zjS;3hhBL{x?y!znY2b$&c-KB15o_RcG`#U6#G7V>H~HFlVZ|YrB4g5|6zt^bO zKm&iyz@L9xN0?@YhzNEn8eu}c{TSMWR=TJ8sX;~bZeGp zrB#S?3ahlbPWDrrnAAoJNxMI_ZsOmfOi_tq27hd;Cqr$9REx{ zX6DQHmNk5=L7ygmG7pz(pXnMN{oH8(u}0?Wz+b97zXBgeO6K@A+u=`_N&lFU{`n(% z!u^c&zcTQqywKJrdDmU*N9f;ZH?!Xd8~7Iv>j{|n3=Q8gk2A|N$p~-C5pC^}sKo|; zszJ{#V;CG!y7+Byu)TX20fVV`MH5#Y2c0KR^2dA3w@q2 z2tix#C%tPmd`QB^{x>qeq7mAp{56P1a_R`^-7 zhWS)-q4Ih&v%C|grQe?EmGc9Wyn|O`J8Mqq9B+PkdH%!RqSDIpht;g|{CP#*!Ugl@ zJ*>(K9w_ov4jVz-5VV&lm7U@|KpJwUj3Ue9BOD6z8mHVsQO21xP{vEIdBzM>aZ&#K zIkO)2RxBu}EH5fjRHvx4&|AP6xe~9hxX4%19;>3Ll9&~h6_l!s7T~~HMW>e}pTA(P z5lC^auF_la@VtVuxgEjg<(CwBi)rS0`K4M4?Fn!cPl~mmvg18vqtV+#%$mEPqF66k zJFc`Szua5K3A%Z7fYv}2mHCw&`I$*pmM+tJivc4e`*Vs2SS(TWyUmgh_DH zzo>lP0vc2fD4N8hM>T_WiW^b}Z$W+q)TE!e^copS;q({M_L2hw7r%}?}IhBOc zsm?@JFfhzG9Y-)BWzFQw01(3TR^}JX?a03)oD2|2x;+WAB3vh7~@BpllW%<|`|$kObyyEJx>h znUgc%dESh>C#T<@?ST{J-tO@lNN-_9nYS3~rG>p-jQAbQGAm=6@=m;MiaY%_@01A> zrf24QbJN|oWqLzS(84~7V0EFVXD4y|mR_Fc{c#bDXHMnA^UEHB-4=RFp}ij$RmeMf zqoM*r^iI}r_rUEk4Dyl$)x4s4^U8iKC4vQL%&WvZdqIAAVQAI{4~2V$G)8Cu?R0Rb z!g-4pUL3$I59fo)*Ej!R^$%7-3Z^QH%gY|>I1>C1CMjFOX|mA5cOgI8%C|6Igi-!n z7*c4+i`$`7!_LZ|Gq+Q4oq9u2vo&$i(QR{!=-ki%!g^>t4qAJSGugD}f3wcn`nhYb zS!d*Ohq%^-C}OZod9!nG_nLAXk`^hRtFelX6V)b*(CKs*N3ugpZL%f=4y5YUUY8@N z{?U?a1fUhKqvV$>pm2gt1WYs1nxVz)#CW^~WeZ9>je}0FL*qdRvr8Fx+K}qpCYsRf z7`94NVnX9~p{_0i$K=`O&v6M z$9xUVg+$gnwd0)>qijCv!9PfsT622aDUaqAbx$KnG)W|-8iuNb(88}Q*ZpGiX?E#? zVa53s#cEjL!=(h%{mODRth{J$z95aK`N*|l8X*m=!&Ht!+a*;{CNnVVYAUGhUsY(z)%nv^X8$pf-5RmFq`09oPaK9{M2e?zA7jv zFZ!|hW-hw{%x5ZRJ_;J9-Tu40boN{n?TSzZ|G)Vx`)%MwCTs11veseO>at<`0eX}hJm=|keQS2ca48%m-yzgzv*`k-?jYr8u86y zn(N`Fe_l4qhPbZj`FJPwH{+XjGt+;wONV#O|G_Tt&Gl5%=R2RBoY{XSauE+wM4>@( z=6@AThY!<)ZN|8au#0L=L2di|OIWc*dc71}Z5oA$@^M*O`-{mu7gzGgdaCawfA z-<#|IH9vsF<#lO4Grs9>CroJkj-JpXIwg@1XT~#8zu-Y|X8um2C!JcVO1aE{hTk>* zFTqPrBf5k$@6`woDiJOl-=c{oOnyk|VrHyiDJ0B!1Y3^*Fy^fs3^+#|&(#`p9ZC`r6 zJMHZyb|}KgNtKf^Iw4f0|Md_4sEQ~?2ue{^T+)V0P?XXFg@0mEHL__$ouWc$OOE$u z=K0o}J3*UD{K1ZO`{w=L$IQH)9q;VSd_COW;`MkKr3!XGD{_nlrQp)Nq=DF8wiK?L zSuNLHR21EutCA4WYQbX}m6(;hlSV3p9#N8`9WPH6a^|4hyaFd`w_6ONb326m-2R1h zRNBo*N_NPi-FazuUfL0zlffcN&BQw5?|LmCH!)apZms( z&pvncp~Xx0Bo9npePQC7^4^>8D7&azPgP6 z0_c}2_t$0c%VqGnGI%@SD*T-3GytpEGB$;Qg!Cmr*cM*TWvp(s0I0tWNIWRH*q!|XoPlt?9w z?nqz45VFz|Jvtmg2a!blG$`6U=>2A7$k5}-{uJP3ZAYy^Gj%FF95vEbJe6bzBPSzz zG&Q83G!m(3+!{%zPC`IvQn+7%`V@0Bm>CwT*^!`XZaf?;s zK5wmcX)_fyG8tRtd(yFpWw1oNj|U~?&!qf2IqM(FW)eGw_wLd6>_W7j9q9-k?&@mS zclzu6d^qzkFZ2iT=Y>4}5ick{{~q=dbP^g*)a_{)4!rCg*>PxmrhYmq`*U87zl5t2 zXF=?p4117fGtKo_>Hk`&t6wKLh{(p;W!}xB);c1yqPfHxf z6FC%e*)ujA$2>}tHk^-(Qt(q7j=m^O+3@)`gYY#Qej6zRy=23e+Hg1PM!=1L8v!>0 zZUo#2{QpIuqxZssMi+LU4lq{O`Z!#Dn}U!-eh_tLrwfp_n#kj;)a)hBpFkd8ab~AD z--kTDQq4|s{vqV?RciKW&UYe@uTZmj&L2b`UwLLnIo}9;f4;DlDC+m;LC%vlvHt-8 zw24Zz)An7r)@tLG8!>-&%DSyE?b|epiWejW{aUWSdp(l7Z)mwW?d887&|bb?p?NN9 zZ``offP;7a!a-GGy5F~{xoH14-^Pzd_kRdtmTl_M#`ZspSuOXjwOkwD{}M1W4?wkN zVsHWJcTozWiT&^SKy*K#7b~9!&~prGgns+`{k~0SKug3I{exXQx7*yKP3*=3!2Ww+ z55>&9ccV}^HoF@far@crkSn^#_h`A_b!oYewXv&J+W3+}M;*rD4lQ?48?VrEf6;Q6 zweetD8+)bd;bVpAyvSUDMv6Lwj%vBzhk8P}*F)WHxwm?xFEyr(FWR~dDyij~x748L zd@tOzvFp`Vvo^8zRhX+oT{kMIaCzp+zY2x=DeN!Wc=wi?n6`ZlH};)<0bPu}qCR{q z^hoGf=ux^aGw*0ZUo#2xDjw8@IQ(`l&@8($&{tm?@{|k zEF;4r30NV6tk`gN=HVr zJw@BLM8X(|BtqsucF0It#g#d($@MZuG;6}rT%grR8fGNXC3%~!XG6t(xQ2}%4358F zDD(oI2099~`dXoI9%u*9OF&0IC=}Mfmx?^lZF2Ev4d9qOr#qQvxZ1N}`I4$U$hH9X z1r$hMOsQ^BY7Y9AovIpT2iD(r&(1A;xftzX8qaz`uLkvH+6D9x_@(Cx1-|AQQmUWu zHY+t_6|IV@FI8#)2q{%f%K#HuZW)B@ci=Y+c6^PsQK|lxH>}i5RD>1vn~R#2ZBJB& zl)ACStxAWtW2sUXQnrN@wNa@7%|@k4TgKPW-Uc5rXg93|VLh+H?>Jz5O}AN5A5=&x z_9mzx#}6^BhkI~6ZfX(aWtecqj`IMy$&G*;0XG6}1l$O?5pW~mM!=1L8-dR`0<^!4 z_OsD@IPQ_dMEvVAGrxCCZlHt3E1qcojcYsB|!Lj zSuoXeO6t*GH!6>)6tj;DqCFHxSXJfi8+Y7gN+4R|TwL5sc}HaV6i4ch#Ea63S7Cn_0K(oQ?Wuq0az8QgMN5&}1f-IA zxG^t-9t_-kjeA8sqojg}=TW%qfL|N@iYlI!ta@F~x(a23vQ=4)ef?(SAwVmkmbg;y z@pwK~Rv^JL;Rl&byo%d__d?$1-GdT-VYhi{pX-{{UYKcn0HMI#A&^h&oi~kNz!wE= zbuD?Pu&onF)U*O(%WeR&aesyd22(>upf4Lw#8RgXGZ0CqOe+%@io}xv+~FI@n9)Ez zbc0>^c|Z+ysgSK>3mqjtoui^L`oUTNK_7QQs4%pcmQbiXODII1g$mSK?-FWdxy}LZ z@ct}f*9z#`p0JyEn2%|-c9b#fEMD06KIwRlPz~{E2aex;5T|jQyd8v$huzN9pg^!5 zn2y5F13iz%T@J$KYw$xq^nZYG+=q{TfkwxSyq4punRA@H z2XJ+v3(f@r$LHIbbdKMAPQd_tvr`#(i|gNfZsK`~Ps#n~bk5=;;A#m!e=dW6!u{NA zu)Wd`wn3Xa!dL%o{ z;LJukVOT~CjvAEI;3rJQTJZ$b7qjBiw=TbL^G1jkgbz1E{dFBi#bUFx=1!)!wjXH>wd+S(TDrpBdUvR? zJ&cNQ)90zBxBu9I0t?CQTuPivA^SKr?e+bBa;>S@!@xHael88fC?f?_> zXGVssNFPwk6m*bs1Ies^Fp?Q${@6$o)C9Fm=10j%!_466COe~p%rp`aG?2M;!eV}2 zeLrOV11VrFV;HXdbe7*t!Ev7m^BaTmAsLK;6XKX3mWK>p1VlP*nAV8!CLSQ+gz`{m zWGEhmYI2W#aIDA=k1#Iz0A}=M2S5|nlHhrlEI-3VqP{-UI7yNOyo^#F;z3!2;SAM0 zZzcy4aI7q|VEp-v49=7O4)6Lf-0?*7AENlaiizanrA1(P-3mXP`;eUGNApw;oH&ob zEZNh11>a3kM)ow{B1-csP~nNjVd8f~2In$lPxBX|xXy)%Fy$%w6%weCG0-|)E$qm(?sO7Mql=eiQqrgcAR4)0$^Pl$m z(>pZL|J43bY2QE&Rhg0W2ZT#X^`l-vWu@i^11J9{5Fke7t3jbzCG9%|JAYN@b0UND2rOT!&r8W3$2WVT?m%<~gp{}I1*OAL zI|%t|2mcS?OW99J`xb}3Q?~*DCx#}A>xChWzvRZLyRwY^J<=ZMarWf2zf;;f<99{c zCc4zl-!Ze40gJu5)a-LRC literal 0 HcmV?d00001 diff --git a/native/src/main/resources/linux_x86_64/velocity-compress-musl.so b/native/src/main/resources/linux_x86_64/velocity-compress-musl.so new file mode 100755 index 0000000000000000000000000000000000000000..57a25fea8a220427ac0c9d70e0fefc31a9d1bc33 GIT binary patch literal 103696 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 z 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

1cGLcub@}IQ9NL6BIaB6v@g` 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 KRnUbV)xQCXxPZO@ literal 0 HcmV?d00001 From 3c23ee75768c695a6c0afbc73f3ac11184479c61 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 7 Sep 2024 13:14:04 -0400 Subject: [PATCH 15/38] Fix typo for musl crypto --- .../src/main/java/com/velocitypowered/natives/util/Natives.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/main/java/com/velocitypowered/natives/util/Natives.java b/native/src/main/java/com/velocitypowered/natives/util/Natives.java index 8c99fe51a..3a0bd1f22 100644 --- a/native/src/main/java/com/velocitypowered/natives/util/Natives.java +++ b/native/src/main/java/com/velocitypowered/natives/util/Natives.java @@ -133,7 +133,7 @@ public class Natives { "OpenSSL 1.1.x (Linux aarch64)", NativeVelocityCipher.FACTORY), // Ubuntu 20.04 new NativeCodeLoader.Variant<>(NativeConstraints.LINUX_AARCH64_MUSL, copyAndLoadNative("/linux_aarch64/velocity-cipher-ossl30x-musl.so"), - "OpenSSL 1.1.x (Linux aarch64, musl)", NativeVelocityCipher.FACTORY), // Alpine 3.18 + "OpenSSL 3.x.x (Linux aarch64, musl)", NativeVelocityCipher.FACTORY), // Alpine 3.18 new NativeCodeLoader.Variant<>(NativeConstraints.MACOS_AARCH64, copyAndLoadNative("/macos_arm64/velocity-cipher.dylib"), From 0cd069ecbfa95c559d4f97282a9208f4853dbb51 Mon Sep 17 00:00:00 2001 From: Nostal Yuu Date: Thu, 12 Sep 2024 03:40:25 +0800 Subject: [PATCH 16/38] Fix exception while serializing JsonArray (#1426) --- .../proxy/protocol/packet/chat/ComponentHolder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java index a4d914634..0935f8ea6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java @@ -173,21 +173,21 @@ public class ComponentHolder { case 1://BinaryTagTypes.BYTE: byte[] bytes = new byte[jsonArray.size()]; for (int i = 0; i < bytes.length; i++) { - bytes[i] = (Byte) jsonArray.get(i).getAsNumber(); + bytes[i] = jsonArray.get(i).getAsNumber().byteValue(); } return ByteArrayBinaryTag.byteArrayBinaryTag(bytes); case 3://BinaryTagTypes.INT: int[] ints = new int[jsonArray.size()]; for (int i = 0; i < ints.length; i++) { - ints[i] = (Integer) jsonArray.get(i).getAsNumber(); + ints[i] = jsonArray.get(i).getAsNumber().intValue(); } return IntArrayBinaryTag.intArrayBinaryTag(ints); case 4://BinaryTagTypes.LONG: long[] longs = new long[jsonArray.size()]; for (int i = 0; i < longs.length; i++) { - longs[i] = (Long) jsonArray.get(i).getAsNumber(); + longs[i] = jsonArray.get(i).getAsNumber().longValue(); } return LongArrayBinaryTag.longArrayBinaryTag(longs); From 6f6d55e9e6f7c88d0781f027533221044c3aa613 Mon Sep 17 00:00:00 2001 From: powercas_gamer Date: Sat, 14 Sep 2024 13:02:08 +0200 Subject: [PATCH 17/38] [ci skip] make 'runShadow' workingDir use the 'run' directory (#1398) --- proxy/build.gradle.kts | 9 +++++++++ .../java/com/velocitypowered/proxy/VelocityServer.java | 9 ++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/proxy/build.gradle.kts b/proxy/build.gradle.kts index 5e1387b06..035ff24de 100644 --- a/proxy/build.gradle.kts +++ b/proxy/build.gradle.kts @@ -92,6 +92,15 @@ tasks { dependsOn(configurateBuildTask) from(zipTree(configurateBuildTask.map { it.outputs.files.singleFile })) } + + runShadow { + workingDir = file("run").also(File::mkdirs) + standardInput = System.`in` + } + named("run") { + workingDir = file("run").also(File::mkdirs) + standardInput = System.`in` // Doesn't work? + } } dependencies { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 004243616..be0c8de9b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -100,6 +100,7 @@ import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.translation.TranslationRegistry; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.bstats.MetricsBase; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; @@ -263,7 +264,13 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { this.cm.queryBind(configuration.getBind().getHostString(), configuration.getQueryPort()); } - Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics()); + final String defaultPackage = new String( + new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's' }); + if (!MetricsBase.class.getPackage().getName().startsWith(defaultPackage)) { + Metrics.VelocityMetrics.startMetrics(this, configuration.getMetrics()); + } else { + logger.warn("debug environment, metrics is disabled!"); + } } private void registerTranslations() { From 2299b78ad36d682b7c5805505e39f2691ae33fdd Mon Sep 17 00:00:00 2001 From: skbeh <60107333+skbeh@users.noreply.github.com> Date: Sat, 14 Sep 2024 11:13:57 +0000 Subject: [PATCH 18/38] fix: apply message in `PlayerChatEvent` when handling `SessionPlayerChatPacket` (#1411) Fix 1.19.3+ unsigned chat not being changed by `PlayerChatEvent`. --- .../proxy/protocol/packet/chat/session/SessionChatHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatHandler.java index 0731f64ed..74b5747f9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionChatHandler.java @@ -71,7 +71,8 @@ public class SessionChatHandler implements ChatHandler invalidChange(logger, player); return null; } - return this.player.getChatBuilderFactory().builder().message(packet.message) + return this.player.getChatBuilderFactory().builder() + .message(chatResult.getMessage().orElse(packet.getMessage())) .setTimestamp(packet.timestamp) .setLastSeenMessages(newLastSeenMessages) .toServer(); From 78f6cfc26cbbb49c5b1479da3c9ea594564eb95f Mon Sep 17 00:00:00 2001 From: Timon Date: Sat, 14 Sep 2024 13:18:23 +0200 Subject: [PATCH 19/38] feat: Require any ConsoleCommandSender for the shutdown command (#1428) --- .../velocitypowered/proxy/command/builtin/ShutdownCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ShutdownCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ShutdownCommand.java index cb9e44f81..402a0ce89 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ShutdownCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/ShutdownCommand.java @@ -23,6 +23,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.proxy.VelocityServer; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -43,7 +44,7 @@ public final class ShutdownCommand { */ public static BrigadierCommand command(final VelocityServer server) { return new BrigadierCommand(LiteralArgumentBuilder.literal("shutdown") - .requires(source -> source == server.getConsoleCommandSource()) + .requires(source -> source instanceof ConsoleCommandSource) .executes(context -> { server.shutdown(true); return Command.SINGLE_SUCCESS; From 4eb02c8d383dbbb566ae020b6b4f55a529075a92 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 15 Sep 2024 13:45:34 -0400 Subject: [PATCH 20/38] Do not store the dimension registry for each connected server This was unused and did little more than add unnecessary memory usage. The only time we need the dimension registry is during server switching - once that is done, we no longer need the registry. --- .../connection/backend/VelocityServerConnection.java | 11 ----------- .../connection/client/ClientPlaySessionHandler.java | 2 -- 2 files changed, 13 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index edf3a9148..af3f81796 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -53,8 +53,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import net.kyori.adventure.nbt.CompoundBinaryTag; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; @@ -72,7 +70,6 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, private boolean gracefulDisconnect = false; private BackendConnectionPhase connectionPhase = BackendConnectionPhases.UNKNOWN; private final Map pendingPings = new HashMap<>(); - private @MonotonicNonNull CompoundBinaryTag activeDimensionRegistry; /** * Initializes a new server connection. @@ -366,12 +363,4 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, public boolean hasCompletedJoin() { return hasCompletedJoin; } - - public CompoundBinaryTag getActiveDimensionRegistry() { - return activeDimensionRegistry; - } - - public void setActiveDimensionRegistry(CompoundBinaryTag activeDimensionRegistry) { - this.activeDimensionRegistry = activeDimensionRegistry; - } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index fed61693f..b41a24ccf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -565,8 +565,6 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } } - destination.setActiveDimensionRegistry(joinGame.getRegistry()); // 1.16 - // Remove previous boss bars. These don't get cleared when sending JoinGame, thus the need to // track them. for (UUID serverBossBar : serverBossBars) { From ffa78d2a92a06b44b06652bd03af0c4a62ea52a6 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 15 Sep 2024 17:00:15 -0400 Subject: [PATCH 21/38] Small clean-ups in event manager. --- .../velocitypowered/proxy/event/EventTypeTracker.java | 5 +++-- .../proxy/event/VelocityEventManager.java | 11 ----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/event/EventTypeTracker.java b/proxy/src/main/java/com/velocitypowered/proxy/event/EventTypeTracker.java index 808e79b92..f96de135c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/event/EventTypeTracker.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/event/EventTypeTracker.java @@ -33,8 +33,9 @@ class EventTypeTracker { } public Collection> getFriendsOf(final Class eventType) { - if (friends.containsKey(eventType)) { - return friends.get(eventType); + ImmutableSet> existingFriends = friends.get(eventType); + if (existingFriends != null) { + return existingFriends; } final Collection> types = getEventTypes(eventType); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java b/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java index ae0295653..f00c3877c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java @@ -156,17 +156,6 @@ public class VelocityEventManager implements EventManager { } } - enum AsyncType { - /** - * The complete event will be handled on an async thread. - */ - ALWAYS, - /** - * The event will never run async, everything is handled on the netty thread. - */ - NEVER - } - static final class HandlersCache { final HandlerRegistration[] handlers; From 4f227badc20dc30b0f6d84b5349c8809481dcbb1 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 14 May 2023 04:32:58 -0400 Subject: [PATCH 22/38] Reintroduce sync event execution to the Velocity event system This required a not-insubstantial number of bug fixes, since the sync support had bit-rotted somewhat. This PR also corrects a number of bugs. Finally. the per-plugin executor services are now used to execute all async event tasks. --- .../api/event/EventManager.java | 18 ++ .../velocitypowered/api/event/PostOrder.java | 2 +- .../velocitypowered/api/event/Subscribe.java | 34 ++-- .../velocitypowered/proxy/VelocityServer.java | 1 - .../proxy/command/VelocityCommandManager.java | 9 +- .../proxy/event/VelocityEventManager.java | 154 +++++++++++++----- .../proxy/command/CommandTestSuite.java | 11 -- .../proxy/event/EventTest.java | 146 ++++++++++++----- .../proxy/testutil/FakePluginManager.java | 29 ++-- 9 files changed, 292 insertions(+), 112 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/event/EventManager.java b/api/src/main/java/com/velocitypowered/api/event/EventManager.java index 63cae6f32..b8702ab35 100644 --- a/api/src/main/java/com/velocitypowered/api/event/EventManager.java +++ b/api/src/main/java/com/velocitypowered/api/event/EventManager.java @@ -45,10 +45,28 @@ public interface EventManager { * @param postOrder the order in which events should be posted to the handler * @param handler the handler to register * @param the event type to handle + * @deprecated use {@link #register(Object, Class, short, EventHandler)} instead */ + @Deprecated void register(Object plugin, Class eventClass, PostOrder postOrder, EventHandler handler); + /** + * Requests that the specified {@code handler} listen for events and associate it with the {@code + * plugin}. + * + *

Note that this method will register a non-asynchronous listener by default. If you want to + * use an asynchronous event handler, return {@link EventTask#async(Runnable)} from the handler.

+ * + * @param plugin the plugin to associate with the handler + * @param eventClass the class for the event handler to register + * @param postOrder the relative order in which events should be posted to the handler + * @param handler the handler to register + * @param the event type to handle + */ + void register(Object plugin, Class eventClass, short postOrder, + EventHandler handler); + /** * Fires the specified event to the event bus asynchronously. This allows Velocity to continue * servicing connections while a plugin handles a potentially long-running operation such as a diff --git a/api/src/main/java/com/velocitypowered/api/event/PostOrder.java b/api/src/main/java/com/velocitypowered/api/event/PostOrder.java index dde8a4379..0d52ed2c0 100644 --- a/api/src/main/java/com/velocitypowered/api/event/PostOrder.java +++ b/api/src/main/java/com/velocitypowered/api/event/PostOrder.java @@ -12,6 +12,6 @@ package com.velocitypowered.api.event; */ public enum PostOrder { - FIRST, EARLY, NORMAL, LATE, LAST + FIRST, EARLY, NORMAL, LATE, LAST, CUSTOM } diff --git a/api/src/main/java/com/velocitypowered/api/event/Subscribe.java b/api/src/main/java/com/velocitypowered/api/event/Subscribe.java index bee71a3cb..abb96c949 100644 --- a/api/src/main/java/com/velocitypowered/api/event/Subscribe.java +++ b/api/src/main/java/com/velocitypowered/api/event/Subscribe.java @@ -22,24 +22,38 @@ public @interface Subscribe { /** * The order events will be posted to this listener. * + * @deprecated specify the order using {@link #priority()} instead * @return the order */ + @Deprecated PostOrder order() default PostOrder.NORMAL; /** - * Whether the handler must be called asynchronously. + * The priority of this event handler. Priorities are used to determine the order in which event + * handlers are called. The higher the priority, the earlier the event handler will be called. * - *

This option currently has no effect, but in the future it will. In Velocity 3.0.0, - * all event handlers run asynchronously by default. You are encouraged to determine whether or - * not to enable it now. This option is being provided as a migration aid.

+ *

Note that due to compatibility constraints, you must specify {@link PostOrder#CUSTOM} + * in order to use this field.

* - *

If this method returns {@code true}, the method is guaranteed to be executed - * asynchronously. Otherwise, the handler may be executed on the current thread or - * asynchronously. This still means you must consider thread-safety in your - * event listeners as the "current thread" can and will be different each time.

+ * @return the priority + */ + short priority() default Short.MIN_VALUE; + + /** + * Whether the handler must be called asynchronously. By default, all event handlers are called + * asynchronously. * - *

If any method handler targeting an event type is marked with {@code true}, then every - * handler targeting that event type will be executed asynchronously.

+ *

For performance (for instance, if you use {@link EventTask#withContinuation}), you can + * optionally specify false. This option will become {@code false} by default + * in a future release of Velocity.

+ * + *

If this is {@code true}, the method is guaranteed to be executed asynchronously. Otherwise, + * the handler may be executed on the current thread or asynchronously. This still means + * you must consider thread-safety in your event listeners as the "current thread" can + * and will be different each time.

+ * + *

Note that if any method handler targeting an event type is marked with {@code true}, then + * every handler targeting that event type will be executed asynchronously.

* * @return Requires async */ diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index be0c8de9b..2a362ccc9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -540,7 +540,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { eventManager.fire(new ProxyShutdownEvent()).join(); - timedOut = !eventManager.shutdown() || timedOut; timedOut = !scheduler.shutdown() || timedOut; if (timedOut) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index a4fab9321..24fc6a059 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -49,6 +49,8 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; @@ -71,6 +73,7 @@ public class VelocityCommandManager implements CommandManager { private final SuggestionsProvider suggestionsProvider; private final CommandGraphInjector injector; private final Map commandMetas; + private final ExecutorService asyncExecutor; /** * Constructs a command manager. @@ -89,6 +92,7 @@ public class VelocityCommandManager implements CommandManager { this.suggestionsProvider = new SuggestionsProvider<>(this.dispatcher, this.lock.readLock()); this.injector = new CommandGraphInjector<>(this.dispatcher, this.lock.readLock()); this.commandMetas = new ConcurrentHashMap<>(); + this.asyncExecutor = ForkJoinPool.commonPool(); // TODO: remove entirely } public void setAnnounceProxyCommands(boolean announceProxyCommands) { @@ -266,7 +270,7 @@ public class VelocityCommandManager implements CommandManager { return false; } return executeImmediately0(source, commandResult.getCommand().orElse(event.getCommand())); - }, eventManager.getAsyncExecutor()); + }, asyncExecutor); } @Override @@ -275,8 +279,7 @@ public class VelocityCommandManager implements CommandManager { Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(cmdLine, "cmdLine"); - return CompletableFuture.supplyAsync( - () -> executeImmediately0(source, cmdLine), eventManager.getAsyncExecutor()); + return CompletableFuture.supplyAsync(() -> executeImmediately0(source, cmdLine), asyncExecutor); } /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java b/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java index f00c3877c..4c48068cb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java @@ -25,7 +25,6 @@ import com.google.common.base.VerifyException; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.reflect.TypeToken; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.EventHandler; import com.velocitypowered.api.event.EventManager; @@ -38,6 +37,7 @@ import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.proxy.event.UntargetedEventHandler.EventTaskHandler; import com.velocitypowered.proxy.event.UntargetedEventHandler.VoidHandler; import com.velocitypowered.proxy.event.UntargetedEventHandler.WithContinuationHandler; +import com.velocitypowered.proxy.util.collect.Enum2IntMap; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; @@ -55,9 +55,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.BiConsumer; @@ -76,6 +73,14 @@ import org.lanternpowered.lmbda.LambdaType; */ public class VelocityEventManager implements EventManager { + private static final Enum2IntMap POST_ORDER_MAP = new Enum2IntMap.Builder<>(PostOrder.class) + .put(PostOrder.FIRST, Short.MAX_VALUE - 1) + .put(PostOrder.EARLY, Short.MAX_VALUE / 2) + .put(PostOrder.NORMAL, 0) + .put(PostOrder.LATE, Short.MIN_VALUE / 2) + .put(PostOrder.LAST, Short.MIN_VALUE + 1) + .put(PostOrder.CUSTOM, 0) + .build(); private static final Logger logger = LogManager.getLogger(VelocityEventManager.class); private static final MethodHandles.Lookup methodHandlesLookup = MethodHandles.lookup(); @@ -87,9 +92,8 @@ public class VelocityEventManager implements EventManager { LambdaType.of(WithContinuationHandler.class); private static final Comparator handlerComparator = - Comparator.comparingInt(o -> o.order); + Collections.reverseOrder(Comparator.comparingInt(o -> o.order)); - private final ExecutorService asyncExecutor; private final PluginManager pluginManager; private final ListMultimap, HandlerRegistration> handlersByType = @@ -112,9 +116,6 @@ public class VelocityEventManager implements EventManager { */ public VelocityEventManager(final PluginManager pluginManager) { this.pluginManager = pluginManager; - this.asyncExecutor = Executors - .newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder() - .setNameFormat("Velocity Async Event Executor - #%d").setDaemon(true).build()); } /** @@ -140,6 +141,7 @@ public class VelocityEventManager implements EventManager { final short order; final Class eventType; final EventHandler handler; + final AsyncType asyncType; /** * The instance of the {@link EventHandler} or the listener instance that was registered. @@ -147,20 +149,40 @@ public class VelocityEventManager implements EventManager { final Object instance; public HandlerRegistration(final PluginContainer plugin, final short order, - final Class eventType, final Object instance, final EventHandler handler) { + final Class eventType, final Object instance, final EventHandler handler, + final AsyncType asyncType) { this.plugin = plugin; this.order = order; this.eventType = eventType; this.instance = instance; this.handler = handler; + this.asyncType = asyncType; } } + enum AsyncType { + /** + * The event will never run async, everything is handled on the netty thread. + */ + NEVER, + /** + * The event will initially start on the thread calling the {@code fire} method, and possibly + * switch over to an async thread. + */ + SOMETIMES, + /** + * The complete event will be handled on an async thread. + */ + ALWAYS + } + static final class HandlersCache { + final AsyncType asyncType; final HandlerRegistration[] handlers; - HandlersCache(final HandlerRegistration[] handlers) { + HandlersCache(AsyncType asyncType, final HandlerRegistration[] handlers) { + this.asyncType = asyncType; this.handlers = handlers; } } @@ -183,7 +205,15 @@ public class VelocityEventManager implements EventManager { } baked.sort(handlerComparator); - return new HandlersCache(baked.toArray(new HandlerRegistration[0])); + + AsyncType asyncType = AsyncType.NEVER; + for (HandlerRegistration registration : baked) { + if (registration.asyncType.compareTo(asyncType) > 0) { + asyncType = registration.asyncType; + } + } + + return new HandlersCache(asyncType, baked.toArray(new HandlerRegistration[0])); } /** @@ -219,15 +249,17 @@ public class VelocityEventManager implements EventManager { static final class MethodHandlerInfo { final Method method; + final AsyncType asyncType; final @Nullable Class eventType; final short order; final @Nullable String errors; final @Nullable Class continuationType; - private MethodHandlerInfo(final Method method, final @Nullable Class eventType, - final short order, final @Nullable String errors, + private MethodHandlerInfo(final Method method, final AsyncType asyncType, + final @Nullable Class eventType, final short order, final @Nullable String errors, final @Nullable Class continuationType) { this.method = method; + this.asyncType = asyncType; this.eventType = eventType; this.order = order; this.errors = errors; @@ -291,17 +323,41 @@ public class VelocityEventManager implements EventManager { } } } + AsyncType asyncType = AsyncType.NEVER; + final Class returnType = method.getReturnType(); if (handlerAdapter == null) { - final Class returnType = method.getReturnType(); if (returnType != void.class && continuationType == Continuation.class) { errors.add("method return type must be void if a continuation parameter is provided"); } else if (returnType != void.class && returnType != EventTask.class) { - errors.add("method return type must be void or EventTask"); + errors.add("method return type must be void, AsyncTask, " + + "EventTask.Basic or EventTask.WithContinuation"); + } else if (returnType == EventTask.class) { + // technically, for compatibility, we *should* assume that the method must be invoked + // async, however, from examining some publicly-available plugins, developers did + // generally follow the contract and returned an EventTask only if they wanted this + // behavior. enable it for them. + asyncType = AsyncType.SOMETIMES; } + } else { + // for custom handlers, we always expect a return type of EventTask. this feature appears + // to have not been used in the wild AFAIK, so it gets the new behavior by default + asyncType = AsyncType.SOMETIMES; + } + + if (paramCount == 1 && returnType == void.class && subscribe.async()) { + // these are almost always a dead giveaway of a plugin that will need its handlers + // run async, so unless we're told otherwise, we'll assume that's the case + asyncType = AsyncType.ALWAYS; + } + + final short order; + if (subscribe.order() == PostOrder.CUSTOM) { + order = subscribe.priority(); + } else { + order = (short) POST_ORDER_MAP.get(subscribe.order()); } - final short order = (short) subscribe.order().ordinal(); final String errorsJoined = errors.isEmpty() ? null : String.join(",", errors); - collected.put(key, new MethodHandlerInfo(method, eventType, order, errorsJoined, + collected.put(key, new MethodHandlerInfo(method, asyncType, eventType, order, errorsJoined, continuationType)); } final Class superclass = targetClass.getSuperclass(); @@ -340,12 +396,29 @@ public class VelocityEventManager implements EventManager { @SuppressWarnings("unchecked") public void register(final Object plugin, final Class eventClass, final PostOrder order, final EventHandler handler) { + if (order == PostOrder.CUSTOM) { + throw new IllegalArgumentException( + "This method does not support custom post orders. Use the overload with short instead." + ); + } + register(plugin, eventClass, (short) POST_ORDER_MAP.get(order), handler, AsyncType.ALWAYS); + } + + @Override + public void register(Object plugin, Class eventClass, short postOrder, + EventHandler handler) { + register(plugin, eventClass, postOrder, handler, AsyncType.SOMETIMES); + } + + private void register(Object plugin, Class eventClass, short postOrder, + EventHandler handler, AsyncType asyncType) { final PluginContainer pluginContainer = pluginManager.ensurePluginContainer(plugin); requireNonNull(eventClass, "eventClass"); requireNonNull(handler, "handler"); final HandlerRegistration registration = new HandlerRegistration(pluginContainer, - (short) order.ordinal(), eventClass, handler, (EventHandler) handler); + postOrder, eventClass, handler, (EventHandler) handler, + AsyncType.ALWAYS); register(Collections.singletonList(registration)); } @@ -375,7 +448,7 @@ public class VelocityEventManager implements EventManager { final EventHandler handler = untargetedHandler.buildHandler(listener); registrations.add(new HandlerRegistration(pluginContainer, info.order, - info.eventType, listener, handler)); + info.eventType, listener, handler, info.asyncType)); } register(registrations); @@ -462,10 +535,13 @@ public class VelocityEventManager implements EventManager { private void fire(final @Nullable CompletableFuture future, final E event, final HandlersCache handlersCache) { - // In Velocity 1.1.0, all events were fired asynchronously. As Velocity 3.0.0 is intended to be - // largely (albeit not 100%) compatible with 1.1.x, we also fire events async. This behavior - // will go away in Velocity Polymer. - asyncExecutor.execute(() -> fire(future, event, 0, true, handlersCache.handlers)); + final HandlerRegistration registration = handlersCache.handlers[0]; + if (registration.asyncType == AsyncType.ALWAYS) { + registration.plugin.getExecutorService().execute( + () -> fire(future, event, 0, true, handlersCache.handlers)); + } else { + fire(future, event, 0, false, handlersCache.handlers); + } } private static final int TASK_STATE_DEFAULT = 0; @@ -494,6 +570,7 @@ public class VelocityEventManager implements EventManager { private final @Nullable CompletableFuture future; private final boolean currentlyAsync; private final E event; + private final Thread firedOnThread; // This field is modified via a VarHandle, so this field is used and cannot be final. @SuppressWarnings({"UnusedVariable", "FieldMayBeFinal", "FieldCanBeLocal"}) @@ -516,6 +593,7 @@ public class VelocityEventManager implements EventManager { this.event = event; this.index = index; this.currentlyAsync = currentlyAsync; + this.firedOnThread = Thread.currentThread(); } @Override @@ -526,8 +604,8 @@ public class VelocityEventManager implements EventManager { } /** - * Executes the task and returns whether the next one should be executed immediately after this - * one without scheduling. + * Executes the task and returns whether the next handler should be executed immediately + * after this one, without additional scheduling. */ boolean execute() { state = TASK_STATE_EXECUTING; @@ -569,7 +647,18 @@ public class VelocityEventManager implements EventManager { } if (!CONTINUATION_TASK_STATE.compareAndSet( this, TASK_STATE_EXECUTING, TASK_STATE_CONTINUE_IMMEDIATELY)) { - asyncExecutor.execute(() -> fire(future, event, index + 1, true, registrations)); + // We established earlier that registrations[index + 1] is a valid index. + // If we are remaining in the same thread for the next handler, fire + // the next event immediately, else fire it within the executor service + // of the plugin with the next handler. + final HandlerRegistration next = registrations[index + 1]; + final Thread currentThread = Thread.currentThread(); + if (currentThread == firedOnThread && next.asyncType != AsyncType.ALWAYS) { + fire(future, event, index + 1, currentlyAsync, registrations); + } else { + next.plugin.getExecutorService().execute(() -> + fire(future, event, index + 1, true, registrations)); + } } } @@ -595,7 +684,7 @@ public class VelocityEventManager implements EventManager { continue; } } else { - asyncExecutor.execute(continuationTask); + registration.plugin.getExecutorService().execute(continuationTask); } // fire will continue in another thread once the async task is // executed and the continuation is resumed @@ -615,13 +704,4 @@ public class VelocityEventManager implements EventManager { logger.error("Couldn't pass {} to {} {}", registration.eventType.getSimpleName(), pluginDescription.getId(), pluginDescription.getVersion().orElse(""), t); } - - public boolean shutdown() throws InterruptedException { - asyncExecutor.shutdown(); - return asyncExecutor.awaitTermination(10, TimeUnit.SECONDS); - } - - public ExecutorService getAsyncExecutor() { - return asyncExecutor; - } } \ No newline at end of file diff --git a/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java b/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java index 9bd202fa4..c8e17d0f8 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java @@ -31,7 +31,6 @@ import com.velocitypowered.proxy.event.MockEventManager; import com.velocitypowered.proxy.event.VelocityEventManager; import java.util.Arrays; import java.util.Collection; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -47,16 +46,6 @@ abstract class CommandTestSuite { eventManager = new MockEventManager(); } - @AfterAll - static void afterAll() { - try { - eventManager.shutdown(); - eventManager = null; - } catch (final InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - @BeforeEach void setUp() { this.manager = new VelocityCommandManager(eventManager); diff --git a/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java b/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java index e05df200f..fc817f4db 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/event/EventTest.java @@ -41,20 +41,24 @@ import org.junit.jupiter.api.TestInstance; public class EventTest { public static final String CONTINUATION_TEST_THREAD_NAME = "Continuation test thread"; - private final VelocityEventManager eventManager = - new VelocityEventManager(new FakePluginManager()); + private final FakePluginManager pluginManager = new FakePluginManager(); + private final VelocityEventManager eventManager = new VelocityEventManager(pluginManager); @AfterAll void shutdown() throws Exception { - eventManager.shutdown(); + pluginManager.shutdown(); } static final class TestEvent { } + static void assertSyncThread(final Thread thread) { + assertEquals(Thread.currentThread(), thread); + } + static void assertAsyncThread(final Thread thread) { - assertTrue(thread.getName().contains("Velocity Async Event Executor")); + assertTrue(thread.getName().contains("Test Async Thread")); } static void assertContinuationThread(final Thread thread) { @@ -90,6 +94,7 @@ public class EventTest { eventManager.fire(new TestEvent()).get(); } finally { eventManager.unregisterListeners(FakePluginManager.PLUGIN_A); + eventManager.unregisterListeners(FakePluginManager.PLUGIN_B); } // Check that the order is A < B < C. @@ -119,6 +124,7 @@ public class EventTest { eventManager.fire(new TestEvent()).get(); } finally { eventManager.unregisterListeners(FakePluginManager.PLUGIN_A); + eventManager.unregisterListeners(FakePluginManager.PLUGIN_B); } // Check that the order is A < B < C. @@ -126,6 +132,26 @@ public class EventTest { assertTrue(listener2Invoked.get() < listener3Invoked.get(), "Listener C invoked before B!"); } + @Test + void testAlwaysSync() throws Exception { + final AlwaysSyncListener listener = new AlwaysSyncListener(); + handleMethodListener(listener); + assertSyncThread(listener.thread); + assertEquals(1, listener.result); + } + + static final class AlwaysSyncListener { + + @MonotonicNonNull Thread thread; + int result; + + @Subscribe(async = false) + void sync(TestEvent event) { + result++; + thread = Thread.currentThread(); + } + } + @Test void testAlwaysAsync() throws Exception { final AlwaysAsyncListener listener = new AlwaysAsyncListener(); @@ -143,7 +169,7 @@ public class EventTest { @MonotonicNonNull Thread threadC; int result; - @Subscribe + @Subscribe(async = true, order = PostOrder.EARLY) void firstAsync(TestEvent event) { result++; threadA = Thread.currentThread(); @@ -155,50 +181,93 @@ public class EventTest { return EventTask.async(() -> result++); } - @Subscribe + @Subscribe(order = PostOrder.LATE) void thirdAsync(TestEvent event) { result++; threadC = Thread.currentThread(); } } + @Test + void testSometimesAsync() throws Exception { + final SometimesAsyncListener listener = new SometimesAsyncListener(); + handleMethodListener(listener); + assertSyncThread(listener.threadA); + assertSyncThread(listener.threadB); + assertAsyncThread(listener.threadC); + assertAsyncThread(listener.threadD); + assertEquals(3, listener.result); + } + + static final class SometimesAsyncListener { + + @MonotonicNonNull Thread threadA; + @MonotonicNonNull Thread threadB; + @MonotonicNonNull Thread threadC; + @MonotonicNonNull Thread threadD; + int result; + + @Subscribe(order = PostOrder.EARLY, async = false) + void notAsync(TestEvent event) { + result++; + threadA = Thread.currentThread(); + } + + @Subscribe + EventTask notAsyncUntilTask(TestEvent event) { + threadB = Thread.currentThread(); + return EventTask.async(() -> { + threadC = Thread.currentThread(); + result++; + }); + } + + @Subscribe(order = PostOrder.LATE, async = false) + void stillAsyncAfterTask(TestEvent event) { + threadD = Thread.currentThread(); + result++; + } + } + @Test void testContinuation() throws Exception { final ContinuationListener listener = new ContinuationListener(); handleMethodListener(listener); - assertAsyncThread(listener.thread1); - assertAsyncThread(listener.thread2); - assertContinuationThread(listener.thread2Custom); - assertAsyncThread(listener.thread3); + assertSyncThread(listener.threadA); + assertSyncThread(listener.threadB); + assertAsyncThread(listener.threadC); assertEquals(2, listener.value.get()); } static final class ContinuationListener { - @MonotonicNonNull Thread thread1; - @MonotonicNonNull Thread thread2; - @MonotonicNonNull Thread thread2Custom; - @MonotonicNonNull Thread thread3; + @MonotonicNonNull Thread threadA; + @MonotonicNonNull Thread threadB; + @MonotonicNonNull Thread threadC; final AtomicInteger value = new AtomicInteger(); @Subscribe(order = PostOrder.EARLY) EventTask continuation(TestEvent event) { - thread1 = Thread.currentThread(); + threadA = Thread.currentThread(); return EventTask.withContinuation(continuation -> { value.incrementAndGet(); - thread2 = Thread.currentThread(); + threadB = Thread.currentThread(); new Thread(() -> { - thread2Custom = Thread.currentThread(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } value.incrementAndGet(); continuation.resume(); - }, CONTINUATION_TEST_THREAD_NAME).start(); + }).start(); }); } @Subscribe(order = PostOrder.LATE) void afterContinuation(TestEvent event) { - thread3 = Thread.currentThread(); + threadC = Thread.currentThread(); } } @@ -207,9 +276,9 @@ public class EventTest { final ResumeContinuationImmediatelyListener listener = new ResumeContinuationImmediatelyListener(); handleMethodListener(listener); - assertAsyncThread(listener.threadA); - assertAsyncThread(listener.threadB); - assertAsyncThread(listener.threadC); + assertSyncThread(listener.threadA); + assertSyncThread(listener.threadB); + assertSyncThread(listener.threadC); assertEquals(2, listener.result); } @@ -241,42 +310,44 @@ public class EventTest { void testContinuationParameter() throws Exception { final ContinuationParameterListener listener = new ContinuationParameterListener(); handleMethodListener(listener); - assertAsyncThread(listener.thread1); - assertAsyncThread(listener.thread2); - assertContinuationThread(listener.thread2Custom); - assertAsyncThread(listener.thread3); + assertSyncThread(listener.threadA); + assertSyncThread(listener.threadB); + assertAsyncThread(listener.threadC); assertEquals(3, listener.result.get()); } static final class ContinuationParameterListener { - @MonotonicNonNull Thread thread1; - @MonotonicNonNull Thread thread2; - @MonotonicNonNull Thread thread2Custom; - @MonotonicNonNull Thread thread3; + @MonotonicNonNull Thread threadA; + @MonotonicNonNull Thread threadB; + @MonotonicNonNull Thread threadC; final AtomicInteger result = new AtomicInteger(); @Subscribe void resume(TestEvent event, Continuation continuation) { - thread1 = Thread.currentThread(); + threadA = Thread.currentThread(); result.incrementAndGet(); continuation.resume(); } @Subscribe(order = PostOrder.LATE) void resumeFromCustomThread(TestEvent event, Continuation continuation) { - thread2 = Thread.currentThread(); + threadB = Thread.currentThread(); new Thread(() -> { - thread2Custom = Thread.currentThread(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } result.incrementAndGet(); continuation.resume(); - }, CONTINUATION_TEST_THREAD_NAME).start(); + }).start(); } @Subscribe(order = PostOrder.LAST) void afterCustomThread(TestEvent event, Continuation continuation) { - thread3 = Thread.currentThread(); + threadC = Thread.currentThread(); result.incrementAndGet(); continuation.resume(); } @@ -328,8 +399,7 @@ public class EventTest { + "the second is the fancy continuation"); } }, - new TypeToken>() { - }, + new TypeToken>() {}, invokeFunction -> (instance, event) -> EventTask.withContinuation(continuation -> invokeFunction.accept(instance, event, new FancyContinuationImpl(continuation)) @@ -349,4 +419,4 @@ public class EventTest { continuation.resume(); } } -} \ No newline at end of file +} diff --git a/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java b/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java index 04ba3867d..b1bc1af72 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java @@ -18,6 +18,7 @@ package com.velocitypowered.proxy.testutil; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.PluginManager; @@ -25,7 +26,7 @@ import java.nio.file.Path; import java.util.Collection; import java.util.Optional; import java.util.concurrent.ExecutorService; -import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Executors; import org.checkerframework.checker.nullness.qual.NonNull; /** @@ -36,15 +37,19 @@ public class FakePluginManager implements PluginManager { public static final Object PLUGIN_A = new Object(); public static final Object PLUGIN_B = new Object(); - private static final PluginContainer PC_A = new FakePluginContainer("a", PLUGIN_A); - private static final PluginContainer PC_B = new FakePluginContainer("b", PLUGIN_B); + private final PluginContainer containerA = new FakePluginContainer("a", PLUGIN_A); + private final PluginContainer containerB = new FakePluginContainer("b", PLUGIN_B); + + private ExecutorService service = Executors.newCachedThreadPool( + new ThreadFactoryBuilder().setNameFormat("Test Async Thread").setDaemon(true).build() + ); @Override public @NonNull Optional fromInstance(@NonNull Object instance) { if (instance == PLUGIN_A) { - return Optional.of(PC_A); + return Optional.of(containerA); } else if (instance == PLUGIN_B) { - return Optional.of(PC_B); + return Optional.of(containerB); } else { return Optional.empty(); } @@ -54,9 +59,9 @@ public class FakePluginManager implements PluginManager { public @NonNull Optional getPlugin(@NonNull String id) { switch (id) { case "a": - return Optional.of(PC_A); + return Optional.of(containerA); case "b": - return Optional.of(PC_B); + return Optional.of(containerB); default: return Optional.empty(); } @@ -64,7 +69,7 @@ public class FakePluginManager implements PluginManager { @Override public @NonNull Collection getPlugins() { - return ImmutableList.of(PC_A, PC_B); + return ImmutableList.of(containerA, containerB); } @Override @@ -77,16 +82,18 @@ public class FakePluginManager implements PluginManager { throw new UnsupportedOperationException(); } - private static class FakePluginContainer implements PluginContainer { + public void shutdown() { + this.service.shutdownNow(); + } + + private class FakePluginContainer implements PluginContainer { private final String id; private final Object instance; - private final ExecutorService service; private FakePluginContainer(String id, Object instance) { this.id = id; this.instance = instance; - this.service = ForkJoinPool.commonPool(); } @Override From 2016d1482f185daf4b92b6775fb4a8d2ef1e0da3 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 15 Sep 2024 20:22:55 -0400 Subject: [PATCH 23/38] Deprecate anonymous command registrations --- .../api/command/CommandManager.java | 4 + .../velocitypowered/proxy/VelocityServer.java | 55 ++++++++++-- .../proxy/command/VelocityCommandManager.java | 70 +++++++++++---- .../proxy/command/VelocityCommands.java | 90 ++++++++++++++++++- .../VelocityArgumentCommandNode.java | 10 +++ .../VelocityBrigadierCommandWrapper.java | 67 ++++++++++++++ .../proxy/command/builtin/GlistCommand.java | 9 +- .../proxy/command/builtin/SendCommand.java | 9 +- .../registrar/BrigadierCommandRegistrar.java | 6 +- .../registrar/InvocableCommandRegistrar.java | 5 +- .../proxy/plugin/VelocityPluginManager.java | 7 +- .../plugin/virtual/VelocityVirtualPlugin.java | 29 ++++++ .../proxy/command/BrigadierCommandTests.java | 2 +- .../proxy/command/CommandTestSuite.java | 3 +- .../proxy/testutil/FakePluginManager.java | 9 +- 15 files changed, 338 insertions(+), 37 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityBrigadierCommandWrapper.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/plugin/virtual/VelocityVirtualPlugin.java diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java index 257fc9e64..ad3893738 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java @@ -45,7 +45,9 @@ public interface CommandManager { * @throws IllegalArgumentException if one of the given aliases is already registered, or * the given command does not implement a registrable {@link Command} subinterface * @see Command for a list of registrable Command subinterfaces + * @deprecated use {@link #register(CommandMeta, Command)} instead with a plugin specified */ + @Deprecated default void register(String alias, Command command, String... otherAliases) { register(metaBuilder(alias).aliases(otherAliases).build(), command); } @@ -55,7 +57,9 @@ public interface CommandManager { * * @param command the command to register * @throws IllegalArgumentException if the node alias is already registered + * @deprecated use {@link #register(CommandMeta, Command)} instead with a plugin specified */ + @Deprecated void register(BrigadierCommand command); /** diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 2a362ccc9..2563f984f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -22,11 +22,13 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyReloadEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.plugin.PluginContainer; +import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; @@ -52,6 +54,9 @@ import com.velocitypowered.proxy.crypto.EncryptionUtils; import com.velocitypowered.proxy.event.VelocityEventManager; import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.plugin.VelocityPluginManager; +import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer; +import com.velocitypowered.proxy.plugin.loader.VelocityPluginDescription; +import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.protocol.util.GameProfileSerializer; @@ -77,6 +82,7 @@ import java.nio.file.Path; import java.security.KeyPair; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -111,6 +117,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ public class VelocityServer implements ProxyServer, ForwardingAudience { + public static final String VELOCITY_URL = "https://velocitypowered.com"; + private static final Logger logger = LogManager.getLogger(VelocityServer.class); public static final Gson GENERAL_GSON = new GsonBuilder() .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) @@ -163,7 +171,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { VelocityServer(final ProxyOptions options) { pluginManager = new VelocityPluginManager(this); eventManager = new VelocityEventManager(pluginManager); - commandManager = new VelocityCommandManager(eventManager); + commandManager = new VelocityCommandManager(eventManager, pluginManager); scheduler = new VelocityScheduler(pluginManager); console = new VelocityConsole(this); cm = new ConnectionManager(this); @@ -200,6 +208,16 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { return new ProxyVersion(implName, implVendor, implVersion); } + private VelocityPluginContainer createVirtualPlugin() { + ProxyVersion version = getVersion(); + PluginDescription description = new VelocityPluginDescription( + "velocity", version.getName(), version.getVersion(), "The Velocity proxy", + VELOCITY_URL, ImmutableList.of(version.getVendor()), Collections.emptyList(), null); + VelocityPluginContainer container = new VelocityPluginContainer(description); + container.setInstance(VelocityVirtualPlugin.INSTANCE); + return container; + } + @Override public VelocityCommandManager getCommandManager() { return commandManager; @@ -214,6 +232,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { void start() { logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion()); console.setupStreams(); + pluginManager.registerPlugin(this.createVirtualPlugin()); registerTranslations(); @@ -222,11 +241,35 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { cm.logChannelInformation(); // Initialize commands first - commandManager.register(VelocityCommand.create(this)); - commandManager.register(CallbackCommand.create()); - commandManager.register(ServerCommand.create(this)); - commandManager.register("shutdown", ShutdownCommand.command(this), - "end", "stop"); + final BrigadierCommand velocityParentCommand = VelocityCommand.create(this); + commandManager.register( + commandManager.metaBuilder(velocityParentCommand) + .plugin(VelocityVirtualPlugin.INSTANCE) + .build(), + velocityParentCommand + ); + final BrigadierCommand callbackCommand = CallbackCommand.create(); + commandManager.register( + commandManager.metaBuilder(callbackCommand) + .plugin(VelocityVirtualPlugin.INSTANCE) + .build(), + velocityParentCommand + ); + final BrigadierCommand serverCommand = ServerCommand.create(this); + commandManager.register( + commandManager.metaBuilder(serverCommand) + .plugin(VelocityVirtualPlugin.INSTANCE) + .build(), + serverCommand + ); + final BrigadierCommand shutdownCommand = ShutdownCommand.command(this); + commandManager.register( + commandManager.metaBuilder(shutdownCommand) + .plugin(VelocityVirtualPlugin.INSTANCE) + .aliases("end", "stop") + .build(), + shutdownCommand + ); new GlistCommand(this).register(); new SendCommand(this).register(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index 24fc6a059..f8bb39f07 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -20,6 +20,7 @@ package com.velocitypowered.proxy.command; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.google.common.util.concurrent.MoreExecutors; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.Message; import com.mojang.brigadier.ParseResults; @@ -37,11 +38,15 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.VelocityBrigadierMessage; import com.velocitypowered.api.event.command.CommandExecuteEvent; import com.velocitypowered.api.event.command.PostCommandInvocationEvent; +import com.velocitypowered.api.plugin.PluginManager; +import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper; import com.velocitypowered.proxy.command.registrar.BrigadierCommandRegistrar; import com.velocitypowered.proxy.command.registrar.CommandRegistrar; import com.velocitypowered.proxy.command.registrar.RawCommandRegistrar; import com.velocitypowered.proxy.command.registrar.SimpleCommandRegistrar; import com.velocitypowered.proxy.event.VelocityEventManager; +import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin; +import io.netty.util.concurrent.FastThreadLocalThread; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -49,8 +54,7 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Executor; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; @@ -73,14 +77,16 @@ public class VelocityCommandManager implements CommandManager { private final SuggestionsProvider suggestionsProvider; private final CommandGraphInjector injector; private final Map commandMetas; - private final ExecutorService asyncExecutor; + private final PluginManager pluginManager; /** * Constructs a command manager. * * @param eventManager the event manager */ - public VelocityCommandManager(final VelocityEventManager eventManager) { + public VelocityCommandManager(final VelocityEventManager eventManager, + PluginManager pluginManager) { + this.pluginManager = pluginManager; this.lock = new ReentrantReadWriteLock(); this.dispatcher = new CommandDispatcher<>(); this.eventManager = Preconditions.checkNotNull(eventManager); @@ -92,7 +98,6 @@ public class VelocityCommandManager implements CommandManager { this.suggestionsProvider = new SuggestionsProvider<>(this.dispatcher, this.lock.readLock()); this.injector = new CommandGraphInjector<>(this.dispatcher, this.lock.readLock()); this.commandMetas = new ConcurrentHashMap<>(); - this.asyncExecutor = ForkJoinPool.commonPool(); // TODO: remove entirely } public void setAnnounceProxyCommands(boolean announceProxyCommands) { @@ -222,16 +227,13 @@ public class VelocityCommandManager implements CommandManager { return eventManager.fire(new CommandExecuteEvent(source, cmdLine)); } - private boolean executeImmediately0(final CommandSource source, final String cmdLine) { + private boolean executeImmediately0(final CommandSource source, final ParseResults parsed) { Preconditions.checkNotNull(source, "source"); - Preconditions.checkNotNull(cmdLine, "cmdLine"); - final String normalizedInput = VelocityCommands.normalizeInput(cmdLine, true); CommandResult result = CommandResult.EXCEPTION; try { // The parse can fail if the requirement predicates throw - final ParseResults parse = this.parse(normalizedInput, source); - boolean executed = dispatcher.execute(parse) != BrigadierCommand.FORWARD; + boolean executed = dispatcher.execute(parsed) != BrigadierCommand.FORWARD; result = executed ? CommandResult.EXECUTED : CommandResult.FORWARDED; return executed; } catch (final CommandSyntaxException e) { @@ -253,9 +255,9 @@ public class VelocityCommandManager implements CommandManager { } } catch (final Throwable e) { // Ugly, ugly swallowing of everything Throwable, because plugins are naughty. - throw new RuntimeException("Unable to invoke command " + cmdLine + " for " + source, e); + throw new RuntimeException("Unable to invoke command " + parsed.getReader().getString() + "for " + source, e); } finally { - eventManager.fireAndForget(new PostCommandInvocationEvent(source, cmdLine, result)); + eventManager.fireAndForget(new PostCommandInvocationEvent(source, parsed.getReader().getString(), result)); } } @@ -264,13 +266,17 @@ public class VelocityCommandManager implements CommandManager { Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(cmdLine, "cmdLine"); - return callCommandEvent(source, cmdLine).thenApplyAsync(event -> { + return callCommandEvent(source, cmdLine).thenComposeAsync(event -> { CommandExecuteEvent.CommandResult commandResult = event.getResult(); if (commandResult.isForwardToServer() || !commandResult.isAllowed()) { - return false; + return CompletableFuture.completedFuture(false); } - return executeImmediately0(source, commandResult.getCommand().orElse(event.getCommand())); - }, asyncExecutor); + final ParseResults parsed = this.parse( + commandResult.getCommand().orElse(cmdLine), source); + return CompletableFuture.supplyAsync( + () -> executeImmediately0(source, parsed), this.getAsyncExecutor(parsed) + ); + }, figureAsyncExecutorForParsing()); } @Override @@ -279,7 +285,13 @@ public class VelocityCommandManager implements CommandManager { Preconditions.checkNotNull(source, "source"); Preconditions.checkNotNull(cmdLine, "cmdLine"); - return CompletableFuture.supplyAsync(() -> executeImmediately0(source, cmdLine), asyncExecutor); + return CompletableFuture.supplyAsync( + () -> this.parse(cmdLine, source), figureAsyncExecutorForParsing() + ).thenCompose( + parsed -> CompletableFuture.supplyAsync( + () -> executeImmediately0(source, parsed), this.getAsyncExecutor(parsed) + ) + ); } /** @@ -327,9 +339,10 @@ public class VelocityCommandManager implements CommandManager { * @return the parse results */ private ParseResults parse(final String input, final CommandSource source) { + final String normalizedInput = VelocityCommands.normalizeInput(input, true); lock.readLock().lock(); try { - return dispatcher.parse(input, source); + return dispatcher.parse(normalizedInput, source); } finally { lock.readLock().unlock(); } @@ -373,4 +386,25 @@ public class VelocityCommandManager implements CommandManager { public CommandGraphInjector getInjector() { return injector; } + + private Executor getAsyncExecutor(ParseResults parse) { + Object registrant; + if (parse.getContext().getCommand() instanceof VelocityBrigadierCommandWrapper vbcw) { + registrant = vbcw.registrant() == null ? VelocityVirtualPlugin.INSTANCE : vbcw.registrant(); + } else { + registrant = VelocityVirtualPlugin.INSTANCE; + } + return pluginManager.ensurePluginContainer(registrant).getExecutorService(); + } + + private Executor figureAsyncExecutorForParsing() { + final Thread thread = Thread.currentThread(); + if (thread instanceof FastThreadLocalThread) { + // we *never* want to block the Netty event loop, so use the async executor + return pluginManager.ensurePluginContainer(VelocityVirtualPlugin.INSTANCE).getExecutorService(); + } else { + // it's some other thread that isn't a Netty event loop thread. direct execution it is! + return MoreExecutors.directExecutor(); + } + } } \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java index 2f10ed25b..3c2ad1991 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java @@ -24,6 +24,7 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; import com.mojang.brigadier.context.ParsedCommandNode; +import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.RootCommandNode; @@ -32,6 +33,7 @@ import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.InvocableCommand; import com.velocitypowered.proxy.command.brigadier.VelocityArgumentCommandNode; +import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper; import java.util.List; import java.util.Locale; import java.util.Map; @@ -44,6 +46,59 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ public final class VelocityCommands { + // Wrapping + + /** + * Walks the command node tree and wraps all {@link Command} instances in a {@link VelocityBrigadierCommandWrapper}, + * to indicate the plugin that registered the command. This also has the side effect of cloning + * the command node tree. + * + * @param delegate the command node to wrap + * @param registrant the plugin that registered the command + * @return the wrapped command node + */ + public static CommandNode wrap(final CommandNode delegate, + final @Nullable Object registrant) { + Preconditions.checkNotNull(delegate, "delegate"); + if (registrant == null) { + // the registrant is null if the `plugin` was absent when we try to register the command + return delegate; + } + + com.mojang.brigadier.Command maybeCommand = delegate.getCommand(); + if (maybeCommand != null && !(maybeCommand instanceof VelocityBrigadierCommandWrapper)) { + maybeCommand = VelocityBrigadierCommandWrapper.wrap(delegate.getCommand(), registrant); + } + + if (delegate instanceof LiteralCommandNode lcn) { + var literalBuilder = shallowCopyAsBuilder(lcn, delegate.getName(), true); + literalBuilder.executes(maybeCommand); + // we also need to wrap any children + for (final CommandNode child : delegate.getChildren()) { + literalBuilder.then(wrap(child, registrant)); + } + if (delegate.getRedirect() != null) { + literalBuilder.redirect(wrap(delegate.getRedirect(), registrant)); + } + return literalBuilder.build(); + } else if (delegate instanceof VelocityArgumentCommandNode vacn) { + return vacn.withCommand(maybeCommand) + .withRedirect(delegate.getRedirect() != null ? wrap(delegate.getRedirect(), registrant) : null); + } else if (delegate instanceof ArgumentCommandNode) { + var argBuilder = delegate.createBuilder().executes(maybeCommand); + // we also need to wrap any children + for (final CommandNode child : delegate.getChildren()) { + argBuilder.then(wrap(child, registrant)); + } + if (delegate.getRedirect() != null) { + argBuilder.redirect(wrap(delegate.getRedirect(), registrant)); + } + return argBuilder.build(); + } else { + throw new IllegalArgumentException("Unsupported node type: " + delegate.getClass()); + } + } + // Normalization /** @@ -135,6 +190,33 @@ public final class VelocityCommands { */ public static LiteralCommandNode shallowCopy( final LiteralCommandNode original, final String newName) { + return shallowCopy(original, newName, original.getCommand()); + } + + /** + * Creates a copy of the given literal with the specified name. + * + * @param original the literal node to copy + * @param newName the name of the returned literal node + * @param newCommand the new command to set on the copied node + * @return a copy of the literal with the given name + */ + private static LiteralCommandNode shallowCopy( + final LiteralCommandNode original, final String newName, + final com.mojang.brigadier.Command newCommand) { + return shallowCopyAsBuilder(original, newName, false).executes(newCommand).build(); + } + + /** + * Creates a copy of the given literal with the specified name. + * + * @param original the literal node to copy + * @param newName the name of the returned literal node + * @return a copy of the literal with the given name + */ + private static LiteralArgumentBuilder shallowCopyAsBuilder( + final LiteralCommandNode original, final String newName, + final boolean skipChildren) { // Brigadier resolves the redirect of a node if further input can be parsed. // Let be a literal node having a redirect to a literal. Then, // the context returned by CommandDispatcher#parseNodes when given the input @@ -150,10 +232,12 @@ public final class VelocityCommands { .requiresWithContext(original.getContextRequirement()) .forward(original.getRedirect(), original.getRedirectModifier(), original.isFork()) .executes(original.getCommand()); - for (final CommandNode child : original.getChildren()) { - builder.then(child); + if (!skipChildren) { + for (final CommandNode child : original.getChildren()) { + builder.then(child); + } } - return builder.build(); + return builder; } // Arguments node diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java b/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java index 9faa29294..03ba6d35e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityArgumentCommandNode.java @@ -93,6 +93,16 @@ public class VelocityArgumentCommandNode extends ArgumentCommandNode withCommand(Command command) { + return new VelocityArgumentCommandNode<>(getName(), type, command, getRequirement(), + getContextRequirement(), getRedirect(), getRedirectModifier(), isFork(), getCustomSuggestions()); + } + + public VelocityArgumentCommandNode withRedirect(CommandNode target) { + return new VelocityArgumentCommandNode<>(getName(), type, getCommand(), getRequirement(), + getContextRequirement(), target, getRedirectModifier(), isFork(), getCustomSuggestions()); + } + @Override public boolean isValidInput(final String input) { return true; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityBrigadierCommandWrapper.java b/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityBrigadierCommandWrapper.java new file mode 100644 index 000000000..8502134cc --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/brigadier/VelocityBrigadierCommandWrapper.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.command.brigadier; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.velocitypowered.api.command.CommandSource; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Wraps a Brigadier command to allow us to track the registrant. + */ +public class VelocityBrigadierCommandWrapper implements Command { + + private final Command delegate; + private final Object registrant; + + private VelocityBrigadierCommandWrapper(Command delegate, Object registrant) { + this.delegate = delegate; + this.registrant = registrant; + } + + /** + * Transforms the given command into a {@code VelocityBrigadierCommandWrapper} if the registrant + * is not null and if the command is not already wrapped. + * + * @param delegate the command to wrap + * @param registrant the registrant of the command + * @return the wrapped command, if necessary + */ + public static Command wrap(Command delegate, @Nullable Object registrant) { + if (registrant == null) { + // nothing to wrap + return delegate; + } + if (delegate instanceof VelocityBrigadierCommandWrapper) { + // already wrapped + return delegate; + } + return new VelocityBrigadierCommandWrapper(delegate, registrant); + } + + @Override + public int run(CommandContext context) throws CommandSyntaxException { + return delegate.run(context); + } + + public Object registrant() { + return registrant; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java index 2cf8c92d2..2b73dcc13 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/GlistCommand.java @@ -31,6 +31,7 @@ import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin; import java.util.List; import java.util.Optional; import net.kyori.adventure.text.Component; @@ -80,7 +81,13 @@ public class GlistCommand { .executes(this::serverCount) .build(); rootNode.then(serverNode); - server.getCommandManager().register(new BrigadierCommand(rootNode)); + final BrigadierCommand command = new BrigadierCommand(rootNode); + server.getCommandManager().register( + server.getCommandManager().metaBuilder(command) + .plugin(VelocityVirtualPlugin.INSTANCE) + .build(), + command + ); } private int totalCount(final CommandContext context) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java index 626131daa..d0df03488 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/SendCommand.java @@ -30,6 +30,7 @@ import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin; import java.util.Objects; import java.util.Optional; import net.kyori.adventure.text.Component; @@ -96,7 +97,13 @@ public class SendCommand { .build(); playerNode.then(serverNode); rootNode.then(playerNode.build()); - server.getCommandManager().register(new BrigadierCommand(rootNode.build())); + final BrigadierCommand command = new BrigadierCommand(rootNode); + server.getCommandManager().register( + server.getCommandManager().metaBuilder(command) + .plugin(VelocityVirtualPlugin.INSTANCE) + .build(), + command + ); } private int usage(final CommandContext context) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/BrigadierCommandRegistrar.java b/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/BrigadierCommandRegistrar.java index bc8d02de4..74d3f30ee 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/BrigadierCommandRegistrar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/BrigadierCommandRegistrar.java @@ -40,17 +40,19 @@ public final class BrigadierCommandRegistrar extends AbstractCommandRegistrar
literal = command.getNode(); + final LiteralCommandNode wrapped = + (LiteralCommandNode) VelocityCommands.wrap(literal, meta.getPlugin()); final String primaryAlias = literal.getName(); if (VelocityCommands.isValidAlias(primaryAlias)) { // Register directly without copying - this.register(literal); + this.register(wrapped); } for (final String alias : meta.getAliases()) { if (primaryAlias.equals(alias)) { continue; } - this.register(literal, alias); + this.register(wrapped, alias); } // Brigadier commands don't support hinting, ignore diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java b/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java index a526fe3de..380f45e39 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java @@ -32,6 +32,7 @@ import com.velocitypowered.api.command.InvocableCommand; import com.velocitypowered.proxy.command.VelocityCommandMeta; import com.velocitypowered.proxy.command.VelocityCommands; import com.velocitypowered.proxy.command.brigadier.VelocityArgumentBuilder; +import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper; import com.velocitypowered.proxy.command.invocation.CommandInvocationFactory; import java.util.Iterator; import java.util.concurrent.locks.Lock; @@ -76,11 +77,11 @@ abstract class InvocableCommandRegistrar, final I invocation = invocationFactory.create(context); return command.hasPermission(invocation); }; - final Command callback = context -> { + final Command callback = VelocityBrigadierCommandWrapper.wrap(context -> { final I invocation = invocationFactory.create(context); command.execute(invocation); return 1; // handled - }; + }, meta.getPlugin()); final LiteralCommandNode literal = LiteralArgumentBuilder .literal(alias) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java index 0af477c42..6bd0e0085 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/VelocityPluginManager.java @@ -68,7 +68,12 @@ public class VelocityPluginManager implements PluginManager { this.server = checkNotNull(server, "server"); } - private void registerPlugin(PluginContainer plugin) { + /** + * Registers a plugin with the plugin manager. + * + * @param plugin the plugin to register + */ + public void registerPlugin(PluginContainer plugin) { pluginsById.put(plugin.getDescription().getId(), plugin); Optional instance = plugin.getInstance(); instance.ifPresent(o -> pluginInstances.put(o, plugin)); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/plugin/virtual/VelocityVirtualPlugin.java b/proxy/src/main/java/com/velocitypowered/proxy/plugin/virtual/VelocityVirtualPlugin.java new file mode 100644 index 000000000..08e4f57c3 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/plugin/virtual/VelocityVirtualPlugin.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.plugin.virtual; + +/** + * A singleton plugin object that represents the Velocity proxy itself. + */ +public class VelocityVirtualPlugin { + @SuppressWarnings("InstantiationOfUtilityClass") + public static final VelocityVirtualPlugin INSTANCE = new VelocityVirtualPlugin(); + + private VelocityVirtualPlugin() { + } +} diff --git a/proxy/src/test/java/com/velocitypowered/proxy/command/BrigadierCommandTests.java b/proxy/src/test/java/com/velocitypowered/proxy/command/BrigadierCommandTests.java index f4110ba52..abbfbc422 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/command/BrigadierCommandTests.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/command/BrigadierCommandTests.java @@ -230,7 +230,7 @@ public class BrigadierCommandTests extends CommandTestSuite { final Exception wrapper = assertThrows(CompletionException.class, () -> manager.executeAsync(source, "hello").join()); - assertSame(expected, wrapper.getCause().getCause()); + assertSame(expected, wrapper.getCause()); } // Suggestions diff --git a/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java b/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java index c8e17d0f8..66e49838a 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/command/CommandTestSuite.java @@ -29,6 +29,7 @@ import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.proxy.event.MockEventManager; import com.velocitypowered.proxy.event.VelocityEventManager; +import com.velocitypowered.proxy.testutil.FakePluginManager; import java.util.Arrays; import java.util.Collection; import org.junit.jupiter.api.BeforeAll; @@ -48,7 +49,7 @@ abstract class CommandTestSuite { @BeforeEach void setUp() { - this.manager = new VelocityCommandManager(eventManager); + this.manager = new VelocityCommandManager(eventManager, new FakePluginManager()); } final void assertHandled(final String input) { diff --git a/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java b/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java index b1bc1af72..7992ac52e 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/testutil/FakePluginManager.java @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginDescription; import com.velocitypowered.api.plugin.PluginManager; +import com.velocitypowered.proxy.plugin.virtual.VelocityVirtualPlugin; import java.nio.file.Path; import java.util.Collection; import java.util.Optional; @@ -39,6 +40,8 @@ public class FakePluginManager implements PluginManager { private final PluginContainer containerA = new FakePluginContainer("a", PLUGIN_A); private final PluginContainer containerB = new FakePluginContainer("b", PLUGIN_B); + private final PluginContainer containerVelocity = new FakePluginContainer("velocity", + VelocityVirtualPlugin.INSTANCE); private ExecutorService service = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNameFormat("Test Async Thread").setDaemon(true).build() @@ -50,6 +53,8 @@ public class FakePluginManager implements PluginManager { return Optional.of(containerA); } else if (instance == PLUGIN_B) { return Optional.of(containerB); + } else if (instance == VelocityVirtualPlugin.INSTANCE) { + return Optional.of(containerVelocity); } else { return Optional.empty(); } @@ -62,6 +67,8 @@ public class FakePluginManager implements PluginManager { return Optional.of(containerA); case "b": return Optional.of(containerB); + case "velocity": + return Optional.of(containerVelocity); default: return Optional.empty(); } @@ -69,7 +76,7 @@ public class FakePluginManager implements PluginManager { @Override public @NonNull Collection getPlugins() { - return ImmutableList.of(containerA, containerB); + return ImmutableList.of(containerVelocity, containerA, containerB); } @Override From b66aa3fb4e6d2350c85523685c5c2420c029ade8 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 15 Sep 2024 20:22:55 -0400 Subject: [PATCH 24/38] Bump to Velocity 3.4.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index cd9c67796..55c2ed10e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ group=com.velocitypowered -version=3.3.0-SNAPSHOT +version=3.4.0-SNAPSHOT From ef1f5009d333a302927e3266c04903052408e57a Mon Sep 17 00:00:00 2001 From: Stefano Date: Mon, 23 Sep 2024 03:42:12 +0200 Subject: [PATCH 25/38] =?UTF-8?q?Adjust=20HAProxy's=20existance=20to=20log?= =?UTF-8?q?=20when=20the=20proxy=20protocol=20is=20enabled=20=E2=80=A6=20(?= =?UTF-8?q?#1436)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adjust HAProxy's existance to log when the proxy protocol is enabled during bind. * Added additional warning message, instead of changing the main one. We can see what the preference would be. --- .../velocitypowered/proxy/network/ConnectionManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index be1db5522..7af894f46 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -109,6 +109,12 @@ public final class ConnectionManager { final Channel channel = future.channel(); if (future.isSuccess()) { this.endpoints.put(address, new Endpoint(channel, ListenerType.MINECRAFT)); + + // Warn people with console access that HAProxy is in use, see PR: #1436 + if (this.server.getConfiguration().isProxyProtocol()) { + LOGGER.warn("Using HAProxy and listening on {}, please ensure this listener is adequately firewalled.", channel.localAddress()); + } + LOGGER.info("Listening on {}", channel.localAddress()); // Fire the proxy bound event after the socket is bound From 99aaf3ce4ef37297985a6b2ad6d7992289546637 Mon Sep 17 00:00:00 2001 From: Isaac - The456 Date: Mon, 7 Oct 2024 10:41:17 +0100 Subject: [PATCH 26/38] expose raw vhost (#1423) --- .../velocitypowered/api/proxy/InboundConnection.java | 11 ++++++++++- .../proxy/connection/client/AuthSessionHandler.java | 2 +- .../proxy/connection/client/ConnectedPlayer.java | 9 ++++++++- .../connection/client/HandshakeSessionHandler.java | 5 +++++ .../connection/client/InitialInboundConnection.java | 5 +++++ .../connection/client/LoginInboundConnection.java | 5 +++++ 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java b/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java index 224abbd6e..1b16e9e14 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java @@ -26,11 +26,20 @@ public interface InboundConnection { /** * Returns the hostname that the user entered into the client, if applicable. - * + *
+ * This is partially processed, including removing a trailing dot, and discarding data after a null byte. + * @return the hostname from the client */ Optional getVirtualHost(); + /** + * Returns the raw hostname that the client sent, if applicable. + * + * @return the raw hostname from the client + */ + Optional getRawVirtualHost(); + /** * Determine whether or not the player remains online. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java index ac02bcf76..6165a8467 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java @@ -96,7 +96,7 @@ public class AuthSessionHandler implements MinecraftSessionHandler { // Initiate a regular connection and move over to it. ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(), - mcConnection, inbound.getVirtualHost().orElse(null), onlineMode, + mcConnection, inbound.getVirtualHost().orElse(null), inbound.getRawVirtualHost().orElse(null), onlineMode, inbound.getIdentifiedKey()); this.connectedPlayer = player; if (!server.canRegisterConnection(player)) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 2b22fc7bc..46c3d63d2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -155,6 +155,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, */ private final MinecraftConnection connection; private final @Nullable InetSocketAddress virtualHost; + private final @Nullable String rawVirtualHost; private GameProfile profile; private PermissionFunction permissionFunction; private int tryIndex = 0; @@ -191,12 +192,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, private final ChatBuilderFactory chatBuilderFactory; ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, - @Nullable InetSocketAddress virtualHost, boolean onlineMode, + @Nullable InetSocketAddress virtualHost, @Nullable String rawVirtualHost, boolean onlineMode, @Nullable IdentifiedKey playerKey) { this.server = server; this.profile = profile; this.connection = connection; this.virtualHost = virtualHost; + this.rawVirtualHost = rawVirtualHost; this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED; this.connectionPhase = connection.getType().getInitialClientPhase(); this.onlineMode = onlineMode; @@ -356,6 +358,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, return Optional.ofNullable(virtualHost); } + @Override + public Optional getRawVirtualHost() { + return Optional.ofNullable(rawVirtualHost); + } + void setPermissionFunction(PermissionFunction permissionFunction) { this.permissionFunction = permissionFunction; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java index fae6533db..f31e08126 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java @@ -243,6 +243,11 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { return Optional.ofNullable(ping.getVhost()); } + @Override + public Optional getRawVirtualHost() { + return getVirtualHost().map(InetSocketAddress::getHostName); + } + @Override public boolean isActive() { return !connection.isClosed(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java index 2441e2ac5..7b9183745 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java @@ -63,6 +63,11 @@ public final class InitialInboundConnection implements VelocityInboundConnection return Optional.of(InetSocketAddress.createUnresolved(cleanedAddress, handshake.getPort())); } + @Override + public Optional getRawVirtualHost() { + return Optional.of(handshake.getServerAddress()); + } + @Override public boolean isActive() { return connection.getChannel().isActive(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java index 837376f95..18596e4e6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java @@ -71,6 +71,11 @@ public class LoginInboundConnection implements LoginPhaseConnection, KeyIdentifi return delegate.getVirtualHost(); } + @Override + public Optional getRawVirtualHost() { + return delegate.getRawVirtualHost(); + } + @Override public boolean isActive() { return delegate.isActive(); From 9a93bbe99013eda85fccf58ef59ee07a4fac7244 Mon Sep 17 00:00:00 2001 From: Elikill58 Date: Mon, 21 Oct 2024 02:05:29 +0200 Subject: [PATCH 27/38] Add deprecation notice (#1442) --- .../velocitypowered/api/event/player/PlayerChatEvent.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java index 786173972..dd1fae29b 100644 --- a/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/player/PlayerChatEvent.java @@ -51,6 +51,13 @@ public final class PlayerChatEvent implements ResultedEvent Date: Sun, 20 Oct 2024 17:06:01 -0700 Subject: [PATCH 28/38] set annotation processor to isolating to decrease compile times on big plugins (#1434) Signed-off-by: U5B <56985400+U5B@users.noreply.github.com> --- .../resources/META-INF/gradle/incremental.annotation.processors | 1 + 1 file changed, 1 insertion(+) create mode 100644 api/src/ap/resources/META-INF/gradle/incremental.annotation.processors diff --git a/api/src/ap/resources/META-INF/gradle/incremental.annotation.processors b/api/src/ap/resources/META-INF/gradle/incremental.annotation.processors new file mode 100644 index 000000000..4e3ccc3f8 --- /dev/null +++ b/api/src/ap/resources/META-INF/gradle/incremental.annotation.processors @@ -0,0 +1 @@ +com.velocitypowered.api.plugin.ap.PluginAnnotationProcessor,isolating \ No newline at end of file From d4e89dbdda9cde355c3e365c67d5428c541afb2f Mon Sep 17 00:00:00 2001 From: Jackson Date: Mon, 21 Oct 2024 11:09:55 +1100 Subject: [PATCH 29/38] Ability to specify servers from the command line (#1445) --- .../velocitypowered/proxy/ProxyOptions.java | 54 +++++++++++++++++++ .../velocitypowered/proxy/VelocityServer.java | 10 +++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java b/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java index 64a49917d..c660bf50e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java @@ -17,11 +17,18 @@ package com.velocitypowered.proxy; +import com.velocitypowered.api.proxy.server.ServerInfo; import java.io.IOException; +import java.net.InetSocketAddress; import java.util.Arrays; +import java.util.List; + +import com.velocitypowered.proxy.util.AddressUtil; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; +import joptsimple.ValueConversionException; +import joptsimple.ValueConverter; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -35,6 +42,8 @@ public final class ProxyOptions { private final boolean help; private final @Nullable Integer port; private final @Nullable Boolean haproxy; + private final boolean ignoreConfigServers; + private final List servers; ProxyOptions(final String[] args) { final OptionParser parser = new OptionParser(); @@ -49,11 +58,20 @@ public final class ProxyOptions { "Choose whether to enable haproxy protocol. " + "The configuration haproxy protocol will be ignored.") .withRequiredArg().ofType(Boolean.class); + final OptionSpec servers = parser.accepts("add-server", + "Define a server mapping. " + + "You must ensure that server name is not also registered in the config or use --ignore-config-servers.") + .withRequiredArg().withValuesConvertedBy(new ServerInfoConverter()); + final OptionSpec ignoreConfigServers = parser.accepts("ignore-config-servers", + "Skip registering servers from the config file. " + + "Useful in dynamic setups or with the --add-server flag."); final OptionSet set = parser.parse(args); this.help = set.has(help); this.port = port.value(set); this.haproxy = haproxy.value(set); + this.servers = servers.values(set); + this.ignoreConfigServers = set.has(ignoreConfigServers); if (this.help) { try { @@ -75,4 +93,40 @@ public final class ProxyOptions { public @Nullable Boolean isHaproxy() { return this.haproxy; } + + public boolean isIgnoreConfigServers() { + return this.ignoreConfigServers; + } + + public List getServers() { + return this.servers; + } + + private static class ServerInfoConverter implements ValueConverter { + + @Override + public ServerInfo convert(String s) { + String[] split = s.split(":", 2); + if (split.length < 2) { + throw new ValueConversionException("Invalid server format. Use :
"); + } + InetSocketAddress address; + try { + address = AddressUtil.parseAddress(split[1]); + } catch (IllegalStateException e) { + throw new ValueConversionException("Invalid hostname for server flag with name: " + split[0]); + } + return new ServerInfo(split[0], address); + } + + @Override + public Class valueType() { + return ServerInfo.class; + } + + @Override + public String valuePattern() { + return "name>: entry : configuration.getServers().entrySet()) { - servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue()))); + for (ServerInfo cliServer : options.getServers()) { + servers.register(cliServer); + } + + if (!options.isIgnoreConfigServers()) { + for (Map.Entry entry : configuration.getServers().entrySet()) { + servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue()))); + } } ipAttemptLimiter = Ratelimiters.createWithMilliseconds(configuration.getLoginRatelimit()); From 2c8ce219f0827823f95fd8c3dc68d1fc22a4bb31 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 20 Oct 2024 20:20:42 -0400 Subject: [PATCH 30/38] fix checkstyle, closes #1446 --- .../src/main/java/com/velocitypowered/proxy/ProxyOptions.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java b/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java index c660bf50e..d0b7f34f2 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/ProxyOptions.java @@ -18,12 +18,11 @@ package com.velocitypowered.proxy; import com.velocitypowered.api.proxy.server.ServerInfo; +import com.velocitypowered.proxy.util.AddressUtil; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.List; - -import com.velocitypowered.proxy.util.AddressUtil; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; From 05235da55d7a1dc59958ffa95163ea983275037e Mon Sep 17 00:00:00 2001 From: Gero Date: Tue, 22 Oct 2024 17:33:55 +0200 Subject: [PATCH 31/38] 1.21.2 (#1447) * 24w33a * 24w34a * 24w38a * 24w40a * 1.21.2 --- .../api/network/ProtocolVersion.java | 3 +- .../api/proxy/player/PlayerSettings.java | 23 ++++ .../client/ClientSettingsWrapper.java | 26 +++-- .../proxy/protocol/StateRegistry.java | 100 ++++++++++++------ .../protocol/packet/ClientSettingsPacket.java | 48 ++++++--- .../proxy/protocol/packet/JoinGamePacket.java | 19 ++++ .../proxy/protocol/packet/RespawnPacket.java | 24 ++++- .../packet/UpsertPlayerInfoPacket.java | 15 +++ 8 files changed, 203 insertions(+), 55 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 8bb0c98b0..1c6b6d536 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -87,7 +87,8 @@ public enum ProtocolVersion implements Ordered { MINECRAFT_1_20_2(764, "1.20.2"), MINECRAFT_1_20_3(765, "1.20.3", "1.20.4"), MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"), - MINECRAFT_1_21(767, "1.21", "1.21.1"); + MINECRAFT_1_21(767, "1.21", "1.21.1"), + MINECRAFT_1_21_2(768, "1.21.2"); private static final int SNAPSHOT_BIT = 30; diff --git a/api/src/main/java/com/velocitypowered/api/proxy/player/PlayerSettings.java b/api/src/main/java/com/velocitypowered/api/proxy/player/PlayerSettings.java index 320b1c203..416cbf154 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/player/PlayerSettings.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/player/PlayerSettings.java @@ -68,6 +68,20 @@ public interface PlayerSettings { */ boolean isClientListingAllowed(); + /** + * Returns if the client has text filtering enabled. + * + * @return if text filtering is enabled + */ + boolean isTextFilteringEnabled(); + + /** + * Returns the selected "Particles" option state. + * + * @return the particle option + */ + ParticleStatus getParticleStatus(); + /** * The client's current chat display mode. */ @@ -84,4 +98,13 @@ public interface PlayerSettings { LEFT, RIGHT } + + /** + * The client's current "Particles" option state. + */ + enum ParticleStatus { + ALL, + DECREASED, + MINIMAL + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java index 5032ff1f4..f8d3107cc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientSettingsWrapper.java @@ -30,7 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class ClientSettingsWrapper implements PlayerSettings { static final PlayerSettings DEFAULT = new ClientSettingsWrapper( - new ClientSettingsPacket("en_US", (byte) 10, 0, true, (short) 127, 1, true, false)); + new ClientSettingsPacket("en_us", (byte) 2, 0, true, (short) 0, 1, false, false, 0)); private final ClientSettingsPacket settings; private final SkinParts parts; @@ -56,11 +56,11 @@ public class ClientSettingsWrapper implements PlayerSettings { @Override public ChatMode getChatMode() { - int chat = settings.getChatVisibility(); - if (chat < 0 || chat > 2) { - return ChatMode.SHOWN; - } - return ChatMode.values()[chat]; + return switch (settings.getChatVisibility()) { + case 1 -> ChatMode.COMMANDS_ONLY; + case 2 -> ChatMode.HIDDEN; + default -> ChatMode.SHOWN; + }; } @Override @@ -83,6 +83,20 @@ public class ClientSettingsWrapper implements PlayerSettings { return settings.isClientListingAllowed(); } + @Override + public boolean isTextFilteringEnabled() { + return settings.isTextFilteringEnabled(); + } + + @Override + public ParticleStatus getParticleStatus() { + return switch (settings.getParticleStatus()) { + case 1 -> ParticleStatus.DECREASED; + case 2 -> ParticleStatus.MINIMAL; + default -> ParticleStatus.ALL; + }; + } + @Override public boolean equals(@Nullable final Object o) { if (this == o) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 41d444a36..1bd70248a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -37,6 +37,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; @@ -252,7 +253,8 @@ public enum StateRegistry { map(0x08, MINECRAFT_1_19_3, false), map(0x09, MINECRAFT_1_19_4, false), map(0x0A, MINECRAFT_1_20_2, false), - map(0x0B, MINECRAFT_1_20_5, false)); + map(0x0B, MINECRAFT_1_20_5, false), + map(0x0D, MINECRAFT_1_21_2, false)); serverbound.register( LegacyChatPacket.class, LegacyChatPacket::new, @@ -264,7 +266,8 @@ public enum StateRegistry { serverbound.register( ChatAcknowledgementPacket.class, ChatAcknowledgementPacket::new, - map(0x03, MINECRAFT_1_19_3, false)); + map(0x03, MINECRAFT_1_19_3, false), + map(0x04, MINECRAFT_1_21_2, false)); serverbound.register(KeyedPlayerCommandPacket.class, KeyedPlayerCommandPacket::new, map(0x03, MINECRAFT_1_19, false), map(0x04, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); @@ -273,14 +276,17 @@ public enum StateRegistry { map(0x05, MINECRAFT_1_19_1, MINECRAFT_1_19_1, false)); serverbound.register(SessionPlayerCommandPacket.class, SessionPlayerCommandPacket::new, map(0x04, MINECRAFT_1_19_3, false), - map(0x05, MINECRAFT_1_20_5, false)); + map(0x05, MINECRAFT_1_20_5, false), + map(0x06, MINECRAFT_1_21_2, false)); serverbound.register(UnsignedPlayerCommandPacket.class, UnsignedPlayerCommandPacket::new, - map(0x04, MINECRAFT_1_20_5, false)); + map(0x04, MINECRAFT_1_20_5, false), + map(0x05, MINECRAFT_1_21_2, false)); serverbound.register( SessionPlayerChatPacket.class, SessionPlayerChatPacket::new, map(0x05, MINECRAFT_1_19_3, false), - map(0x06, MINECRAFT_1_20_5, false)); + map(0x06, MINECRAFT_1_20_5, false), + map(0x07, MINECRAFT_1_21_2, false)); serverbound.register( ClientSettingsPacket.class, ClientSettingsPacket::new, @@ -294,10 +300,12 @@ public enum StateRegistry { map(0x07, MINECRAFT_1_19_3, false), map(0x08, MINECRAFT_1_19_4, false), map(0x09, MINECRAFT_1_20_2, false), - map(0x0A, MINECRAFT_1_20_5, false)); + map(0x0A, MINECRAFT_1_20_5, false), + map(0x0C, MINECRAFT_1_21_2, false)); serverbound.register( ServerboundCookieResponsePacket.class, ServerboundCookieResponsePacket::new, - map(0x11, MINECRAFT_1_20_5, false)); + map(0x11, MINECRAFT_1_20_5, false), + map(0x13, MINECRAFT_1_21_2, false)); serverbound.register( PluginMessagePacket.class, PluginMessagePacket::new, @@ -314,7 +322,8 @@ public enum StateRegistry { map(0x0D, MINECRAFT_1_19_4, false), map(0x0F, MINECRAFT_1_20_2, false), map(0x10, MINECRAFT_1_20_3, false), - map(0x12, MINECRAFT_1_20_5, false)); + map(0x12, MINECRAFT_1_20_5, false), + map(0x14, MINECRAFT_1_21_2, false)); serverbound.register( KeepAlivePacket.class, KeepAlivePacket::new, @@ -332,7 +341,8 @@ public enum StateRegistry { map(0x12, MINECRAFT_1_19_4, false), map(0x14, MINECRAFT_1_20_2, false), map(0x15, MINECRAFT_1_20_3, false), - map(0x18, MINECRAFT_1_20_5, false)); + map(0x18, MINECRAFT_1_20_5, false), + map(0x1A, MINECRAFT_1_21_2, false)); serverbound.register( ResourcePackResponsePacket.class, ResourcePackResponsePacket::new, @@ -347,11 +357,13 @@ public enum StateRegistry { map(0x24, MINECRAFT_1_19_1, false), map(0x27, MINECRAFT_1_20_2, false), map(0x28, MINECRAFT_1_20_3, false), - map(0x2B, MINECRAFT_1_20_5, false)); + map(0x2B, MINECRAFT_1_20_5, false), + map(0x2D, MINECRAFT_1_21_2, false)); serverbound.register( FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE, map(0x0B, MINECRAFT_1_20_2, false), - map(0x0C, MINECRAFT_1_20_5, false)); + map(0x0C, MINECRAFT_1_20_5, false), + map(0x0E, MINECRAFT_1_21_2, false)); clientbound.register( BossBarPacket.class, @@ -449,7 +461,8 @@ public enum StateRegistry { map(0x1F, MINECRAFT_1_19_3, false), map(0x23, MINECRAFT_1_19_4, false), map(0x24, MINECRAFT_1_20_2, false), - map(0x26, MINECRAFT_1_20_5, false)); + map(0x26, MINECRAFT_1_20_5, false), + map(0x27, MINECRAFT_1_21_2, false)); clientbound.register( JoinGamePacket.class, JoinGamePacket::new, @@ -466,7 +479,8 @@ public enum StateRegistry { map(0x24, MINECRAFT_1_19_3, false), map(0x28, MINECRAFT_1_19_4, false), map(0x29, MINECRAFT_1_20_2, false), - map(0x2B, MINECRAFT_1_20_5, false)); + map(0x2B, MINECRAFT_1_20_5, false), + map(0x2C, MINECRAFT_1_21_2, false)); clientbound.register( RespawnPacket.class, RespawnPacket::new, @@ -486,12 +500,14 @@ public enum StateRegistry { map(0x41, MINECRAFT_1_19_4, true), map(0x43, MINECRAFT_1_20_2, true), map(0x45, MINECRAFT_1_20_3, true), - map(0x47, MINECRAFT_1_20_5, true)); + map(0x47, MINECRAFT_1_20_5, true), + map(0x4C, MINECRAFT_1_21_2, true)); clientbound.register( RemoveResourcePackPacket.class, RemoveResourcePackPacket::new, map(0x43, MINECRAFT_1_20_3, false), - map(0x45, MINECRAFT_1_20_5, false)); + map(0x45, MINECRAFT_1_20_5, false), + map(0x4A, MINECRAFT_1_21_2, false)); clientbound.register( ResourcePackRequestPacket.class, ResourcePackRequestPacket::new, @@ -511,7 +527,8 @@ public enum StateRegistry { map(0x40, MINECRAFT_1_19_4, false), map(0x42, MINECRAFT_1_20_2, false), map(0x44, MINECRAFT_1_20_3, false), - map(0x46, MINECRAFT_1_20_5, false)); + map(0x46, MINECRAFT_1_20_5, false), + map(0x4B, MINECRAFT_1_21_2, false)); clientbound.register( HeaderAndFooterPacket.class, HeaderAndFooterPacket::new, @@ -532,7 +549,8 @@ public enum StateRegistry { map(0x65, MINECRAFT_1_19_4, true), map(0x68, MINECRAFT_1_20_2, true), map(0x6A, MINECRAFT_1_20_3, true), - map(0x6D, MINECRAFT_1_20_5, true)); + map(0x6D, MINECRAFT_1_20_5, true), + map(0x74, MINECRAFT_1_21_2, true)); clientbound.register( LegacyTitlePacket.class, LegacyTitlePacket::new, @@ -552,7 +570,8 @@ public enum StateRegistry { map(0x5D, MINECRAFT_1_19_4, true), map(0x5F, MINECRAFT_1_20_2, true), map(0x61, MINECRAFT_1_20_3, true), - map(0x63, MINECRAFT_1_20_5, true)); + map(0x63, MINECRAFT_1_20_5, true), + map(0x6A, MINECRAFT_1_21_2, true)); clientbound.register( TitleTextPacket.class, TitleTextPacket::new, @@ -563,7 +582,8 @@ public enum StateRegistry { map(0x5F, MINECRAFT_1_19_4, true), map(0x61, MINECRAFT_1_20_2, true), map(0x63, MINECRAFT_1_20_3, true), - map(0x65, MINECRAFT_1_20_5, true)); + map(0x65, MINECRAFT_1_20_5, true), + map(0x6C, MINECRAFT_1_21_2, true)); clientbound.register( TitleActionbarPacket.class, TitleActionbarPacket::new, @@ -574,7 +594,8 @@ public enum StateRegistry { map(0x46, MINECRAFT_1_19_4, true), map(0x48, MINECRAFT_1_20_2, true), map(0x4A, MINECRAFT_1_20_3, true), - map(0x4C, MINECRAFT_1_20_5, true)); + map(0x4C, MINECRAFT_1_20_5, true), + map(0x51, MINECRAFT_1_21_2, true)); clientbound.register( TitleTimesPacket.class, TitleTimesPacket::new, @@ -585,7 +606,8 @@ public enum StateRegistry { map(0x60, MINECRAFT_1_19_4, true), map(0x62, MINECRAFT_1_20_2, true), map(0x64, MINECRAFT_1_20_3, true), - map(0x66, MINECRAFT_1_20_5, true)); + map(0x66, MINECRAFT_1_20_5, true), + map(0x6D, MINECRAFT_1_21_2, true)); clientbound.register( TitleClearPacket.class, TitleClearPacket::new, @@ -612,17 +634,20 @@ public enum StateRegistry { map(0x35, MINECRAFT_1_19_3, false), map(0x39, MINECRAFT_1_19_4, false), map(0x3B, MINECRAFT_1_20_2, false), - map(0x3D, MINECRAFT_1_20_5, false)); + map(0x3D, MINECRAFT_1_20_5, false), + map(0x3F, MINECRAFT_1_21_2, false)); clientbound.register( UpsertPlayerInfoPacket.class, UpsertPlayerInfoPacket::new, map(0x36, MINECRAFT_1_19_3, false), map(0x3A, MINECRAFT_1_19_4, false), map(0x3C, MINECRAFT_1_20_2, false), - map(0x3E, MINECRAFT_1_20_5, false)); + map(0x3E, MINECRAFT_1_20_5, false), + map(0x40, MINECRAFT_1_21_2, false)); clientbound.register( ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new, - map(0x6B, MINECRAFT_1_20_5, false)); + map(0x6B, MINECRAFT_1_20_5, false), + map(0x72, MINECRAFT_1_21_2, false)); clientbound.register( SystemChatPacket.class, SystemChatPacket::new, @@ -632,7 +657,8 @@ public enum StateRegistry { map(0x64, MINECRAFT_1_19_4, true), map(0x67, MINECRAFT_1_20_2, true), map(0x69, MINECRAFT_1_20_3, true), - map(0x6C, MINECRAFT_1_20_5, true)); + map(0x6C, MINECRAFT_1_20_5, true), + map(0x73, MINECRAFT_1_21_2, true)); clientbound.register( PlayerChatCompletionPacket.class, PlayerChatCompletionPacket::new, @@ -650,13 +676,15 @@ public enum StateRegistry { map(0x45, MINECRAFT_1_19_4, false), map(0x47, MINECRAFT_1_20_2, false), map(0x49, MINECRAFT_1_20_3, false), - map(0x4B, MINECRAFT_1_20_5, false)); + map(0x4B, MINECRAFT_1_20_5, false), + map(0x50, MINECRAFT_1_21_2, false)); clientbound.register( StartUpdatePacket.class, () -> StartUpdatePacket.INSTANCE, map(0x65, MINECRAFT_1_20_2, false), map(0x67, MINECRAFT_1_20_3, false), - map(0x69, MINECRAFT_1_20_5, false)); + map(0x69, MINECRAFT_1_20_5, false), + map(0x70, MINECRAFT_1_21_2, false)); clientbound.register( BundleDelimiterPacket.class, () -> BundleDelimiterPacket.INSTANCE, @@ -664,12 +692,18 @@ public enum StateRegistry { clientbound.register( TransferPacket.class, TransferPacket::new, - map(0x73, MINECRAFT_1_20_5, false) - ); - clientbound.register(ClientboundCustomReportDetailsPacket.class, ClientboundCustomReportDetailsPacket::new, - map(0x7A, MINECRAFT_1_21, false)); - clientbound.register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new, - map(0x7B, MINECRAFT_1_21, false)); + map(0x73, MINECRAFT_1_20_5, false), + map(0x7A, MINECRAFT_1_21_2, false)); + clientbound.register( + ClientboundCustomReportDetailsPacket.class, + ClientboundCustomReportDetailsPacket::new, + map(0x7A, MINECRAFT_1_21, false), + map(0x81, MINECRAFT_1_21_2, false)); + clientbound.register( + ClientboundServerLinksPacket.class, + ClientboundServerLinksPacket::new, + map(0x7B, MINECRAFT_1_21, false), + map(0x82, MINECRAFT_1_21_2, false)); } }, LOGIN { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java index 0e3668030..39e6fde02 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientSettingsPacket.java @@ -34,21 +34,25 @@ public class ClientSettingsPacket implements MinecraftPacket { private byte difficulty; // 1.7 Protocol private short skinParts; private int mainHand; - private boolean chatFilteringEnabled; // Added in 1.17 + private boolean textFilteringEnabled; // Added in 1.17 private boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode + private int particleStatus; // Added in 1.21.2 public ClientSettingsPacket() { } public ClientSettingsPacket(String locale, byte viewDistance, int chatVisibility, boolean chatColors, - short skinParts, int mainHand, boolean chatFilteringEnabled, boolean clientListingAllowed) { + short skinParts, int mainHand, boolean textFilteringEnabled, boolean clientListingAllowed, + int particleStatus) { this.locale = locale; this.viewDistance = viewDistance; this.chatVisibility = chatVisibility; this.chatColors = chatColors; this.skinParts = skinParts; this.mainHand = mainHand; + this.textFilteringEnabled = textFilteringEnabled; this.clientListingAllowed = clientListingAllowed; + this.particleStatus = particleStatus; } public String getLocale() { @@ -102,12 +106,12 @@ public class ClientSettingsPacket implements MinecraftPacket { this.mainHand = mainHand; } - public boolean isChatFilteringEnabled() { - return chatFilteringEnabled; + public boolean isTextFilteringEnabled() { + return textFilteringEnabled; } - public void setChatFilteringEnabled(boolean chatFilteringEnabled) { - this.chatFilteringEnabled = chatFilteringEnabled; + public void setTextFilteringEnabled(boolean textFilteringEnabled) { + this.textFilteringEnabled = textFilteringEnabled; } public boolean isClientListingAllowed() { @@ -118,12 +122,20 @@ public class ClientSettingsPacket implements MinecraftPacket { this.clientListingAllowed = clientListingAllowed; } + public int getParticleStatus() { + return particleStatus; + } + + public void setParticleStatus(int particleStatus) { + this.particleStatus = particleStatus; + } + @Override public String toString() { return "ClientSettings{" + "locale='" + locale + '\'' + ", viewDistance=" + viewDistance + ", chatVisibility=" + chatVisibility + ", chatColors=" + chatColors + ", skinParts=" + - skinParts + ", mainHand=" + mainHand + ", chatFilteringEnabled=" + chatFilteringEnabled + - ", clientListingAllowed=" + clientListingAllowed + '}'; + skinParts + ", mainHand=" + mainHand + ", chatFilteringEnabled=" + textFilteringEnabled + + ", clientListingAllowed=" + clientListingAllowed + ", particleStatus=" + particleStatus + '}'; } @Override @@ -143,10 +155,14 @@ public class ClientSettingsPacket implements MinecraftPacket { this.mainHand = ProtocolUtils.readVarInt(buf); if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { - this.chatFilteringEnabled = buf.readBoolean(); + this.textFilteringEnabled = buf.readBoolean(); if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { this.clientListingAllowed = buf.readBoolean(); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + this.particleStatus = ProtocolUtils.readVarInt(buf); + } } } } @@ -172,11 +188,15 @@ public class ClientSettingsPacket implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, mainHand); if (version.noLessThan(ProtocolVersion.MINECRAFT_1_17)) { - buf.writeBoolean(chatFilteringEnabled); + buf.writeBoolean(textFilteringEnabled); if (version.noLessThan(ProtocolVersion.MINECRAFT_1_18)) { buf.writeBoolean(clientListingAllowed); } + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + ProtocolUtils.writeVarInt(buf, particleStatus); + } } } } @@ -201,8 +221,9 @@ public class ClientSettingsPacket implements MinecraftPacket { && difficulty == that.difficulty && skinParts == that.skinParts && mainHand == that.mainHand - && chatFilteringEnabled == that.chatFilteringEnabled + && textFilteringEnabled == that.textFilteringEnabled && clientListingAllowed == that.clientListingAllowed + && particleStatus == that.particleStatus && Objects.equals(locale, that.locale); } @@ -216,7 +237,8 @@ public class ClientSettingsPacket implements MinecraftPacket { difficulty, skinParts, mainHand, - chatFilteringEnabled, - clientListingAllowed); + textFilteringEnabled, + clientListingAllowed, + particleStatus); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java index f1d2b6400..787d858eb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/JoinGamePacket.java @@ -51,6 +51,7 @@ public class JoinGamePacket implements MinecraftPacket { private int simulationDistance; // 1.18+ private @Nullable Pair lastDeathPosition; // 1.19+ private int portalCooldown; // 1.20+ + private int seaLevel; // 1.21.2+ private boolean enforcesSecureChat; // 1.20.5+ public int getEntityId() { @@ -181,6 +182,14 @@ public class JoinGamePacket implements MinecraftPacket { this.portalCooldown = portalCooldown; } + public int getSeaLevel() { + return seaLevel; + } + + public void setSeaLevel(int seaLevel) { + this.seaLevel = seaLevel; + } + public boolean getEnforcesSecureChat() { return this.enforcesSecureChat; } @@ -204,6 +213,7 @@ public class JoinGamePacket implements MinecraftPacket { dimensionInfo + '\'' + ", currentDimensionData='" + currentDimensionData + '\'' + ", previousGamemode=" + previousGamemode + ", simulationDistance=" + simulationDistance + ", lastDeathPosition='" + lastDeathPosition + '\'' + ", portalCooldown=" + portalCooldown + + ", seaLevel=" + seaLevel + '}'; } @@ -343,6 +353,11 @@ public class JoinGamePacket implements MinecraftPacket { } this.portalCooldown = ProtocolUtils.readVarInt(buf); + + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + this.seaLevel = ProtocolUtils.readVarInt(buf); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { this.enforcesSecureChat = buf.readBoolean(); } @@ -491,6 +506,10 @@ public class JoinGamePacket implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, portalCooldown); + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + ProtocolUtils.writeVarInt(buf, seaLevel); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_5)) { buf.writeBoolean(this.enforcesSecureChat); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java index ceaee9fd2..9304dc3f9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java @@ -41,6 +41,7 @@ public class RespawnPacket implements MinecraftPacket { private CompoundBinaryTag currentDimensionData; // 1.16.2+ private @Nullable Pair lastDeathPosition; // 1.19+ private int portalCooldown; // 1.20+ + private int seaLevel; // 1.21.2+ public RespawnPacket() { } @@ -48,7 +49,8 @@ public class RespawnPacket implements MinecraftPacket { public RespawnPacket(int dimension, long partialHashedSeed, short difficulty, short gamemode, String levelType, byte dataToKeep, DimensionInfo dimensionInfo, short previousGamemode, CompoundBinaryTag currentDimensionData, - @Nullable Pair lastDeathPosition, int portalCooldown) { + @Nullable Pair lastDeathPosition, int portalCooldown, + int seaLevel) { this.dimension = dimension; this.partialHashedSeed = partialHashedSeed; this.difficulty = difficulty; @@ -60,13 +62,15 @@ public class RespawnPacket implements MinecraftPacket { this.currentDimensionData = currentDimensionData; this.lastDeathPosition = lastDeathPosition; this.portalCooldown = portalCooldown; + this.seaLevel = seaLevel; } public static RespawnPacket fromJoinGame(JoinGamePacket joinGame) { return new RespawnPacket(joinGame.getDimension(), joinGame.getPartialHashedSeed(), joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType(), (byte) 0, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode(), - joinGame.getCurrentDimensionData(), joinGame.getLastDeathPosition(), joinGame.getPortalCooldown()); + joinGame.getCurrentDimensionData(), joinGame.getLastDeathPosition(), + joinGame.getPortalCooldown(), joinGame.getSeaLevel()); } public int getDimension() { @@ -141,6 +145,14 @@ public class RespawnPacket implements MinecraftPacket { this.portalCooldown = portalCooldown; } + public int getSeaLevel() { + return seaLevel; + } + + public void setSeaLevel(int seaLevel) { + this.seaLevel = seaLevel; + } + @Override public String toString() { return "Respawn{" @@ -155,6 +167,7 @@ public class RespawnPacket implements MinecraftPacket { + ", previousGamemode=" + previousGamemode + ", dimensionData=" + currentDimensionData + ", portalCooldown=" + portalCooldown + + ", seaLevel=" + seaLevel + '}'; } @@ -204,6 +217,9 @@ public class RespawnPacket implements MinecraftPacket { if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20)) { this.portalCooldown = ProtocolUtils.readVarInt(buf); } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + this.seaLevel = ProtocolUtils.readVarInt(buf); + } if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { this.dataToKeep = buf.readByte(); } @@ -262,6 +278,10 @@ public class RespawnPacket implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, portalCooldown); } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_1)) { + ProtocolUtils.writeVarInt(buf, seaLevel); + } + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { buf.writeByte(dataToKeep); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java index 4c70536b0..e2d40d2e3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java @@ -188,6 +188,11 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { if (info.displayName != null) { info.displayName.write(buf); } + }), + UPDATE_LIST_ORDER((version, buf, info) -> { // read + info.listOrder = ProtocolUtils.readVarInt(buf); + }, (version, buf, info) -> { // write + ProtocolUtils.writeVarInt(buf, info.listOrder); }); private final Read read; @@ -218,6 +223,7 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { private int gameMode; @Nullable private ComponentHolder displayName; + private int listOrder; @Nullable private RemoteChatSession chatSession; @@ -250,6 +256,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { return displayName; } + public int getListOrder() { + return listOrder; + } + @Nullable public RemoteChatSession getChatSession() { return chatSession; @@ -275,6 +285,10 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { this.displayName = displayName; } + public void setListOrder(int listOrder) { + this.listOrder = listOrder; + } + public void setChatSession(@Nullable RemoteChatSession chatSession) { this.chatSession = chatSession; } @@ -288,6 +302,7 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { ", latency=" + latency + ", gameMode=" + gameMode + ", displayName=" + displayName + + ", listOrder=" + listOrder + ", chatSession=" + chatSession + '}'; } From 7a9227d517f7d95296458600fe2ca34f0f2bc296 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 22 Oct 2024 19:01:55 +0200 Subject: [PATCH 32/38] Fix extra respawn packet byte on <1.21.2 (#1448) --- .../velocitypowered/proxy/protocol/packet/RespawnPacket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java index 9304dc3f9..fd9c8ca77 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/RespawnPacket.java @@ -278,7 +278,7 @@ public class RespawnPacket implements MinecraftPacket { ProtocolUtils.writeVarInt(buf, portalCooldown); } - if (version.noLessThan(ProtocolVersion.MINECRAFT_1_12_1)) { + if (version.noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { ProtocolUtils.writeVarInt(buf, seaLevel); } From 69ee655d7490705f950699f9d3b0a206671456ab Mon Sep 17 00:00:00 2001 From: Timon Date: Wed, 23 Oct 2024 20:39:12 +0200 Subject: [PATCH 33/38] Update 1.21.2 protocol to include 1.21.3 release (#1449) --- .../java/com/velocitypowered/api/network/ProtocolVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 1c6b6d536..3b5bbcb85 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -88,7 +88,7 @@ public enum ProtocolVersion implements Ordered { MINECRAFT_1_20_3(765, "1.20.3", "1.20.4"), MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"), MINECRAFT_1_21(767, "1.21", "1.21.1"), - MINECRAFT_1_21_2(768, "1.21.2"); + MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"); private static final int SNAPSHOT_BIT = 30; From b135148dfc007e9e116ff9822c65d491ace2157c Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 27 Oct 2024 13:54:08 -0400 Subject: [PATCH 34/38] fix a typo --- .../proxy/protocol/netty/MinecraftVarintFrameDecoder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java index 7bf7563ea..84f35381e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java @@ -46,6 +46,7 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { // skip any runs of 0x00 we might find int packetStart = in.forEachByte(FIND_NON_NUL); if (packetStart == -1) { + in.clear(); return; } in.readerIndex(packetStart); From f2d6e143ae872f7da83d69a97acca0412923c7f6 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 27 Oct 2024 14:10:33 -0400 Subject: [PATCH 35/38] Update several dependencies --- gradle/libs.versions.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 510c4d62a..58e8e11bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,8 +2,8 @@ configurate3 = "3.7.3" configurate4 = "4.1.2" flare = "2.0.1" -log4j = "2.22.1" -netty = "4.1.106.Final" +log4j = "2.24.1" +netty = "4.1.114.Final" [plugins] indra-publishing = "net.kyori.indra.publishing:2.0.6" @@ -18,7 +18,7 @@ auto-service = "com.google.auto.service:auto-service:1.0.1" auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1" brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT" bstats = "org.bstats:bstats-base:3.0.2" -caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.5" +caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8" checker-qual = "org.checkerframework:checker-qual:3.42.0" checkstyle = "com.puppycrawl.tools:checkstyle:10.9.3" completablefutures = "com.spotify:completable-futures:0.3.6" @@ -28,15 +28,15 @@ configurate3-gson = { module = "org.spongepowered:configurate-gson", version.ref configurate4-hocon = { module = "org.spongepowered:configurate-hocon", version.ref = "configurate4" } configurate4-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate4" } configurate4-gson = { module = "org.spongepowered:configurate-gson", version.ref = "configurate4" } -disruptor = "com.lmax:disruptor:3.4.4" -fastutil = "it.unimi.dsi:fastutil:8.5.12" +disruptor = "com.lmax:disruptor:4.0.0" +fastutil = "it.unimi.dsi:fastutil:8.5.15" flare-core = { module = "space.vectrix.flare:flare", version.ref = "flare" } flare-fastutil = { module = "space.vectrix.flare:flare-fastutil", version.ref = "flare" } -jline = "org.jline:jline-terminal-jansi:3.23.0" +jline = "org.jline:jline-terminal-jansi:3.27.1" jopt = "net.sf.jopt-simple:jopt-simple:5.0.4" junit = "org.junit.jupiter:junit-jupiter:5.10.2" jspecify = "org.jspecify:jspecify:0.3.0" -kyori-ansi = "net.kyori:ansi:1.0.3" +kyori-ansi = "net.kyori:ansi:1.1.0" guava = "com.google.guava:guava:25.1-jre" gson = "com.google.code.gson:gson:2.10.1" guice = "com.google.inject:guice:6.0.0" From dc40e160d7dbbf63c448ca0fd6b2788d206f22eb Mon Sep 17 00:00:00 2001 From: Aaron <71191102+RealBauHD@users.noreply.github.com> Date: Sun, 27 Oct 2024 19:27:09 +0100 Subject: [PATCH 36/38] replace old velocitypowered.com links (#1399) * replace old links * content to 'PaperMC', cause its probably the best --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- .../proxy/command/builtin/VelocityCommand.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 82cada2b6..ec81079b6 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -44,7 +44,7 @@ body: ``` [17:44:10 INFO]: Velocity 3.3.0-SNAPSHOT (git-9d25d309-b400) [17:44:10 INFO]: Copyright 2018-2023 Velocity Contributors. Velocity is licensed under the terms of the GNU General Public License v3. - [17:44:10 INFO]: velocitypowered.com - GitHub + [17:44:10 INFO]: PaperMC - GitHub ``` validations: diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java index 79ac41e74..85bf2061b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java @@ -174,10 +174,10 @@ public final class VelocityCommand { if (version.getName().equals("Velocity")) { final TextComponent embellishment = Component.text() .append(Component.text() - .content("velocitypowered.com") + .content("PaperMC") .color(NamedTextColor.GREEN) .clickEvent( - ClickEvent.openUrl("https://velocitypowered.com")) + ClickEvent.openUrl("https://papermc.io/software/velocity")) .build()) .append(Component.text(" - ")) .append(Component.text() From 08a42b3723633ea5eb6b96c0bb42180f3c2b07eb Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sun, 27 Oct 2024 15:13:16 -0400 Subject: [PATCH 37/38] Replace home-made legacy hover event serializer with Adventure's implementation This technically can break backwards compatibility, but this seems to be very unlikely to be the practice in reality. (The Velocity implementation probably wasn't correct, anyway.) --- gradle/libs.versions.toml | 3 +- proxy/build.gradle.kts | 2 +- .../proxy/protocol/ProtocolUtils.java | 9 +- .../VelocityLegacyHoverEventSerializer.java | 123 ------------------ 4 files changed, 7 insertions(+), 130 deletions(-) delete mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 58e8e11bf..7557ed42c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,8 @@ spotless = "com.diffplug.spotless:6.25.0" [libraries] adventure-bom = "net.kyori:adventure-bom:4.17.0" -adventure-facet = "net.kyori:adventure-platform-facet:4.3.2" +adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.17.0" +adventure-facet = "net.kyori:adventure-platform-facet:4.3.4" asm = "org.ow2.asm:asm:9.6" auto-service = "com.google.auto.service:auto-service:1.0.1" auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1" diff --git a/proxy/build.gradle.kts b/proxy/build.gradle.kts index 035ff24de..9b74dae57 100644 --- a/proxy/build.gradle.kts +++ b/proxy/build.gradle.kts @@ -127,7 +127,7 @@ dependencies { runtimeOnly(libs.disruptor) implementation(libs.fastutil) implementation(platform(libs.adventure.bom)) - implementation("net.kyori:adventure-nbt") + implementation(libs.adventure.text.serializer.json.legacy.impl) implementation(libs.adventure.facet) implementation(libs.completablefutures) implementation(libs.nightconfig) 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 797a58223..ca63c1e56 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -25,7 +25,6 @@ import com.velocitypowered.api.proxy.crypto.IdentifiedKey; import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; -import com.velocitypowered.proxy.protocol.util.VelocityLegacyHoverEventSerializer; import com.velocitypowered.proxy.util.except.QuietDecoderException; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; @@ -47,6 +46,7 @@ import net.kyori.adventure.nbt.BinaryTagTypes; import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.json.JSONOptions; +import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer; import net.kyori.option.OptionState; /** @@ -58,8 +58,7 @@ public enum ProtocolUtils { private static final GsonComponentSerializer PRE_1_16_SERIALIZER = GsonComponentSerializer.builder() .downsampleColors() - .emitLegacyHoverEvent() - .legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE) + .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()) .options( OptionState.optionState() // before 1.16 @@ -74,7 +73,7 @@ public enum ProtocolUtils { .build(); private static final GsonComponentSerializer PRE_1_20_3_SERIALIZER = GsonComponentSerializer.builder() - .legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE) + .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()) .options( OptionState.optionState() // after 1.16 @@ -89,7 +88,7 @@ public enum ProtocolUtils { .build(); private static final GsonComponentSerializer MODERN_SERIALIZER = GsonComponentSerializer.builder() - .legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE) + .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()) .options( OptionState.optionState() // after 1.16 diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java deleted file mode 100644 index e667a9b16..000000000 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/VelocityLegacyHoverEventSerializer.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2020-2023 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.velocitypowered.proxy.protocol.util; - -import java.io.IOException; -import java.util.UUID; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.nbt.CompoundBinaryTag; -import net.kyori.adventure.nbt.TagStringIO; -import net.kyori.adventure.nbt.api.BinaryTagHolder; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.event.HoverEvent.ShowEntity; -import net.kyori.adventure.text.event.HoverEvent.ShowItem; -import net.kyori.adventure.text.serializer.gson.LegacyHoverEventSerializer; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; -import net.kyori.adventure.util.Codec.Decoder; -import net.kyori.adventure.util.Codec.Encoder; -import org.checkerframework.checker.nullness.qual.NonNull; - -/** - * An implementation of {@link LegacyHoverEventSerializer} that implements the interface in the most - * literal, albeit "incompatible" way possible. - */ -public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSerializer { - - public static final LegacyHoverEventSerializer INSTANCE = - new VelocityLegacyHoverEventSerializer(); - - private VelocityLegacyHoverEventSerializer() { - - } - - private static Key legacyIdToFakeKey(byte id) { - return Key.key("velocity", "legacy_hover/id_" + id); - } - - @Override - public HoverEvent.@NonNull ShowItem deserializeShowItem(@NonNull Component input) - throws IOException { - String snbt = PlainTextComponentSerializer.plainText().serialize(input); - CompoundBinaryTag item = TagStringIO.get().asCompound(snbt); - - Key key; - String idIfString = item.getString("id", ""); - if (idIfString.isEmpty()) { - key = legacyIdToFakeKey(item.getByte("id")); - } else { - key = Key.key(idIfString); - } - - byte count = item.getByte("Count", (byte) 1); - return ShowItem.of(key, count, BinaryTagHolder.binaryTagHolder(snbt)); - } - - @Override - public HoverEvent.@NonNull ShowEntity deserializeShowEntity(@NonNull Component input, - Decoder componentDecoder) throws IOException { - String snbt = PlainTextComponentSerializer.plainText().serialize(input); - CompoundBinaryTag item = TagStringIO.get().asCompound(snbt); - - Component name; - try { - name = componentDecoder.decode(item.getString("name")); - } catch (Exception e) { - name = Component.text(item.getString("name")); - } - - return ShowEntity.of(Key.key(item.getString("type")), - UUID.fromString(item.getString("id")), - name); - } - - @Override - public @NonNull Component serializeShowItem(HoverEvent.@NonNull ShowItem input) - throws IOException { - final CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder() - .putByte("Count", (byte) input.count()); - - String keyAsString = input.item().asString(); - if (keyAsString.startsWith("velocity:legacy_hover/id_")) { - builder.putByte("id", Byte.parseByte(keyAsString - .substring("velocity:legacy_hover/id_".length()))); - } else { - builder.putString("id", keyAsString); - } - - BinaryTagHolder nbt = input.nbt(); - if (nbt != null) { - builder.put("tag", TagStringIO.get().asCompound(nbt.string())); - } - - return Component.text(TagStringIO.get().asString(builder.build())); - } - - @Override - public @NonNull Component serializeShowEntity(HoverEvent.@NonNull ShowEntity input, - Encoder componentEncoder) throws IOException { - CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder() - .putString("id", input.id().toString()) - .putString("type", input.type().asString()); - Component name = input.name(); - if (name != null) { - tag.putString("name", componentEncoder.encode(name)); - } - return Component.text(TagStringIO.get().asString(tag.build())); - } -} From cefa3b272ef06715976fbea28ac5937305f2d27d Mon Sep 17 00:00:00 2001 From: Timon Date: Sun, 10 Nov 2024 00:03:34 +0100 Subject: [PATCH 38/38] feat: expose list order in TabListEntry (#1451) * feat: expose list order in TabListEntry * fix: address comment (from github) * fix: address another comment (from github) --- .../api/proxy/player/TabList.java | 21 +++++++++- .../api/proxy/player/TabListEntry.java | 39 ++++++++++++++++++- .../proxy/tablist/KeyedVelocityTabList.java | 9 ++++- .../proxy/tablist/VelocityTabList.java | 23 +++++++++-- .../proxy/tablist/VelocityTabListEntry.java | 25 +++++++++++- 5 files changed, 107 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java b/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java index 4d03d3a87..feffd76e2 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/player/TabList.java @@ -168,6 +168,25 @@ public interface TabList { * @deprecated Internal usage. Use {@link TabListEntry.Builder} instead. */ @Deprecated + default TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, + int gameMode, @Nullable ChatSession chatSession, boolean listed) { + return buildEntry(profile, displayName, latency, gameMode, chatSession, listed, 0); + } + + /** + * Represents an entry in a {@link Player}'s tab list. + * + * @param profile the profile + * @param displayName the display name + * @param latency the latency + * @param gameMode the game mode + * @param chatSession the chat session + * @param listed the visible status of entry + * @param listOrder the order/priority of entry in the tab list + * @return the entry + * @deprecated Internal usage. Use {@link TabListEntry.Builder} instead. + */ + @Deprecated TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, - int gameMode, @Nullable ChatSession chatSession, boolean listed); + int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder); } diff --git a/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java b/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java index 401d6a8da..b5140776e 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/player/TabListEntry.java @@ -139,6 +139,27 @@ public interface TabListEntry extends KeyIdentifiable { return this; } + /** + * Returns the order/priority of this entry in the tab list. + * + * @return order of this entry + * @sinceMinecraft 1.21.2 + */ + default int getListOrder() { + return 0; + } + + /** + * Sets the order/priority of this entry in the tab list. + * + * @param order order of this entry + * @return {@code this}, for chaining + * @sinceMinecraft 1.21.2 + */ + default TabListEntry setListOrder(int order) { + return this; + } + /** * Returns a {@link Builder} to create a {@link TabListEntry}. * @@ -161,6 +182,7 @@ public interface TabListEntry extends KeyIdentifiable { private int latency = 0; private int gameMode = 0; private boolean listed = true; + private int listOrder = 0; private @Nullable ChatSession chatSession; @@ -243,7 +265,7 @@ public interface TabListEntry extends KeyIdentifiable { } /** - * Sets wether this entry should be visible. + * Sets whether this entry should be visible. * * @param listed to set * @return ${code this}, for chaining @@ -254,6 +276,19 @@ public interface TabListEntry extends KeyIdentifiable { return this; } + /** + * Sets the order/priority of this entry in the tab list. + * + * @param order to set + * @return ${code this}, for chaining + * @sinceMinecraft 1.21.2 + * @see TabListEntry#getListOrder() + */ + public Builder listOrder(int order) { + this.listOrder = order; + return this; + } + /** * Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}. * @@ -266,7 +301,7 @@ public interface TabListEntry extends KeyIdentifiable { if (profile == null) { throw new IllegalStateException("The GameProfile must be set when building a TabListEntry"); } - return tabList.buildEntry(profile, displayName, latency, gameMode, chatSession, listed); + return tabList.buildEntry(profile, displayName, latency, gameMode, chatSession, listed, listOrder); } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java index 61967fbc2..daaa9b0a6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/KeyedVelocityTabList.java @@ -159,12 +159,17 @@ public class KeyedVelocityTabList implements InternalTabList { @Override public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, - int gameMode, - @Nullable ChatSession chatSession, boolean listed) { + int gameMode, @Nullable ChatSession chatSession, boolean listed) { return new KeyedVelocityTabListEntry(this, profile, displayName, latency, gameMode, chatSession == null ? null : chatSession.getIdentifiedKey()); } + @Override + public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, + int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder) { + return buildEntry(profile, displayName, latency, gameMode, chatSession, listed); + } + @Override public void processLegacy(LegacyPlayerListItemPacket packet) { // Packets are already forwarded on, so no need to do that here diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java index d6b4143ce..0990119c6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabList.java @@ -19,6 +19,7 @@ package com.velocitypowered.proxy.tablist; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; +import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.player.ChatSession; import com.velocitypowered.api.proxy.player.TabListEntry; @@ -89,7 +90,7 @@ public class VelocityTabList implements InternalTabList { } else { entry = new VelocityTabListEntry(this, entry1.getProfile(), entry1.getDisplayNameComponent().orElse(null), - entry1.getLatency(), entry1.getGameMode(), entry1.getChatSession(), entry1.isListed()); + entry1.getLatency(), entry1.getGameMode(), entry1.getChatSession(), entry1.isListed(), entry1.getListOrder()); } EnumSet actions = EnumSet @@ -128,6 +129,11 @@ public class VelocityTabList implements InternalTabList { actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LISTED); playerInfoEntry.setListed(entry.isListed()); } + if (!Objects.equals(previousEntry.getListOrder(), entry.getListOrder()) + && player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER); + playerInfoEntry.setListOrder(entry.getListOrder()); + } if (!Objects.equals(previousEntry.getChatSession(), entry.getChatSession())) { ChatSession from = entry.getChatSession(); if (from != null) { @@ -162,6 +168,11 @@ public class VelocityTabList implements InternalTabList { } playerInfoEntry.setLatency(entry.getLatency()); playerInfoEntry.setListed(entry.isListed()); + if (entry.getListOrder() != 0 + && player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER); + playerInfoEntry.setListOrder(entry.getListOrder()); + } } return entry; }); @@ -207,9 +218,9 @@ public class VelocityTabList implements InternalTabList { @Override public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, int gameMode, - @Nullable ChatSession chatSession, boolean listed) { + @Nullable ChatSession chatSession, boolean listed, int listOrder) { return new VelocityTabListEntry(this, profile, displayName, latency, gameMode, chatSession, - listed); + listed, listOrder); } @Override @@ -246,7 +257,8 @@ public class VelocityTabList implements InternalTabList { 0, -1, null, - false + false, + 0 ) ); } else { @@ -274,6 +286,9 @@ public class VelocityTabList implements InternalTabList { if (actions.contains(UpsertPlayerInfoPacket.Action.UPDATE_LISTED)) { currentEntry.setListedWithoutUpdate(entry.isListed()); } + if (actions.contains(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER)) { + currentEntry.setListOrderWithoutUpdate(entry.getListOrder()); + } } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java index 4e036504a..352d62716 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListEntry.java @@ -17,6 +17,7 @@ package com.velocitypowered.proxy.tablist; +import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.player.ChatSession; import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabListEntry; @@ -38,6 +39,7 @@ public class VelocityTabListEntry implements TabListEntry { private int latency; private int gameMode; private boolean listed; + private int listOrder; private @Nullable ChatSession session; /** @@ -45,7 +47,7 @@ public class VelocityTabListEntry implements TabListEntry { */ public VelocityTabListEntry(VelocityTabList tabList, GameProfile profile, Component displayName, int latency, - int gameMode, @Nullable ChatSession session, boolean listed) { + int gameMode, @Nullable ChatSession session, boolean listed, int listOrder) { this.tabList = tabList; this.profile = profile; this.displayName = displayName; @@ -53,6 +55,7 @@ public class VelocityTabListEntry implements TabListEntry { this.gameMode = gameMode; this.session = session; this.listed = listed; + this.listOrder = listOrder; } @Override @@ -150,4 +153,24 @@ public class VelocityTabListEntry implements TabListEntry { void setListedWithoutUpdate(boolean listed) { this.listed = listed; } + + @Override + public int getListOrder() { + return listOrder; + } + + @Override + public VelocityTabListEntry setListOrder(int listOrder) { + this.listOrder = listOrder; + if (tabList.getPlayer().getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_2)) { + UpsertPlayerInfoPacket.Entry upsertEntry = this.tabList.createRawEntry(this); + upsertEntry.setListOrder(listOrder); + tabList.emitActionRaw(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER, upsertEntry); + } + return this; + } + + void setListOrderWithoutUpdate(int listOrder) { + this.listOrder = listOrder; + } } \ No newline at end of file