AOLGYq)sa(w9^3-f}NHy=GR4v%t2YZly3m1G@5y`xBh_HGrD%f
z>;|Ty?9FiJAc&UVD(StT4I`
zfVQwxhE9bXE6r2mKO8Ag7{L^jCyqQb0QqKDPE=RAgqn8q1O^>(z7h5kE(6va%QqRZ
zkIOmp(})rLSS(2{=C12e&@!W2=Jel-^_R``0xHO^+t!(oXbcv5yhD4g*$t_F)_5Dl
zSVCgesW%;DtYPCFs{G;GX_o?1J3;QQPPv)rWw;>}
zJ&KwnUqwNXloNXlK_+pNDfI~hON#SokVJb&ilg8d7^NWo2ZQymCqQMnjfi>ePibjr
z-Z@q!?RGN$Mj}Nk){X_vaj6?Mj$>ACR*z|6MsXy3VZ^PFn@yHkPo(>m(iWepn8SC@
z>D2;R4m+gDRZ=SIX!b+CP(qE=JDIUkn=D$aUu+Ihn9-+k1LS3PreQg0N5eWIG@x${nC3v^7caS>1!PKNAY9J
z#}E}Q9w#SP>(GY7Hbj&z4$Li6o5taBO|4+F`yS9zq*LJ<38wy4I>HA9(&GYrk4dLajKGww))BWli6Ln1A^Lda@N~p+snkb9C
z@OthI+<##vp8!HVQT4Wk(=@zQ{OvZ$EKWS73+JHb)eYLGD-cqi6^|vd$<+IHuc?Nq
zW7JertT~3))4?J|28n$I@nAD0c1%9C&IVhEZX~mUsf{efyS(XNG%ch;!N~d7S(Ri7
zb&=BuON95aVA&kLn6&MVU|x}xPMp7xwWxNU1wS+F6#y}1@^wQZB*(&ecT?RnQcI}Y
z2*z!^!D?gDUhc@;M^OpLs4mq>C&p{}OWVv<)S9KMars@0JQ{c_ScGsFo3BJ)Irg++
zAWwypJdTO-_{Uh8m(Z!3KL7K{ZZzKHj;{M8I$mV>k
znTM?sa0);^=X^cglL`uC+^J)M7nEa$w=VwFULg~%DJllw+7dJAj3{qnP5i3@wr7%y
zjXp?Wl2%Th=my&3u?Q$RV6N5tzKMSPTsc#J+-cDDp~qFB6bL2C8AS7Y3PKtVhdhl)
zIaLqH5+OnWPWSt(lQCgkN8lczc-V%_iZ{>#1%Z$N*>lu#S;0MZ$T2Y8Kg!U;hAZj>
z6S#%$DQ_`Ic%Zr@?}GgjRXg@qTj^17n`65oJ@Wj0u1X8&+UVd|Xs?J+i_^GZ94m6=
zUc96~Q`OJvlKB_Lr15*Yw_PUPEr?f?H&00b^-W%26mD)(n(rGGNfK9~2h=C>p-7BZ
zFd&*&Msdu{w~(eyFOglwCPH^Rb}O(N7LtS+nnEwDx*pGD?|&9Si~M43a+*L(b0$5A
zv`T`(G3xO;I_sx;FwTP21ZlfDpz
zOo?}Vlgf~fo{YWm@n_JyD*frOg{XsvBA~|Tn4V6hu>Gd>89-rblfVJUaGvj6X%NZ}
z$tFF9sx=4_$*c~G`9iPLGh@=sV+O{D2-t*K@J7H=`V+oVt}8?04WwU3h1BgS!f%1P
zFak-T#7`TtLcR=Yz>g0R!ZQrH!YiZOQN=_V-UyncN1Rc18?KY?#O`v#JK+pq0K$~H
z3D@v9DZF42R)b9#BBX{^$DOMlJ!g)Gc
za{o-1e%F6NvgKq9tC8pV+9S$;9*zNv{J*)n&dmf~anP1)4~N%~h#c(=B#3*KgzhCKhFdgDoWi2IDog{RVyzK|Y`rCUs3T~pJMmdZJy4?b
z&s5G=zhf**(t7Y^oC_mcTsE-{^}wiaoUu&?kojLKs>SJPxjcP>{a5CbXCx92AcBE)
zHtqP}LjZ{W>PH?Tu(E0X=%{PBMW@F_?#7b!^q`<-5$ur+-q6
z{dn=(^UZw6*3-XM_(=@<1_*i&XM4=0t5u!gm6
z{UlmNGPKgO_;e;q9|#esq~Sq`<}%d{+sRmhvsA{5i*91=tub>OZZ%)xUA#4q$dDyy
z1`w4%?OPLg3JeZb#cqSMO?*Xn%|-FCcuH2i2fn_{IFusub6;NQdN|7TD1N?%E8*g?
z$apAt@cEe!I%jB=*q$p_3=t_5R0ph%{qaq+QDg!c99Y!Xa!&oDZOeis_ot)gNXr{l
zdY$|So2Qed2Y7KMNBrS^E169kG%h<+z{Z_p_;shB!uY)>yAVcK=&!bg`lVg)4T1|7
z0}7FpfydVH4F87K@c!nEG+WGKm{Ouo)Slpl;#qcEIQ0zdMfLA#;dBxYw;p;KoVv6|
z3_D5&7rJdG12CnDSvZUW?$UC6^UVSW^|vw|o-_4bz)(w5(3AiVhpeT(|=f#x_}E?s#qHZF#xA6AF_ujl$G
z-jHD%q(d2}v2PhXx&6YWps~m(^+RXl91Q#xRRJBhjKl$FG4bk);|ag;ieUZ&!Ii3$
z(iGz1+0m7#g5>ASldBbNZL=ZHh=tmmJt$!71;
zIML2GhEz1pg@1rQN(M^_691wAGkJ@Pga_05WuQ6!
zG5RkGY2^`@(H~pp7&Ga+Pwh3L!Njj!-rc;^bTIfo5hP@H##1X8xUZJckrx>id`bAd3QUx9GuomqBYZ!uN1-&o
zvTxC?;p8vL67&fW8fw(YOqt>L@bdLrEF*3OgYe$4n4{
zEB40LiU#6-0@5jdN`0w}N0qi@c0~oT2FP
z)LNk&a82my?jv(tQpiMi$TK_L@lub#lsM$R{Dk?Ya@%%%huZkct~tSWM714c!45k}-ZLVA-bVM`>|_ZBbW_m-7|
z3U%xrAhi}n?T(2F{_n4EZ10inkIFl#y09?7$uwBoJgqY8vylwev)fDOn;>0R!aEnV
zBz%j0Mqpx~EZU3q@%+oV7;}|vt7$~ou@faEIq{p?FY$XXg&6*K)b_LP=}gi9`Bij3
zN`zEo|B6*|-;>S`rNa^BKRDbDAk>X#MsR`EvL>6bqU@SaDDs
z8>bu@3YdRaWs*Te@G-UHjU%F~kTHw5(0PVJ+pwh#ha2u;DB+UMo@A5UYIl#5rtBV-
zGX_hIpw}3C@H*Us(Cc-d#-gNrG#w$(9+S=GxO>3SR`SE2fHZ2KrDc#_C^$jI>Y}#;
zMwY=R6@+dWi~0RXw(c@3GZ&%~9K(q&ee0Zw;pwL`E_tZak-#8^_b)Dpyi73^he?xV
zXJ08&wh5-M&}qy4f7!D&=E)puDD(Nmg1d_(j`4LvxM5x_huNg-pGG%9rYqO6mImyJ@}*3Y>^3OvcnTG%EV1)
zq_Ap?Z!Iw__7#D=pOWnQN$gB!Mr0!9yx|g<4icJh{cFOu3B8}&RiYm+Mb;VEK``LK
zL(NcpcTiGieOIssSjr?ob}^``nNf&UcJhXyncO9m{6gD$kqSD`S69(aF8dkWz5>!9
zBLe4Sib7Hs2x_L2Ls6Ish$MGVKrGt5+_2zCyP1byaCg3upo+-I}R4&$m)8
zQ7|jc1Z^VWggpuQj*cP;>Zo9LS!VSzrqmZczaf;u`d0J(f%Z9r%An@s!e>n9%y=n!IZ_tVGu{Jmsbp}Fk%HJIU?a+-~bjfLTuH|JExA8EROowzr
zqW9{YyZhR0a4clRK>1I4Ncx&WER~{iE;F^$T7K%X@3PGOA%6#Z%p3TS^&M;Dnjw@i
z^o!$9nhcsmcHcY4?4j9+ofL_CWsZ4Hcch(rjsGfGD(nsH>w}^ERqGnz%iGj0j{g}h
z7wMkJ-2Z2~eS>2!i}0~B63i;>SyFJU2+>VCS^AxaDOx%g6-t0eM^P<3+*z`ztvOqrG3)$K?&
z_Y0wbWID47@cU`E1A6A&!`aZk0ZE@z-h#l1NqX2#`$Uev2gepW`rf8*!=rD5&;Jb{
zl08rU>dPo=K%-1Ao1~G-@4ve~y5#9E8x;TE0k5d^TC(=Zc>mwjW^c=+U-<9}b0ku~}gj
z3sbW>R2M6DR!g#NUP;nxo>)@7*=RP{U18SDop6b2&PHce^&h97@xx3t+VK+!keE#}
z;(Uf&89as9k8{$nkLbuB!-d7TP`_VJpL^Xs8OKB~ri$YUbW8fch64}7|0EWoT(TRj{
z*GT<7Y<7DsrCi79ZsM)z#c(!nNOGySOCkY1fAuQOq12&iUVC!a`#O;dBLf=d?&4*B
zI~LgAO7E0qxK(uRTM;IgJ}+z^gD+bi-6I!3x{r9`l~%8TRP%UE0V8E*Sz>Nl1NVG<<7(wDHZ+HcOkQm$O&k+vyx)y)x{Pz!U8hS$*m
zByc0h6BUI*BOpuL==P+H|Hx%`>7!W+1H!l9vi&)`V
zyn2o9{z=lc+VX*!Vh~SF=)L}Z40XeG>LF6cP^b+R$NxSeUqbK^Q*UTalKzP8X%{9@RSCXm_NhF>{=S2
zi}ezam_^P`S!!-cyEW9y7DBbK93roz@Raccy*v}?mKXScU9E_4g;hBU7}zSofAFda
zKYEe?{{I54
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b82aa23a..e2847c82 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 1aa94a42..5bdb31f8 100755
--- a/gradlew
+++ b/gradlew
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -200,7 +203,7 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
@@ -246,4 +249,4 @@ eval "set -- $(
tr '\n' ' '
)" '"$@"'
-exec "$JAVACMD" "$@"
+exec "$JAVACMD" "$@"
\ No newline at end of file
diff --git a/gradlew.bat b/gradlew.bat
index 7101f8e4..6042cb3e 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -34,7 +36,7 @@ set APP_HOME=%DIRNAME%
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -89,4 +91,4 @@ exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
-:omega
+:omega
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 594ed9ff..40384465 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
- id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
+ id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
}
rootProject.name = "velocity"
From af97ffffa586a7457312286c3270ed77bd1e49a7 Mon Sep 17 00:00:00 2001
From: Andrew Steinborn
Date: Sat, 21 Dec 2024 03:45:17 -0500
Subject: [PATCH 04/34] A few small code cleanups for cryptography
* Remove some unused cryptographic code
* Add some notes about how Minecraft's cryptography choices have not quite survived the test of time
---
native/src/main/c/jni_cipher_macos.c | 9 ++++
native/src/main/c/jni_cipher_openssl.c | 9 ++++
.../encryption/JavaVelocityCipher.java | 9 ++++
.../velocitypowered/proxy/VelocityServer.java | 9 ++++
.../proxy/crypto/EncryptionUtils.java | 54 -------------------
5 files changed, 36 insertions(+), 54 deletions(-)
diff --git a/native/src/main/c/jni_cipher_macos.c b/native/src/main/c/jni_cipher_macos.c
index aa7d1aba..d658eb5d 100644
--- a/native/src/main/c/jni_cipher_macos.c
+++ b/native/src/main/c/jni_cipher_macos.c
@@ -26,6 +26,15 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
return 0;
}
+ // But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
+ // the IV defeats the entire point - we might as well just initialize it to all zeroes.
+ //
+ // You can blame Mojang. For the record, we also don't consider the Minecraft protocol
+ // encryption scheme to be secure, and it has reached the point where any serious cryptographic
+ // protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
+ // most serious.
+ //
+ // If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
CCCryptorRef cryptor = NULL;
CCCryptorStatus result = CCCryptorCreateWithMode(encrypt ? kCCEncrypt : kCCDecrypt,
kCCModeCFB8,
diff --git a/native/src/main/c/jni_cipher_openssl.c b/native/src/main/c/jni_cipher_openssl.c
index 83515be5..06f09e11 100644
--- a/native/src/main/c/jni_cipher_openssl.c
+++ b/native/src/main/c/jni_cipher_openssl.c
@@ -32,6 +32,15 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
return 0;
}
+ // But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
+ // the IV defeats the entire point - we might as well just initialize it to all zeroes.
+ //
+ // You can blame Mojang. For the record, we also don't consider the Minecraft protocol
+ // encryption scheme to be secure, and it has reached the point where any serious cryptographic
+ // protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
+ // most serious.
+ //
+ // If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes,
encrypt);
if (result != 1) {
diff --git a/native/src/main/java/com/velocitypowered/natives/encryption/JavaVelocityCipher.java b/native/src/main/java/com/velocitypowered/natives/encryption/JavaVelocityCipher.java
index 58a7da58..00f5a1bf 100644
--- a/native/src/main/java/com/velocitypowered/natives/encryption/JavaVelocityCipher.java
+++ b/native/src/main/java/com/velocitypowered/natives/encryption/JavaVelocityCipher.java
@@ -48,6 +48,15 @@ public class JavaVelocityCipher implements VelocityCipher {
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
+ // But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
+ // the IV defeats the entire point - we might as well just initialize it to all zeroes.
+ //
+ // You can blame Mojang. For the record, we also don't consider the Minecraft protocol
+ // encryption scheme to be secure, and it has reached the point where any serious cryptographic
+ // protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
+ // most serious.
+ //
+ // If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
this.cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
new IvParameterSpec(key.getEncoded()));
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
index 8b5d4d3d..45c228b3 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
@@ -236,6 +236,15 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
registerTranslations();
+ // Yes, you're reading that correctly. We're generating a 1024-bit RSA keypair. Sounds
+ // dangerous, right? We're well within the realm of factoring such a key...
+ //
+ // You can blame Mojang. For the record, we also don't consider the Minecraft protocol
+ // encryption scheme to be secure, and it has reached the point where any serious cryptographic
+ // protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
+ // most serious.
+ //
+ // If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
cm.logChannelInformation();
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/crypto/EncryptionUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/crypto/EncryptionUtils.java
index 283c8d49..04107b39 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/crypto/EncryptionUtils.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/crypto/EncryptionUtils.java
@@ -23,8 +23,6 @@ import com.velocitypowered.proxy.util.except.QuietDecoderException;
import it.unimi.dsi.fastutil.Pair;
import java.io.IOException;
import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Key;
@@ -111,42 +109,6 @@ public enum EncryptionUtils {
}
}
- /**
- * Generates a signature for input data.
- *
- * @param algorithm the signature algorithm
- * @param base the private key to sign with
- * @param toSign the byte array(s) of data to sign
- * @return the generated signature
- */
- public static byte[] generateSignature(String algorithm, PrivateKey base, byte[]... toSign) {
- Preconditions.checkArgument(toSign.length > 0);
- try {
- Signature construct = Signature.getInstance(algorithm);
- construct.initSign(base);
- for (byte[] bytes : toSign) {
- construct.update(bytes);
- }
- return construct.sign();
- } catch (GeneralSecurityException e) {
- throw new IllegalArgumentException("Invalid signature parameters");
- }
- }
-
- /**
- * Encodes a long array as Big-endian byte array.
- *
- * @param bits the long (array) of numbers to encode
- * @return the encoded bytes
- */
- public static byte[] longToBigEndianByteArray(long... bits) {
- ByteBuffer ret = ByteBuffer.allocate(8 * bits.length).order(ByteOrder.BIG_ENDIAN);
- for (long put : bits) {
- ret.putLong(put);
- }
- return ret.array();
- }
-
public static String encodeUrlEncoded(byte[] data) {
return MIME_SPECIAL_ENCODER.encodeToString(data);
}
@@ -155,22 +117,6 @@ public enum EncryptionUtils {
return Base64.getMimeDecoder().decode(toParse);
}
- /**
- * Parse a cer-encoded RSA key into its key bytes.
- *
- * @param toParse the cer-encoded key String
- * @param descriptors the type of key
- * @return the parsed key bytes
- */
- public static byte[] parsePemEncoded(String toParse, Pair descriptors) {
- int startIdx = toParse.indexOf(descriptors.first());
- Preconditions.checkArgument(startIdx >= 0);
- int firstLen = descriptors.first().length();
- int endIdx = toParse.indexOf(descriptors.second(), firstLen + startIdx) + 1;
- Preconditions.checkArgument(endIdx > 0);
- return decodeUrlEncoded(toParse.substring(startIdx + firstLen, endIdx));
- }
-
/**
* Encodes an RSA key as String cer format.
*
From 1db8c8c6ab14a2309e936df917333fd4359621a0 Mon Sep 17 00:00:00 2001
From: Aaron <71191102+RealBauHD@users.noreply.github.com>
Date: Mon, 23 Dec 2024 03:59:13 +0100
Subject: [PATCH 05/34] Bump adventure to 4.18.0 (#1481)
---
gradle/libs.versions.toml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7557ed42..e3859eb0 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,8 +11,8 @@ shadow = "io.github.goooler.shadow:8.1.5"
spotless = "com.diffplug.spotless:6.25.0"
[libraries]
-adventure-bom = "net.kyori:adventure-bom:4.17.0"
-adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.17.0"
+adventure-bom = "net.kyori:adventure-bom:4.18.0"
+adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.18.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"
From 00b68859fffa31bc5bd66338e339131ddbb50ff5 Mon Sep 17 00:00:00 2001
From: TangJin <55261514+tangjin0418@users.noreply.github.com>
Date: Thu, 2 Jan 2025 17:53:53 +0800
Subject: [PATCH 06/34] Add "GetPlayerServer". (#1484)
---
.../backend/BungeeCordMessageResponder.java | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java
index b047d186..6161c4c2 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java
@@ -301,6 +301,21 @@ public class BungeeCordMessageResponder {
}
}
+ private void processGetPlayerServer(ByteBufDataInput in) {
+ proxy.getPlayer(in.readUTF()).ifPresent(player -> {
+ player.getCurrentServer().ifPresent(server -> {
+ ByteBuf buf = Unpooled.buffer();
+ ByteBufDataOutput out = new ByteBufDataOutput(buf);
+
+ out.writeUTF("GetPlayerServer");
+ out.writeUTF(player.getUsername());
+ out.writeUTF(server.getServerInfo().getName());
+
+ sendResponseOnConnection(buf);
+ });
+ });
+ }
+
static String getBungeeCordChannel(ProtocolVersion version) {
return version.noLessThan(ProtocolVersion.MINECRAFT_1_13) ? MODERN_CHANNEL.getId()
: LEGACY_CHANNEL.getId();
@@ -331,6 +346,9 @@ public class BungeeCordMessageResponder {
ByteBufDataInput in = new ByteBufDataInput(message.content());
String subChannel = in.readUTF();
switch (subChannel) {
+ case "GetPlayerServer":
+ this.processGetPlayerServer(in);
+ break;
case "ForwardToPlayer":
this.processForwardToPlayer(in);
break;
From c0fdf20224d374927c49a628c3636f44b5245627 Mon Sep 17 00:00:00 2001
From: Gero
Date: Wed, 15 Jan 2025 02:44:20 +0100
Subject: [PATCH 07/34] Add InboundConnection#getHandshakeIntent (#1493)
* Add InboundConnection#getHandshakeIntent
---
.../velocitypowered/api/proxy/InboundConnection.java | 8 ++++++++
.../proxy/connection/client/AuthSessionHandler.java | 2 +-
.../proxy/connection/client/ConnectedPlayer.java | 10 +++++++++-
.../connection/client/HandshakeSessionHandler.java | 5 +++++
.../connection/client/InitialInboundConnection.java | 6 ++++++
.../connection/client/LoginInboundConnection.java | 6 ++++++
6 files changed, 35 insertions(+), 2 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 1b16e9e1..884cd411 100644
--- a/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java
+++ b/api/src/main/java/com/velocitypowered/api/proxy/InboundConnection.java
@@ -7,6 +7,7 @@
package com.velocitypowered.api.proxy;
+import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.network.ProtocolVersion;
import java.net.InetSocketAddress;
@@ -60,4 +61,11 @@ public interface InboundConnection {
* @return the protocol state of the connection
*/
ProtocolState getProtocolState();
+
+ /**
+ * Returns the initial intent for the connection.
+ *
+ * @return the intent of the connection
+ */
+ HandshakeIntent getHandshakeIntent();
}
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 6165a846..257429e2 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
@@ -97,7 +97,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), inbound.getRawVirtualHost().orElse(null), onlineMode,
- inbound.getIdentifiedKey());
+ inbound.getHandshakeIntent(), inbound.getIdentifiedKey());
this.connectedPlayer = player;
if (!server.canRegisterConnection(player)) {
player.disconnect0(
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 46c3d63d..a7717055 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
@@ -38,6 +38,7 @@ import com.velocitypowered.api.event.player.PlayerModInfoEvent;
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
+import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.permission.PermissionFunction;
@@ -156,6 +157,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
private final MinecraftConnection connection;
private final @Nullable InetSocketAddress virtualHost;
private final @Nullable String rawVirtualHost;
+ private final HandshakeIntent handshakeIntent;
private GameProfile profile;
private PermissionFunction permissionFunction;
private int tryIndex = 0;
@@ -193,12 +195,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection,
@Nullable InetSocketAddress virtualHost, @Nullable String rawVirtualHost, boolean onlineMode,
- @Nullable IdentifiedKey playerKey) {
+ HandshakeIntent handshakeIntent, @Nullable IdentifiedKey playerKey) {
this.server = server;
this.profile = profile;
this.connection = connection;
this.virtualHost = virtualHost;
this.rawVirtualHost = rawVirtualHost;
+ this.handshakeIntent = handshakeIntent;
this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED;
this.connectionPhase = connection.getType().getInitialClientPhase();
this.onlineMode = onlineMode;
@@ -1335,6 +1338,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
return connection.getState().toProtocolState();
}
+ @Override
+ public HandshakeIntent getHandshakeIntent() {
+ return handshakeIntent;
+ }
+
private final class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {
private final RegisteredServer toConnect;
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 f31e0812..80260d2b 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
@@ -277,5 +277,10 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
public ProtocolState getProtocolState() {
return connection.getState().toProtocolState();
}
+
+ @Override
+ public HandshakeIntent getHandshakeIntent() {
+ return HandshakeIntent.STATUS;
+ }
}
}
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 7b918374..35368844 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
@@ -17,6 +17,7 @@
package com.velocitypowered.proxy.connection.client;
+import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.InboundConnection;
@@ -98,6 +99,11 @@ public final class InitialInboundConnection implements VelocityInboundConnection
return connection.getState().toProtocolState();
}
+ @Override
+ public HandshakeIntent getHandshakeIntent() {
+ return handshake.getIntent();
+ }
+
/**
* Disconnects the connection from the server.
*
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 18596e4e..9922c509 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
@@ -17,6 +17,7 @@
package com.velocitypowered.proxy.connection.client;
+import com.velocitypowered.api.network.HandshakeIntent;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.LoginPhaseConnection;
@@ -177,4 +178,9 @@ public class LoginInboundConnection implements LoginPhaseConnection, KeyIdentifi
public ProtocolState getProtocolState() {
return delegate.getProtocolState();
}
+
+ @Override
+ public HandshakeIntent getHandshakeIntent() {
+ return delegate.getHandshakeIntent();
+ }
}
From 71feb11b2ed143100fc5aeda771f28339fae354e Mon Sep 17 00:00:00 2001
From: Shane Freeder
Date: Fri, 17 Jan 2025 15:10:06 +0000
Subject: [PATCH 08/34] Fix fallback compression handler
---
.../natives/compression/JavaVelocityCompressor.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java b/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java
index 7182b352..b6c3aab4 100644
--- a/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java
+++ b/native/src/main/java/com/velocitypowered/natives/compression/JavaVelocityCompressor.java
@@ -57,7 +57,8 @@ public class JavaVelocityCompressor implements VelocityCompressor {
inflater.setInput(source.nioBuffer());
try {
- while (!inflater.finished() && inflater.getBytesWritten() < uncompressedSize) {
+ final int readable = source.readableBytes();
+ while (!inflater.finished() && inflater.getBytesRead() < readable) {
if (!destination.isWritable()) {
destination.ensureWritable(ZLIB_BUFFER_SIZE);
}
From 7392cd6574fcc5b1496fe4c3ac2b6fa7c8e993d7 Mon Sep 17 00:00:00 2001
From: Henri Schubin
Date: Tue, 21 Jan 2025 19:37:20 +0200
Subject: [PATCH 09/34] Fix nonsensical deprecation for specifying listener
priority (#1491)
* Fix nonsensical deprecation for specifying listener priority
* Fix checkstyle error
---
.../java/com/velocitypowered/api/event/PostOrder.java | 10 +++++++++-
.../java/com/velocitypowered/api/event/Subscribe.java | 5 +----
.../proxy/event/VelocityEventManager.java | 3 ++-
3 files changed, 12 insertions(+), 6 deletions(-)
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 0d52ed2c..98fa2e86 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,14 @@ package com.velocitypowered.api.event;
*/
public enum PostOrder {
- FIRST, EARLY, NORMAL, LATE, LAST, CUSTOM
+ FIRST, EARLY, NORMAL, LATE, LAST,
+
+ /**
+ * Previously used to specify that {@link Subscribe#priority()} should be used.
+ *
+ * @deprecated No longer required, you only need to specify {@link Subscribe#priority()}.
+ */
+ @Deprecated
+ 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 abb96c94..e0cdeb86 100644
--- a/api/src/main/java/com/velocitypowered/api/event/Subscribe.java
+++ b/api/src/main/java/com/velocitypowered/api/event/Subscribe.java
@@ -32,12 +32,9 @@ public @interface Subscribe {
* 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.
*
- * Note that due to compatibility constraints, you must specify {@link PostOrder#CUSTOM}
- * in order to use this field.
- *
* @return the priority
*/
- short priority() default Short.MIN_VALUE;
+ short priority() default 0;
/**
* Whether the handler must be called asynchronously. By default, all event handlers are called
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 4c48068c..9d54b2d0 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/event/VelocityEventManager.java
@@ -350,8 +350,9 @@ public class VelocityEventManager implements EventManager {
asyncType = AsyncType.ALWAYS;
}
+ // The default value of 0 will fall back to PostOrder, the default PostOrder (NORMAL) is also 0
final short order;
- if (subscribe.order() == PostOrder.CUSTOM) {
+ if (subscribe.priority() != 0) {
order = subscribe.priority();
} else {
order = (short) POST_ORDER_MAP.get(subscribe.order());
From 371e68607695b48117e1cd57d19c67e40384c33a Mon Sep 17 00:00:00 2001
From: Shane Freeder
Date: Thu, 23 Jan 2025 19:29:37 +0000
Subject: [PATCH 10/34] properly apply vanilla cap to chat packets
---
.../proxy/protocol/packet/chat/legacy/LegacyChatPacket.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java
index 4239489f..ccefdd13 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java
@@ -91,7 +91,7 @@ public class LegacyChatPacket implements MinecraftPacket {
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
- message = ProtocolUtils.readString(buf);
+ message = ProtocolUtils.readString(buf, 256);
if (direction == ProtocolUtils.Direction.CLIENTBOUND
&& version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
type = buf.readByte();
From 6995f415d3ca008f12af4625b83db5915c06c625 Mon Sep 17 00:00:00 2001
From: kyngs <38181667+kyngs@users.noreply.github.com>
Date: Sun, 26 Jan 2025 13:49:35 +0100
Subject: [PATCH 11/34] Expose shutdownInProgress to the API. (#1485)
Co-authored-by: kyngs
---
.../java/com/velocitypowered/api/proxy/ProxyServer.java | 7 +++++++
.../java/com/velocitypowered/proxy/VelocityServer.java | 5 +++++
2 files changed, 12 insertions(+)
diff --git a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java
index f043b0c6..bbf999b2 100644
--- a/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java
+++ b/api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java
@@ -41,6 +41,13 @@ public interface ProxyServer extends Audience {
*/
void shutdown();
+ /**
+ * Returns whether the proxy is currently shutting down.
+ *
+ * @return {@code true} if the proxy is shutting down, {@code false} otherwise
+ */
+ boolean isShuttingDown();
+
/**
* Closes all listening endpoints for this server.
* This includes the main minecraft listener and query channel.
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
index 45c228b3..abf2cf00 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
@@ -803,6 +803,11 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
public VelocityChannelRegistrar getChannelRegistrar() {
return channelRegistrar;
}
+
+ @Override
+ public boolean isShuttingDown() {
+ return shutdownInProgress.get();
+ }
@Override
public InetSocketAddress getBoundAddress() {
From 876b9c36012d6ed91ca7b7c0a747f6c2809ba1b3 Mon Sep 17 00:00:00 2001
From: Isaac - The456
Date: Mon, 27 Jan 2025 04:03:37 +0000
Subject: [PATCH 12/34] Fix ShutdownCommand message styling (#1427)
* Fix ShutdownCommand message
* Fix checkstyle violation.
---
.../command/builtin/ShutdownCommand.java | 25 ++++++++++++++-----
1 file changed, 19 insertions(+), 6 deletions(-)
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 402a0ce8..b60ead91 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
@@ -17,6 +17,7 @@
package com.velocitypowered.proxy.command.builtin;
+import com.google.gson.JsonSyntaxException;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@@ -25,8 +26,9 @@ 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.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
-import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
/**
* Shuts down the proxy.
@@ -53,11 +55,22 @@ public final class ShutdownCommand {
StringArgumentType.greedyString())
.executes(context -> {
String reason = context.getArgument("reason", String.class);
- server.shutdown(true, MiniMessage.miniMessage().deserialize(
- MiniMessage.miniMessage().serialize(
- LegacyComponentSerializer.legacy('&').deserialize(reason)
- )
- ));
+ Component reasonComponent = null;
+
+ if (reason.startsWith("{") || reason.startsWith("[") || reason.startsWith("\"")) {
+ try {
+ reasonComponent = GsonComponentSerializer.gson()
+ .deserializeOrNull(reason);
+ } catch (JsonSyntaxException expected) {
+
+ }
+ }
+
+ if (reasonComponent == null) {
+ reasonComponent = MiniMessage.miniMessage().deserialize(reason);
+ }
+
+ server.shutdown(true, reasonComponent);
return Command.SINGLE_SUCCESS;
})
).build());
From 0e84b57e5387de45ce0021870b61045c4f5b43e5 Mon Sep 17 00:00:00 2001
From: scratchyone
Date: Fri, 31 Jan 2025 07:04:52 -0500
Subject: [PATCH 13/34] Add Virtualhost support for server list pings (#1265)
* Add virtualhost support for server list pings
* Add virtualhost support to ping public API
* Applied suggestions
* fixed checkstyle
* Added nullable annotation
---------
Co-authored-by: Adrian
---
.../api/proxy/server/PingOptions.java | 29 +++++++++++++++++--
.../util/ServerListPingHandler.java | 6 ++--
.../proxy/server/PingSessionHandler.java | 7 +++--
.../server/VelocityRegisteredServer.java | 2 +-
4 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/PingOptions.java b/api/src/main/java/com/velocitypowered/api/proxy/server/PingOptions.java
index 51be358e..a3cd0ac1 100644
--- a/api/src/main/java/com/velocitypowered/api/proxy/server/PingOptions.java
+++ b/api/src/main/java/com/velocitypowered/api/proxy/server/PingOptions.java
@@ -15,6 +15,7 @@ import java.util.Objects;
import java.util.concurrent.TimeUnit;
import net.kyori.adventure.builder.AbstractBuilder;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
* Contains the parameters used to ping a {@link RegisteredServer}.
@@ -30,10 +31,12 @@ public final class PingOptions {
public static final PingOptions DEFAULT = PingOptions.builder().build();
private final ProtocolVersion protocolVersion;
private final long timeout;
+ private final String virtualHost;
private PingOptions(final Builder builder) {
this.protocolVersion = builder.protocolVersion;
this.timeout = builder.timeout;
+ this.virtualHost = builder.virtualHost;
}
/**
@@ -54,6 +57,16 @@ public final class PingOptions {
return this.timeout;
}
+ /**
+ * The virtual host to pass to the server for the ping.
+ *
+ * @return the virtual hostname to pass to the server for the ping
+ * @since 3.4.0
+ */
+ public @Nullable String getVirtualHost() {
+ return this.virtualHost;
+ }
+
/**
* Create a new builder to assign values to a new PingOptions.
*
@@ -68,10 +81,9 @@ public final class PingOptions {
if (o == null) {
return false;
}
- if (!(o instanceof PingOptions)) {
+ if (!(o instanceof final PingOptions other)) {
return false;
}
- final PingOptions other = (PingOptions) o;
return Objects.equals(this.protocolVersion, other.protocolVersion)
&& Objects.equals(this.timeout, other.timeout);
}
@@ -97,6 +109,7 @@ public final class PingOptions {
public static final class Builder implements AbstractBuilder {
private ProtocolVersion protocolVersion = ProtocolVersion.UNKNOWN;
private long timeout = 0;
+ private String virtualHost = null;
private Builder() {
}
@@ -146,6 +159,18 @@ public final class PingOptions {
return this;
}
+ /**
+ * Sets the virtual host to pass to the server for the ping.
+ *
+ * @param virtualHost the virtual hostname to pass to the server for the ping
+ * @return this builder
+ * @since 3.4.0
+ */
+ public Builder virtualHost(final @Nullable String virtualHost) {
+ this.virtualHost = virtualHost;
+ return this;
+ }
+
/**
* Create a new {@link PingOptions} with the values of this Builder.
*
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java
index 951d6098..97947eb6 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java
@@ -63,7 +63,7 @@ public class ServerListPingHandler {
}
private CompletableFuture attemptPingPassthrough(VelocityInboundConnection connection,
- PingPassthroughMode mode, List servers, ProtocolVersion responseProtocolVersion) {
+ PingPassthroughMode mode, List servers, ProtocolVersion responseProtocolVersion, String virtualHostStr) {
ServerPing fallback = constructLocalPing(connection.getProtocolVersion());
List> pings = new ArrayList<>();
for (String s : servers) {
@@ -73,7 +73,7 @@ public class ServerListPingHandler {
}
VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs.get();
pings.add(vrs.ping(connection.getConnection().eventLoop(), PingOptions.builder()
- .version(responseProtocolVersion).build()));
+ .version(responseProtocolVersion).virtualHost(virtualHostStr).build()));
}
if (pings.isEmpty()) {
return CompletableFuture.completedFuture(fallback);
@@ -155,7 +155,7 @@ public class ServerListPingHandler {
.orElse("");
List serversToTry = server.getConfiguration().getForcedHosts().getOrDefault(
virtualHostStr, server.getConfiguration().getAttemptConnectionOrder());
- return attemptPingPassthrough(connection, passthroughMode, serversToTry, shownVersion);
+ return attemptPingPassthrough(connection, passthroughMode, serversToTry, shownVersion, virtualHostStr);
}
}
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java
index 89760344..e58c72b4 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/server/PingSessionHandler.java
@@ -43,20 +43,23 @@ public class PingSessionHandler implements MinecraftSessionHandler {
private final MinecraftConnection connection;
private final ProtocolVersion version;
private boolean completed = false;
+ private final String virtualHostString;
PingSessionHandler(CompletableFuture result, RegisteredServer server,
- MinecraftConnection connection, ProtocolVersion version) {
+ MinecraftConnection connection, ProtocolVersion version, String virtualHostString) {
this.result = result;
this.server = server;
this.connection = connection;
this.version = version;
+ this.virtualHostString = virtualHostString;
}
@Override
public void activated() {
HandshakePacket handshake = new HandshakePacket();
handshake.setIntent(HandshakeIntent.STATUS);
- handshake.setServerAddress(server.getServerInfo().getAddress().getHostString());
+ handshake.setServerAddress(this.virtualHostString == null || this.virtualHostString.isEmpty()
+ ? server.getServerInfo().getAddress().getHostString() : this.virtualHostString);
handshake.setPort(server.getServerInfo().getAddress().getPort());
handshake.setProtocolVersion(version);
connection.delayedWrite(handshake);
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java
index a3ddd71c..697135e3 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/server/VelocityRegisteredServer.java
@@ -129,7 +129,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
if (future.isSuccess()) {
MinecraftConnection conn = future.channel().pipeline().get(MinecraftConnection.class);
PingSessionHandler handler = new PingSessionHandler(pingFuture,
- VelocityRegisteredServer.this, conn, pingOptions.getProtocolVersion());
+ VelocityRegisteredServer.this, conn, pingOptions.getProtocolVersion(), pingOptions.getVirtualHost());
conn.setActiveSessionHandler(StateRegistry.HANDSHAKE, handler);
} else {
pingFuture.completeExceptionally(future.cause());
From 91bdcebb1aefd7eb21ef88356e4dc88a3f697cb3 Mon Sep 17 00:00:00 2001
From: Outfluencer
Date: Fri, 31 Jan 2025 14:11:39 +0100
Subject: [PATCH 14/34] Use real vanilla limits for LegacyChat (#1502)
Client -> server
< 1.11 has 100
>= 1.11 has 256
Server -> client has always 262144
---
.../proxy/protocol/packet/chat/legacy/LegacyChatPacket.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java
index ccefdd13..80b239d5 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/legacy/LegacyChatPacket.java
@@ -91,7 +91,8 @@ public class LegacyChatPacket implements MinecraftPacket {
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
- message = ProtocolUtils.readString(buf, 256);
+ message = ProtocolUtils.readString(buf, direction == ProtocolUtils.Direction.CLIENTBOUND
+ ? 262144 : version.noLessThan(ProtocolVersion.MINECRAFT_1_11) ? 256 : 100);
if (direction == ProtocolUtils.Direction.CLIENTBOUND
&& version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
type = buf.readByte();
From 6815808d327fa8d1a742099a4206d69ba7423764 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke
Date: Fri, 31 Jan 2025 19:11:34 +0100
Subject: [PATCH 15/34] Improve fml mod list parsing
---
api/src/main/java/com/velocitypowered/api/util/ModInfo.java | 2 ++
.../proxy/connection/forge/legacy/LegacyForgeUtil.java | 1 +
2 files changed, 3 insertions(+)
diff --git a/api/src/main/java/com/velocitypowered/api/util/ModInfo.java b/api/src/main/java/com/velocitypowered/api/util/ModInfo.java
index 8a51f322..6c65782f 100644
--- a/api/src/main/java/com/velocitypowered/api/util/ModInfo.java
+++ b/api/src/main/java/com/velocitypowered/api/util/ModInfo.java
@@ -79,6 +79,8 @@ public final class ModInfo {
public Mod(String id, String version) {
this.id = Preconditions.checkNotNull(id, "id");
this.version = Preconditions.checkNotNull(version, "version");
+ Preconditions.checkArgument(id.length() < 128, "mod id is too long");
+ Preconditions.checkArgument(version.length() < 128, "mod version is too long");
}
public String getId() {
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java
index 1d066117..854a52d7 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java
@@ -64,6 +64,7 @@ class LegacyForgeUtil {
if (discriminator == MOD_LIST_DISCRIMINATOR) {
ImmutableList.Builder mods = ImmutableList.builder();
int modCount = ProtocolUtils.readVarInt(contents);
+ Preconditions.checkArgument(modCount < 1024, "Oversized mods list");
for (int index = 0; index < modCount; index++) {
String id = ProtocolUtils.readString(contents);
From 1652d44f5fd874e50285cffc5e791dc300e0d5b6 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke
Date: Fri, 31 Jan 2025 19:35:43 +0100
Subject: [PATCH 16/34] =?UTF-8?q?Fix=20checkstyle=20=F0=9F=92=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
api/src/main/java/com/velocitypowered/api/util/ModInfo.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/api/src/main/java/com/velocitypowered/api/util/ModInfo.java b/api/src/main/java/com/velocitypowered/api/util/ModInfo.java
index 6c65782f..cfc52289 100644
--- a/api/src/main/java/com/velocitypowered/api/util/ModInfo.java
+++ b/api/src/main/java/com/velocitypowered/api/util/ModInfo.java
@@ -76,6 +76,12 @@ public final class ModInfo {
private final String id;
private final String version;
+ /**
+ * Creates a new mod info.
+ *
+ * @param id the mod identifier
+ * @param version the mod version
+ */
public Mod(String id, String version) {
this.id = Preconditions.checkNotNull(id, "id");
this.version = Preconditions.checkNotNull(version, "version");
From a26d5581c47bda30e34933fd176a3025e31c5ebc Mon Sep 17 00:00:00 2001
From: Kezz
Date: Wed, 12 Feb 2025 19:35:15 +0000
Subject: [PATCH 17/34] docs: Remove beta annotations on events (#1505)
These events aren't in beta, some of them have been stable for years now.
---
.../api/event/command/PlayerAvailableCommandsEvent.java | 2 --
.../api/event/player/ServerPostConnectEvent.java | 2 --
.../api/event/proxy/server/ServerRegisteredEvent.java | 2 --
.../api/event/proxy/server/ServerUnregisteredEvent.java | 2 --
4 files changed, 8 deletions(-)
diff --git a/api/src/main/java/com/velocitypowered/api/event/command/PlayerAvailableCommandsEvent.java b/api/src/main/java/com/velocitypowered/api/event/command/PlayerAvailableCommandsEvent.java
index 3f9b0298..d00cffac 100644
--- a/api/src/main/java/com/velocitypowered/api/event/command/PlayerAvailableCommandsEvent.java
+++ b/api/src/main/java/com/velocitypowered/api/event/command/PlayerAvailableCommandsEvent.java
@@ -9,7 +9,6 @@ package com.velocitypowered.api.event.command;
import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.annotations.Beta;
import com.mojang.brigadier.tree.RootCommandNode;
import com.velocitypowered.api.event.annotation.AwaitingEvent;
import com.velocitypowered.api.proxy.Player;
@@ -21,7 +20,6 @@ import com.velocitypowered.api.proxy.Player;
* client.
*/
@AwaitingEvent
-@Beta
public class PlayerAvailableCommandsEvent {
private final Player player;
diff --git a/api/src/main/java/com/velocitypowered/api/event/player/ServerPostConnectEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/ServerPostConnectEvent.java
index 1be39544..95e70a49 100644
--- a/api/src/main/java/com/velocitypowered/api/event/player/ServerPostConnectEvent.java
+++ b/api/src/main/java/com/velocitypowered/api/event/player/ServerPostConnectEvent.java
@@ -7,7 +7,6 @@
package com.velocitypowered.api.event.player;
-import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
@@ -18,7 +17,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* available in {@link Player#getCurrentServer()}. Velocity will not wait on this event to finish
* firing.
*/
-@Beta
public class ServerPostConnectEvent {
private final Player player;
private final RegisteredServer previousServer;
diff --git a/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerRegisteredEvent.java b/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerRegisteredEvent.java
index 754492a6..1ffb58bc 100644
--- a/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerRegisteredEvent.java
+++ b/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerRegisteredEvent.java
@@ -7,7 +7,6 @@
package com.velocitypowered.api.event.proxy.server;
-import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
@@ -23,7 +22,6 @@ import org.jetbrains.annotations.NotNull;
* @param registeredServer A {@link RegisteredServer} that has been registered.
* @since 3.3.0
*/
-@Beta
public record ServerRegisteredEvent(@NotNull RegisteredServer registeredServer) {
public ServerRegisteredEvent {
Preconditions.checkNotNull(registeredServer, "registeredServer");
diff --git a/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerUnregisteredEvent.java b/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerUnregisteredEvent.java
index 36b4023b..1d9548b7 100644
--- a/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerUnregisteredEvent.java
+++ b/api/src/main/java/com/velocitypowered/api/event/proxy/server/ServerUnregisteredEvent.java
@@ -7,7 +7,6 @@
package com.velocitypowered.api.event.proxy.server;
-import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
@@ -23,7 +22,6 @@ import org.jetbrains.annotations.NotNull;
* @param unregisteredServer A {@link RegisteredServer} that has been unregistered.
* @since 3.3.0
*/
-@Beta
public record ServerUnregisteredEvent(@NotNull RegisteredServer unregisteredServer) {
public ServerUnregisteredEvent {
Preconditions.checkNotNull(unregisteredServer, "unregisteredServer");
From 83c1749eed3f550b54aa726e778dee1763201ac6 Mon Sep 17 00:00:00 2001
From: Andre_601
Date: Wed, 12 Feb 2025 20:35:38 +0100
Subject: [PATCH 18/34] Add javadoc for ServerPing.Builder (#1500)
* Add javadoc for ServerPing.Builder
* Address codestyle reports
---
.../api/proxy/server/ServerPing.java | 77 ++++++++++++++++++-
1 file changed, 76 insertions(+), 1 deletion(-)
diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java
index a7d9518f..f927228e 100644
--- a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java
+++ b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java
@@ -159,31 +159,68 @@ public final class ServerPing {
}
+ /**
+ * Uses the modified {@code version} info in the response.
+ *
+ * @param version version info to set
+ * @return this builder, for chaining
+ */
public Builder version(Version version) {
this.version = Preconditions.checkNotNull(version, "version");
return this;
}
+ /**
+ * Uses the modified {@code onlinePlayers} number in the response.
+ *
+ * @param onlinePlayers number for online players to set
+ * @return this builder, for chaining
+ */
public Builder onlinePlayers(int onlinePlayers) {
this.onlinePlayers = onlinePlayers;
return this;
}
+ /**
+ * Uses the modified {@code maximumPlayers} number in the response.
+ * This will not modify the actual maximum players that can join the server.
+ *
+ * @param maximumPlayers number for maximum players to set
+ * @return this builder, for chaining
+ */
public Builder maximumPlayers(int maximumPlayers) {
this.maximumPlayers = maximumPlayers;
return this;
}
+ /**
+ * Uses the modified {@code players} array in the response.
+ *
+ * @param players array of SamplePlayers to set
+ * @return this builder, for chaining
+ */
public Builder samplePlayers(SamplePlayer... players) {
this.samplePlayers.addAll(Arrays.asList(players));
return this;
}
+ /**
+ * Uses the modified {@code modType} in the response.
+ *
+ * @param modType the mod type to set
+ * @return this builder, for chaining
+ */
public Builder modType(String modType) {
this.modType = Preconditions.checkNotNull(modType, "modType");
return this;
}
+ /**
+ * Uses the modified {@code mods} array in the response.
+ *
+ * @param mods array of mods to use
+ * @return this builder, for chaining
+ */
public Builder mods(ModInfo.Mod... mods) {
this.mods.addAll(Arrays.asList(mods));
return this;
@@ -193,7 +230,7 @@ public final class ServerPing {
* Uses the modified {@code mods} list in the response.
*
* @param mods the mods list to use
- * @return this build, for chaining
+ * @return this builder, for chaining
*/
public Builder mods(ModInfo mods) {
Preconditions.checkNotNull(mods, "mods");
@@ -203,36 +240,74 @@ public final class ServerPing {
return this;
}
+ /**
+ * Clears the current list of mods to use in the response.
+ *
+ * @return this builder, for chaining
+ */
public Builder clearMods() {
this.mods.clear();
return this;
}
+ /**
+ * Clears the current list of PlayerSamples to use in the response.
+ *
+ * @return this builder, for chaining
+ */
public Builder clearSamplePlayers() {
this.samplePlayers.clear();
return this;
}
+ /**
+ * Defines the server as mod incompatible in the response.
+ *
+ * @return this builder, for chaining
+ */
public Builder notModCompatible() {
this.nullOutModinfo = true;
return this;
}
+ /**
+ * Enables nulling Players in the response.
+ * This will display the player count as {@code ???}.
+ *
+ * @return this builder, for chaining
+ */
public Builder nullPlayers() {
this.nullOutPlayers = true;
return this;
}
+ /**
+ * Uses the {@code description} Component in the response.
+ *
+ * @param description Component to use as the description.
+ * @return this builder, for chaining
+ */
public Builder description(net.kyori.adventure.text.Component description) {
this.description = Preconditions.checkNotNull(description, "description");
return this;
}
+ /**
+ * Uses the {@code favicon} in the response.
+ *
+ * @param favicon Favicon instance to use.
+ * @return this builder, for chaining
+ */
public Builder favicon(Favicon favicon) {
this.favicon = Preconditions.checkNotNull(favicon, "favicon");
return this;
}
+ /**
+ * Clears the current favicon used in the response.
+ *
+ * @return this builder, for chaining
+ */
public Builder clearFavicon() {
this.favicon = null;
return this;
From 9d1332d3a3444814a77e7539935f27bd3d2755d4 Mon Sep 17 00:00:00 2001
From: Shane Freeder
Date: Tue, 11 Feb 2025 17:19:37 +0000
Subject: [PATCH 19/34] Add PluginMessageEvents for configuration phase
---
.../backend/ConfigSessionHandler.java | 28 ++++++++++++++++-
.../client/ClientConfigSessionHandler.java | 30 ++++++++++++++++++-
2 files changed, 56 insertions(+), 2 deletions(-)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java
index 74f0576c..1860093d 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java
@@ -17,6 +17,7 @@
package com.velocitypowered.proxy.connection.backend;
+import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.connection.PreTransferEvent;
import com.velocitypowered.api.event.player.CookieRequestEvent;
import com.velocitypowered.api.event.player.CookieStoreEvent;
@@ -24,6 +25,7 @@ import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent;
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
import com.velocitypowered.api.network.ProtocolVersion;
+import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
@@ -54,6 +56,8 @@ import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket;
import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket;
import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
@@ -261,7 +265,29 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(),
serverConn.getPlayer().getProtocolVersion()));
} else {
- serverConn.getPlayer().getConnection().write(packet.retain());
+ byte[] bytes = ByteBufUtil.getBytes(packet.content());
+ ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel());
+
+ if (id == null) {
+ serverConn.getPlayer().getConnection().write(packet.retain());
+ return true;
+ }
+
+ // Handling this stuff async means that we should probably pause
+ // the connection while we toss this off into another pool
+ this.serverConn.getConnection().setAutoReading(false);
+ this.server.getEventManager()
+ .fire(new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, bytes))
+ .thenAcceptAsync(pme -> {
+ if (pme.getResult().isAllowed() && !serverConn.getPlayer().getConnection().isClosed()) {
+ serverConn.getPlayer().getConnection().write(new PluginMessagePacket(
+ pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes)));
+ }
+ this.serverConn.getConnection().setAutoReading(true);
+ }, serverConn.ensureConnected().eventLoop()).exceptionally((ex) -> {
+ logger.error("Exception while handling plugin message {}", packet, ex);
+ return null;
+ });
}
return true;
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java
index 7bb7bedf..a4e734ed 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java
@@ -17,14 +17,17 @@
package com.velocitypowered.proxy.connection.client;
+import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.CookieReceiveEvent;
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
import com.velocitypowered.api.event.player.configuration.PlayerConfigurationEvent;
import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
+import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
+import com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
@@ -41,6 +44,7 @@ import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket;
import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -123,8 +127,32 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
brandChannel = packet.getChannel();
// Client sends `minecraft:brand` packet immediately after Login,
// but at this time the backend server may not be ready
+ } else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) {
+ return true;
} else if (serverConn != null) {
- serverConn.ensureConnected().write(packet.retain());
+ byte[] bytes = ByteBufUtil.getBytes(packet.content());
+ ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel());
+
+ if (id == null) {
+ serverConn.getPlayer().getConnection().write(packet.retain());
+ return true;
+ }
+
+ // Handling this stuff async means that we should probably pause
+ // the connection while we toss this off into another pool
+ serverConn.getPlayer().getConnection().setAutoReading(false);
+ this.server.getEventManager()
+ .fire(new PluginMessageEvent(serverConn.getPlayer(), serverConn, id, bytes))
+ .thenAcceptAsync(pme -> {
+ if (pme.getResult().isAllowed() && serverConn.getConnection() != null) {
+ serverConn.ensureConnected().write(new PluginMessagePacket(
+ pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes)));
+ }
+ serverConn.getPlayer().getConnection().setAutoReading(true);
+ }, player.getConnection().eventLoop()).exceptionally((ex) -> {
+ logger.error("Exception while handling plugin message packet for {}", player, ex);
+ return null;
+ });
}
return true;
}
From f986eb51ecf341fe3eec2a14a1f3563f4a58af21 Mon Sep 17 00:00:00 2001
From: Shane Freeder
Date: Thu, 13 Feb 2025 12:12:33 +0000
Subject: [PATCH 20/34] Do not print an exception if a client closed before
switching to config state
---
.../proxy/connection/client/ConnectedPlayer.java | 5 +++++
1 file changed, 5 insertions(+)
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 a7717055..be7a82b2 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
@@ -1287,6 +1287,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
public void switchToConfigState() {
server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer()))
.completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
+ // if the connection was closed earlier, there is a risk that the player is no longer connected
+ if (!connection.getChannel().isActive()) {
+ return;
+ }
+
if (bundleHandler.isInBundleSession()) {
bundleHandler.toggleBundleSession();
connection.write(BundleDelimiterPacket.INSTANCE);
From e69213f9877d5b38c0e7f460b6133cf1ce7bfe83 Mon Sep 17 00:00:00 2001
From: Tommy
Date: Mon, 17 Feb 2025 12:07:06 +0000
Subject: [PATCH 21/34] respect log-player-connections flag in config for
connection messages (#1503)
---
.../proxy/connection/client/AuthSessionHandler.java | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
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 257429e2..0aea8d77 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
@@ -106,7 +106,9 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
return CompletableFuture.completedFuture(null);
}
- logger.info("{} has connected", player);
+ if (server.getConfiguration().isLogPlayerConnections()) {
+ logger.info("{} has connected", player);
+ }
return server.getEventManager()
.fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
From 58816c804a80d781c3d734c8ad670e25959116c9 Mon Sep 17 00:00:00 2001
From: ST3V1K <56466580+ST3V1K@users.noreply.github.com>
Date: Sat, 22 Feb 2025 17:08:37 +0100
Subject: [PATCH 22/34] fix: problem with PluginMessageEvents for configuration
phase (#1517)
---
.../proxy/connection/client/ClientConfigSessionHandler.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java
index a4e734ed..a7143462 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java
@@ -134,7 +134,7 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel());
if (id == null) {
- serverConn.getPlayer().getConnection().write(packet.retain());
+ serverConn.ensureConnected().write(packet.retain());
return true;
}
From 0afe0612247206275a0a2cdba0440ec7b4e46154 Mon Sep 17 00:00:00 2001
From: Adrian <68704415+4drian3d@users.noreply.github.com>
Date: Thu, 27 Feb 2025 01:25:01 -0500
Subject: [PATCH 23/34] Updated Adventure to 4.19.0 (#1520)
Also updated ASM and Ansi to support Java 23 and 24
---
.../com/velocitypowered/api/command/CommandSource.java | 4 ++--
gradle/libs.versions.toml | 10 +++++-----
.../main/java/com/velocitypowered/proxy/Metrics.java | 3 ++-
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java
index b3263639..3cc03925 100644
--- a/api/src/main/java/com/velocitypowered/api/command/CommandSource.java
+++ b/api/src/main/java/com/velocitypowered/api/command/CommandSource.java
@@ -27,7 +27,7 @@ public interface CommandSource extends Audience, PermissionSubject {
* for more information on the format.
**/
default void sendRichMessage(final @NotNull String message) {
- this.sendMessage(MiniMessage.miniMessage().deserialize(message));
+ this.sendMessage(MiniMessage.miniMessage().deserialize(message, this));
}
/**
@@ -43,7 +43,7 @@ public interface CommandSource extends Audience, PermissionSubject {
final @NotNull String message,
final @NotNull TagResolver @NotNull... resolvers
) {
- this.sendMessage(MiniMessage.miniMessage().deserialize(message, resolvers));
+ this.sendMessage(MiniMessage.miniMessage().deserialize(message, this, resolvers));
}
/**
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e3859eb0..9d34ba9e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,14 +11,14 @@ shadow = "io.github.goooler.shadow:8.1.5"
spotless = "com.diffplug.spotless:6.25.0"
[libraries]
-adventure-bom = "net.kyori:adventure-bom:4.18.0"
-adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.18.0"
+adventure-bom = "net.kyori:adventure-bom:4.19.0"
+adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.19.0"
adventure-facet = "net.kyori:adventure-platform-facet:4.3.4"
-asm = "org.ow2.asm:asm:9.6"
+asm = "org.ow2.asm:asm:9.7.1"
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"
+bstats = "org.bstats:bstats-base:3.0.3"
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"
@@ -37,7 +37,7 @@ 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.1.0"
+kyori-ansi = "net.kyori:ansi:1.1.1"
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"
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java
index 89f63b56..7feeb25f 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/Metrics.java
@@ -65,7 +65,8 @@ public class Metrics {
logger::info,
config.isLogErrorsEnabled(),
config.isLogSentDataEnabled(),
- config.isLogResponseStatusTextEnabled()
+ config.isLogResponseStatusTextEnabled(),
+ false
);
if (!config.didExistBefore()) {
From d4ea40a4a27f352b1c9871dd7cb7edd3c64a6884 Mon Sep 17 00:00:00 2001
From: Andrew Steinborn
Date: Thu, 27 Feb 2025 23:42:39 -0500
Subject: [PATCH 24/34] Add support for `SO_REUSEPORT`
---
.../proxy/config/VelocityConfiguration.java | 12 ++
.../proxy/network/ConnectionManager.java | 115 ++++++++++++------
.../src/main/resources/default-velocity.toml | 6 +
3 files changed, 93 insertions(+), 40 deletions(-)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
index 9c2e1e82..bbd49f55 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java
@@ -407,6 +407,10 @@ public class VelocityConfiguration implements ProxyConfig {
return forceKeyAuthentication;
}
+ public boolean isEnableReusePort() {
+ return advanced.isEnableReusePort();
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
@@ -716,6 +720,8 @@ public class VelocityConfiguration implements ProxyConfig {
private boolean logPlayerConnections = true;
@Expose
private boolean acceptTransfers = false;
+ @Expose
+ private boolean enableReusePort = false;
private Advanced() {
}
@@ -741,6 +747,7 @@ public class VelocityConfiguration implements ProxyConfig {
this.logCommandExecutions = config.getOrElse("log-command-executions", false);
this.logPlayerConnections = config.getOrElse("log-player-connections", true);
this.acceptTransfers = config.getOrElse("accepts-transfers", false);
+ this.enableReusePort = config.getOrElse("enable-reuse-port", false);
}
}
@@ -804,6 +811,10 @@ public class VelocityConfiguration implements ProxyConfig {
return this.acceptTransfers;
}
+ public boolean isEnableReusePort() {
+ return enableReusePort;
+ }
+
@Override
public String toString() {
return "Advanced{"
@@ -821,6 +832,7 @@ public class VelocityConfiguration implements ProxyConfig {
+ ", logCommandExecutions=" + logCommandExecutions
+ ", logPlayerConnections=" + logPlayerConnections
+ ", acceptTransfers=" + acceptTransfers
+ + ", enableReusePort=" + enableReusePort
+ '}';
}
}
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 7af894f4..7b724f61 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java
@@ -18,6 +18,8 @@
package com.velocitypowered.proxy.network;
import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
import com.velocitypowered.api.event.proxy.ListenerCloseEvent;
import com.velocitypowered.api.network.ListenerType;
@@ -28,14 +30,17 @@ import com.velocitypowered.proxy.protocol.netty.GameSpyQueryHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
+import io.netty.channel.unix.UnixChannelOption;
import io.netty.util.concurrent.GlobalEventExecutor;
+import io.netty.util.concurrent.MultithreadEventExecutorGroup;
import java.net.InetSocketAddress;
import java.net.http.HttpClient;
-import java.util.HashMap;
+import java.util.Collection;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -49,7 +54,7 @@ public final class ConnectionManager {
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
1 << 21);
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
- private final Map endpoints = new HashMap<>();
+ private final Multimap endpoints = HashMultimap.create();
private final TransportType transportType;
private final EventLoopGroup bossGroup;
private final EventLoopGroup workerGroup;
@@ -93,7 +98,6 @@ public final class ConnectionManager {
public void bind(final InetSocketAddress address) {
final ServerBootstrap bootstrap = new ServerBootstrap()
.channelFactory(this.transportType.serverSocketChannelFactory)
- .group(this.bossGroup, this.workerGroup)
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
.childHandler(this.serverChannelInitializer.get())
.childOption(ChannelOption.TCP_NODELAY, true)
@@ -104,26 +108,50 @@ public final class ConnectionManager {
bootstrap.option(ChannelOption.TCP_FASTOPEN, 3);
}
- bootstrap.bind()
- .addListener((ChannelFutureListener) future -> {
- 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());
+ if (server.getConfiguration().isEnableReusePort()) {
+ // We don't need a boss group, since each worker will bind to the socket
+ bootstrap.option(UnixChannelOption.SO_REUSEPORT, true)
+ .group(this.workerGroup);
+ } else {
+ bootstrap.group(this.bossGroup, this.workerGroup);
+ }
+
+ final int binds = server.getConfiguration().isEnableReusePort()
+ ? ((MultithreadEventExecutorGroup) this.workerGroup).executorCount() : 1;
+
+ for (int bind = 0; bind < binds; bind++) {
+ // Wait for each bind to open. If we encounter any errors, don't try to bind again.
+ int finalBind = bind;
+ ChannelFuture f = bootstrap.bind()
+ .addListener((ChannelFutureListener) future -> {
+ final Channel channel = future.channel();
+ if (future.isSuccess()) {
+ this.endpoints.put(address, new Endpoint(channel, ListenerType.MINECRAFT));
+
+ LOGGER.info("Listening on {}", channel.localAddress());
+
+ if (finalBind == 0) {
+ // 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());
+ }
+
+ // Fire the proxy bound event after the socket is bound
+ server.getEventManager().fireAndForget(
+ new ListenerBoundEvent(address, ListenerType.MINECRAFT));
+ }
+ } else {
+ LOGGER.error("Can't bind to {}", address, future.cause());
}
+ });
+ f.syncUninterruptibly();
- LOGGER.info("Listening on {}", channel.localAddress());
-
- // Fire the proxy bound event after the socket is bound
- server.getEventManager().fireAndForget(
- new ListenerBoundEvent(address, ListenerType.MINECRAFT));
- } else {
- LOGGER.error("Can't bind to {}", address, future.cause());
- }
- });
+ if (!f.isSuccess()) {
+ break;
+ }
+ }
}
/**
@@ -181,17 +209,20 @@ public final class ConnectionManager {
* @param oldBind the endpoint to close
*/
public void close(InetSocketAddress oldBind) {
- Endpoint endpoint = endpoints.remove(oldBind);
+ Collection endpoints = this.endpoints.removeAll(oldBind);
+ Preconditions.checkState(!endpoints.isEmpty(), "Endpoint was not registered");
+
+ ListenerType type = endpoints.iterator().next().getType();
// Fire proxy close event to notify plugins of socket close. We block since plugins
// should have a chance to be notified before the server stops accepting connections.
- server.getEventManager().fire(new ListenerCloseEvent(oldBind, endpoint.getType())).join();
+ server.getEventManager().fire(new ListenerCloseEvent(oldBind, type)).join();
- Channel serverChannel = endpoint.getChannel();
-
- Preconditions.checkState(serverChannel != null, "Endpoint %s not registered", oldBind);
- LOGGER.info("Closing endpoint {}", serverChannel.localAddress());
- serverChannel.close().syncUninterruptibly();
+ for (Endpoint endpoint : endpoints) {
+ Channel serverChannel = endpoint.getChannel();
+ LOGGER.info("Closing endpoint {}", serverChannel.localAddress());
+ serverChannel.close().syncUninterruptibly();
+ }
}
/**
@@ -200,24 +231,28 @@ public final class ConnectionManager {
* @param interrupt should closing forward interruptions
*/
public void closeEndpoints(boolean interrupt) {
- for (final Map.Entry entry : this.endpoints.entrySet()) {
+ for (final Map.Entry> entry : this.endpoints.asMap()
+ .entrySet()) {
final InetSocketAddress address = entry.getKey();
- final Endpoint endpoint = entry.getValue();
+ final Collection endpoints = entry.getValue();
+ ListenerType type = endpoints.iterator().next().getType();
// Fire proxy close event to notify plugins of socket close. We block since plugins
// should have a chance to be notified before the server stops accepting connections.
- server.getEventManager().fire(new ListenerCloseEvent(address, endpoint.getType())).join();
+ server.getEventManager().fire(new ListenerCloseEvent(address, type)).join();
- LOGGER.info("Closing endpoint {}", address);
- if (interrupt) {
- try {
- endpoint.getChannel().close().sync();
- } catch (final InterruptedException e) {
- LOGGER.info("Interrupted whilst closing endpoint", e);
- Thread.currentThread().interrupt();
+ for (Endpoint endpoint : endpoints) {
+ LOGGER.info("Closing endpoint {}", address);
+ if (interrupt) {
+ try {
+ endpoint.getChannel().close().sync();
+ } catch (final InterruptedException e) {
+ LOGGER.info("Interrupted whilst closing endpoint", e);
+ Thread.currentThread().interrupt();
+ }
+ } else {
+ endpoint.getChannel().close().syncUninterruptibly();
}
- } else {
- endpoint.getChannel().close().syncUninterruptibly();
}
}
this.endpoints.clear();
diff --git a/proxy/src/main/resources/default-velocity.toml b/proxy/src/main/resources/default-velocity.toml
index e402305c..5b0a1e27 100644
--- a/proxy/src/main/resources/default-velocity.toml
+++ b/proxy/src/main/resources/default-velocity.toml
@@ -145,6 +145,12 @@ log-player-connections = true
# Transfer packet (Minecraft 1.20.5) to be received.
accepts-transfers = false
+# Enables support for SO_REUSEPORT. This may help the proxy scale better on multicore systems
+# with a lot of incoming connections, and provide better CPU utilization than the existing
+# strategy of having a single thread accepting connections and distributing them to worker
+# threads. Disabled by default. Requires Linux or macOS.
+enable-reuse-port = false
+
[query]
# Whether to enable responding to GameSpy 4 query responses or not.
enabled = false
From b8fe3577c9582972a92134642e35eb7fac671376 Mon Sep 17 00:00:00 2001
From: Gero
Date: Fri, 28 Feb 2025 15:01:20 +0100
Subject: [PATCH 25/34] Use correct component serializer in
ComponentHolder#getBinaryTag
---
.../proxy/protocol/packet/chat/ComponentHolder.java | 9 +++------
1 file changed, 3 insertions(+), 6 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 0935f8ea..22b49621 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
@@ -43,7 +43,6 @@ import net.kyori.adventure.nbt.LongBinaryTag;
import net.kyori.adventure.nbt.ShortBinaryTag;
import net.kyori.adventure.nbt.StringBinaryTag;
import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@@ -106,16 +105,14 @@ public class ComponentHolder {
public BinaryTag getBinaryTag() {
if (binaryTag == null) {
// TODO: replace this with adventure-text-serializer-nbt
- binaryTag = serialize(GsonComponentSerializer.gson().serializeToTree(getComponent()));
+ binaryTag = serialize(ProtocolUtils.getJsonChatSerializer(version).serializeToTree(getComponent()));
}
return binaryTag;
}
public static BinaryTag serialize(JsonElement json) {
- if (json instanceof JsonPrimitive) {
- JsonPrimitive jsonPrimitive = (JsonPrimitive) json;
-
- if (jsonPrimitive.isNumber()) {
+ if (json instanceof JsonPrimitive jsonPrimitive) {
+ if (jsonPrimitive.isNumber()) {
Number number = json.getAsNumber();
if (number instanceof Byte) {
From 8f3dea54277558ceade53eb19a9dbdb309685e09 Mon Sep 17 00:00:00 2001
From: Andrew Steinborn
Date: Fri, 28 Feb 2025 22:21:32 -0500
Subject: [PATCH 26/34] Bump Netty version
---
gradle/libs.versions.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 9d34ba9e..3057e031 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -3,7 +3,7 @@ configurate3 = "3.7.3"
configurate4 = "4.1.2"
flare = "2.0.1"
log4j = "2.24.1"
-netty = "4.1.114.Final"
+netty = "4.1.119.Final"
[plugins]
indra-publishing = "net.kyori.indra.publishing:2.0.6"
From f980037bfd87ec6c1891f33e0c7af7edb507bf37 Mon Sep 17 00:00:00 2001
From: Riley Park
Date: Thu, 6 Mar 2025 19:14:18 -0800
Subject: [PATCH 27/34] chore(proxy): require explicitly setting
velocity.command.info permission to true
---
.../velocitypowered/proxy/command/builtin/VelocityCommand.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 85bf2061..1e393476 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
@@ -82,7 +82,7 @@ public final class VelocityCommand {
.executes(new Heap())
.build();
final LiteralCommandNode info = BrigadierCommand.literalArgumentBuilder("info")
- .requires(source -> source.getPermissionValue("velocity.command.info") != Tristate.FALSE)
+ .requires(source -> source.getPermissionValue("velocity.command.info") == Tristate.TRUE)
.executes(new Info(server))
.build();
final LiteralCommandNode plugins = BrigadierCommand
From c9aa1cca09313b71ead6309ecd85b59867df4183 Mon Sep 17 00:00:00 2001
From: Warrior <50800980+Warriorrrr@users.noreply.github.com>
Date: Mon, 10 Mar 2025 16:11:01 +0100
Subject: [PATCH 28/34] Enable use tab in javadocs (#1525)
---
api/build.gradle.kts | 1 +
1 file changed, 1 insertion(+)
diff --git a/api/build.gradle.kts b/api/build.gradle.kts
index 2e524db9..a4cbd741 100644
--- a/api/build.gradle.kts
+++ b/api/build.gradle.kts
@@ -61,6 +61,7 @@ tasks {
o.encoding = "UTF-8"
o.source = "17"
+ o.use()
o.links(
"https://www.slf4j.org/apidocs/",
"https://guava.dev/releases/${libs.guava.get().version}/api/docs/",
From d9f1016bd57d50f5052bb90222894adb40fd256c Mon Sep 17 00:00:00 2001
From: Jones <73846784+jonesdevelopment@users.noreply.github.com>
Date: Fri, 14 Mar 2025 10:26:59 -0500
Subject: [PATCH 29/34] Validate uncompressed packet size (#1527)
* Validate uncompressed packet size
* Fix debug using incorrect value
---
.../proxy/protocol/netty/MinecraftCompressDecoder.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java
index 6e7fb4d4..5321b6ac 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressDecoder.java
@@ -52,6 +52,9 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder {
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List