Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-17 05:20:14 +01:00
Merge branch 'dev/1.1.0' into dev/2.0.0
# Conflicts: # build.gradle
Dieser Commit ist enthalten in:
Commit
e21cd77ae7
@ -89,19 +89,4 @@ publishing {
|
|||||||
artifact javadocJar
|
artifact javadocJar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
credentials {
|
|
||||||
username System.getenv("NEXUS_USERNAME")
|
|
||||||
password System.getenv("NEXUS_PASSWORD")
|
|
||||||
}
|
|
||||||
|
|
||||||
name = 'velocity-nexus'
|
|
||||||
def base = 'https://nexus.velocitypowered.com/repository/velocity-artifacts'
|
|
||||||
def releasesRepoUrl = "$base-releases/"
|
|
||||||
def snapshotsRepoUrl = "$base-snapshots/"
|
|
||||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ public enum ProtocolVersion {
|
|||||||
MINECRAFT_1_16_1(736, "1.16.1"),
|
MINECRAFT_1_16_1(736, "1.16.1"),
|
||||||
MINECRAFT_1_16_2(751, "1.16.2"),
|
MINECRAFT_1_16_2(751, "1.16.2"),
|
||||||
MINECRAFT_1_16_3(753, "1.16.3"),
|
MINECRAFT_1_16_3(753, "1.16.3"),
|
||||||
MINECRAFT_1_16_4(754, 2, "1.16.4");
|
MINECRAFT_1_16_4(754, "1.16.4");
|
||||||
|
|
||||||
private static final int SNAPSHOT_BIT = 30;
|
private static final int SNAPSHOT_BIT = 30;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import net.kyori.minecraft.Key;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,6 +51,35 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
|||||||
return new MinecraftChannelIdentifier(namespace, name);
|
return new MinecraftChannelIdentifier(namespace, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an channel identifier from the specified Minecraft identifier.
|
||||||
|
*
|
||||||
|
* @param identifier the Minecraft identifier
|
||||||
|
* @return a new channel identifier
|
||||||
|
*/
|
||||||
|
public static MinecraftChannelIdentifier from(String identifier) {
|
||||||
|
int colonPos = identifier.indexOf(':');
|
||||||
|
if (colonPos == -1) {
|
||||||
|
throw new IllegalArgumentException("Identifier does not contain a colon.");
|
||||||
|
}
|
||||||
|
if (colonPos + 1 == identifier.length()) {
|
||||||
|
throw new IllegalArgumentException("Identifier is empty.");
|
||||||
|
}
|
||||||
|
String namespace = identifier.substring(0, colonPos);
|
||||||
|
String name = identifier.substring(colonPos + 1);
|
||||||
|
return create(namespace, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an channel identifier from the specified Minecraft identifier.
|
||||||
|
*
|
||||||
|
* @param key the Minecraft key to use
|
||||||
|
* @return a new channel identifier
|
||||||
|
*/
|
||||||
|
public static MinecraftChannelIdentifier from(Key key) {
|
||||||
|
return create(key.namespace(), key.value());
|
||||||
|
}
|
||||||
|
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return namespace;
|
return namespace;
|
||||||
}
|
}
|
||||||
@ -58,6 +88,10 @@ public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Key asKey() {
|
||||||
|
return Key.of(namespace, name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return namespace + ":" + name + " (modern)";
|
return namespace + ":" + name + " (modern)";
|
||||||
|
@ -104,8 +104,9 @@ public final class ServerPing {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of this {@link ServerPing} instance as a builder so that it can be modified.
|
* Returns a copy of this {@link ServerPing} instance as a builder so that it can be modified.
|
||||||
* It is guaranteed that {@code ping.asBuilder().ping().equals(ping)}: that is, if no other
|
* It is guaranteed that {@code ping.asBuilder().build().equals(ping)} is true: that is, if no
|
||||||
* changes are made to the returned builder, the built instance will equal the original instance.
|
* other changes are made to the returned builder, the built instance will equal the original
|
||||||
|
* instance.
|
||||||
*
|
*
|
||||||
* @return a copy of this instance as a {@link Builder}
|
* @return a copy of this instance as a {@link Builder}
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package com.velocitypowered.api.proxy.messages;
|
package com.velocitypowered.api.proxy.messages;
|
||||||
|
|
||||||
import static com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier.create;
|
import static com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier.create;
|
||||||
|
import static com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier.from;
|
||||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -26,4 +28,24 @@ class MinecraftChannelIdentifierTest {
|
|||||||
() -> assertThrows(IllegalArgumentException.class, () -> create("minecraft", null))
|
() -> assertThrows(IllegalArgumentException.class, () -> create("minecraft", null))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fromIdentifierIsCorrect() {
|
||||||
|
MinecraftChannelIdentifier expected = MinecraftChannelIdentifier.create("velocity", "test");
|
||||||
|
assertEquals(expected, MinecraftChannelIdentifier.from("velocity:test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fromIdentifierThrowsOnBadValues() {
|
||||||
|
assertAll(
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from("")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from(":")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from(":a")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from("a:")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from("hello:$$$$$$")),
|
||||||
|
() -> assertThrows(IllegalArgumentException.class, () -> from("hello::"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ publishing {
|
|||||||
|
|
||||||
name = 'velocity-nexus'
|
name = 'velocity-nexus'
|
||||||
def base = 'https://nexus.velocitypowered.com/repository/velocity-artifacts'
|
def base = 'https://nexus.velocitypowered.com/repository/velocity-artifacts'
|
||||||
def releasesRepoUrl = "$base-releases/"
|
def releasesRepoUrl = "$base-release/"
|
||||||
def snapshotsRepoUrl = "$base-snapshots/"
|
def snapshotsRepoUrl = "$base-snapshots/"
|
||||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ ! "$CC" ]; then
|
||||||
|
# The libdeflate authors recommend that we build using GCC as it produces "slightly faster binaries":
|
||||||
|
# https://github.com/ebiggers/libdeflate#for-unix
|
||||||
|
export CC=gcc
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -d libdeflate ]; then
|
if [ ! -d libdeflate ]; then
|
||||||
echo "Cloning libdeflate..."
|
echo "Cloning libdeflate..."
|
||||||
git clone https://github.com/ebiggers/libdeflate.git
|
git clone https://github.com/ebiggers/libdeflate.git
|
||||||
@ -10,10 +16,10 @@ cd libdeflate || exit
|
|||||||
CFLAGS="-fPIC -O2 -fomit-frame-pointer" make
|
CFLAGS="-fPIC -O2 -fomit-frame-pointer" make
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -fomit-frame-pointer"
|
CFLAGS="-O2 -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -fPIC -shared -Wl,-z,noexecstack -Wall -Werror -fomit-frame-pointer"
|
||||||
ARCH=$(uname -m)
|
ARCH=$(uname -m)
|
||||||
mkdir -p src/main/resources/linux_$ARCH
|
mkdir -p src/main/resources/linux_$ARCH
|
||||||
gcc $CFLAGS -Ilibdeflate src/main/c/jni_util.c src/main/c/jni_zlib_deflate.c src/main/c/jni_zlib_inflate.c \
|
$CC $CFLAGS -Ilibdeflate src/main/c/jni_util.c src/main/c/jni_zlib_deflate.c src/main/c/jni_zlib_inflate.c \
|
||||||
libdeflate/libdeflate.a -o src/main/resources/linux_$ARCH/velocity-compress.so
|
libdeflate/libdeflate.a -o src/main/resources/linux_$ARCH/velocity-compress.so
|
||||||
gcc $CFLAGS -I $MBEDTLS_ROOT/include -shared src/main/c/jni_util.c src/main/c/jni_cipher.c \
|
$CC $CFLAGS -shared src/main/c/jni_util.c src/main/c/jni_cipher.c \
|
||||||
-o src/main/resources/linux_$ARCH/velocity-cipher.so -lcrypto
|
-o src/main/resources/linux_$ARCH/velocity-cipher.so -lcrypto
|
@ -8,29 +8,32 @@ typedef unsigned char byte;
|
|||||||
|
|
||||||
JNIEXPORT jlong JNICALL
|
JNIEXPORT jlong JNICALL
|
||||||
Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
|
Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jbyteArray key,
|
jbyteArray key,
|
||||||
jboolean encrypt)
|
jboolean encrypt)
|
||||||
{
|
{
|
||||||
|
jsize keyLen = (*env)->GetArrayLength(env, key);
|
||||||
|
if (keyLen != 16) {
|
||||||
|
throwException(env, "java/lang/IllegalArgumentException", "cipher not 16 bytes");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
throwException(env, "java/lang/OutOfMemoryError", "allocate cipher");
|
throwException(env, "java/lang/OutOfMemoryError", "allocate cipher");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsize keyLen = (*env)->GetArrayLength(env, key);
|
// Since we know the array size is always bounded, we can just use Get<Primitive>ArrayRegion
|
||||||
jbyte* keyBytes = (*env)->GetPrimitiveArrayCritical(env, key, NULL);
|
// and save ourselves some error-checking headaches.
|
||||||
if (keyBytes == NULL) {
|
jbyte keyBytes[16];
|
||||||
EVP_CIPHER_CTX_free(ctx);
|
(*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) keyBytes);
|
||||||
throwException(env, "java/lang/OutOfMemoryError", "cipher get key");
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes,
|
int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes,
|
||||||
encrypt);
|
encrypt);
|
||||||
// Release the key byte array now - we won't need it
|
|
||||||
(*env)->ReleasePrimitiveArrayCritical(env, key, keyBytes, 0);
|
|
||||||
|
|
||||||
if (result != 1) {
|
if (result != 1) {
|
||||||
EVP_CIPHER_CTX_free(ctx);
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
throwException(env, "java/security/GeneralSecurityException", "openssl initialize cipher");
|
throwException(env, "java/security/GeneralSecurityException", "openssl initialize cipher");
|
||||||
@ -41,7 +44,7 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_free(JNIEnv *env,
|
Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_free(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jlong ptr)
|
jlong ptr)
|
||||||
{
|
{
|
||||||
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *) ptr);
|
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *) ptr);
|
||||||
@ -49,7 +52,7 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_free(JNIEnv *env,
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_process(JNIEnv *env,
|
Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_process(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jlong ptr,
|
jlong ptr,
|
||||||
jlong source,
|
jlong source,
|
||||||
jint len,
|
jint len,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
JNIEXPORT jlong JNICALL
|
JNIEXPORT jlong JNICALL
|
||||||
Java_com_velocitypowered_natives_compression_NativeZlibDeflate_init(JNIEnv *env,
|
Java_com_velocitypowered_natives_compression_NativeZlibDeflate_init(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jint level)
|
jint level)
|
||||||
{
|
{
|
||||||
struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level);
|
struct libdeflate_compressor *compressor = libdeflate_alloc_compressor(level);
|
||||||
@ -21,7 +21,7 @@ Java_com_velocitypowered_natives_compression_NativeZlibDeflate_init(JNIEnv *env,
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_velocitypowered_natives_compression_NativeZlibDeflate_free(JNIEnv *env,
|
Java_com_velocitypowered_natives_compression_NativeZlibDeflate_free(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jlong ctx)
|
jlong ctx)
|
||||||
{
|
{
|
||||||
libdeflate_free_compressor((struct libdeflate_compressor *) ctx);
|
libdeflate_free_compressor((struct libdeflate_compressor *) ctx);
|
||||||
@ -29,13 +29,12 @@ Java_com_velocitypowered_natives_compression_NativeZlibDeflate_free(JNIEnv *env,
|
|||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_com_velocitypowered_natives_compression_NativeZlibDeflate_process(JNIEnv *env,
|
Java_com_velocitypowered_natives_compression_NativeZlibDeflate_process(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jlong ctx,
|
jlong ctx,
|
||||||
jlong sourceAddress,
|
jlong sourceAddress,
|
||||||
jint sourceLength,
|
jint sourceLength,
|
||||||
jlong destinationAddress,
|
jlong destinationAddress,
|
||||||
jint destinationLength,
|
jint destinationLength)
|
||||||
jboolean finish)
|
|
||||||
{
|
{
|
||||||
struct libdeflate_compressor *compressor = (struct libdeflate_compressor *) ctx;
|
struct libdeflate_compressor *compressor = (struct libdeflate_compressor *) ctx;
|
||||||
size_t produced = libdeflate_zlib_compress(compressor, (void *) sourceAddress, sourceLength,
|
size_t produced = libdeflate_zlib_compress(compressor, (void *) sourceAddress, sourceLength,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
JNIEXPORT jlong JNICALL
|
JNIEXPORT jlong JNICALL
|
||||||
Java_com_velocitypowered_natives_compression_NativeZlibInflate_init(JNIEnv *env,
|
Java_com_velocitypowered_natives_compression_NativeZlibInflate_init(JNIEnv *env,
|
||||||
jobject obj)
|
jclass clazz)
|
||||||
{
|
{
|
||||||
struct libdeflate_decompressor *decompress = libdeflate_alloc_decompressor();
|
struct libdeflate_decompressor *decompress = libdeflate_alloc_decompressor();
|
||||||
if (decompress == NULL) {
|
if (decompress == NULL) {
|
||||||
@ -21,7 +21,7 @@ Java_com_velocitypowered_natives_compression_NativeZlibInflate_init(JNIEnv *env,
|
|||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_velocitypowered_natives_compression_NativeZlibInflate_free(JNIEnv *env,
|
Java_com_velocitypowered_natives_compression_NativeZlibInflate_free(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jlong ctx)
|
jlong ctx)
|
||||||
{
|
{
|
||||||
libdeflate_free_decompressor((struct libdeflate_decompressor *) ctx);
|
libdeflate_free_decompressor((struct libdeflate_decompressor *) ctx);
|
||||||
@ -29,7 +29,7 @@ Java_com_velocitypowered_natives_compression_NativeZlibInflate_free(JNIEnv *env,
|
|||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
JNIEXPORT jboolean JNICALL
|
||||||
Java_com_velocitypowered_natives_compression_NativeZlibInflate_process(JNIEnv *env,
|
Java_com_velocitypowered_natives_compression_NativeZlibInflate_process(JNIEnv *env,
|
||||||
jobject obj,
|
jclass clazz,
|
||||||
jlong ctx,
|
jlong ctx,
|
||||||
jlong sourceAddress,
|
jlong sourceAddress,
|
||||||
jint sourceLength,
|
jint sourceLength,
|
||||||
@ -53,5 +53,9 @@ Java_com_velocitypowered_natives_compression_NativeZlibInflate_process(JNIEnv *e
|
|||||||
// These cases are the same for us. We expect the full uncompressed size to be known.
|
// These cases are the same for us. We expect the full uncompressed size to be known.
|
||||||
throwException(env, "java/util/zip/DataFormatException", "uncompressed size is inaccurate");
|
throwException(env, "java/util/zip/DataFormatException", "uncompressed size is inaccurate");
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
|
default:
|
||||||
|
// Unhandled case
|
||||||
|
throwException(env, "java/util/zip/DataFormatException", "unknown libdeflate return code");
|
||||||
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,9 +9,7 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor {
|
|||||||
|
|
||||||
public static final VelocityCompressorFactory FACTORY = LibdeflateVelocityCompressor::new;
|
public static final VelocityCompressorFactory FACTORY = LibdeflateVelocityCompressor::new;
|
||||||
|
|
||||||
private final NativeZlibInflate inflate = new NativeZlibInflate();
|
|
||||||
private final long inflateCtx;
|
private final long inflateCtx;
|
||||||
private final NativeZlibDeflate deflate = new NativeZlibDeflate();
|
|
||||||
private final long deflateCtx;
|
private final long deflateCtx;
|
||||||
private boolean disposed = false;
|
private boolean disposed = false;
|
||||||
|
|
||||||
@ -21,8 +19,8 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor {
|
|||||||
throw new IllegalArgumentException("Invalid compression level " + level);
|
throw new IllegalArgumentException("Invalid compression level " + level);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inflateCtx = inflate.init();
|
this.inflateCtx = NativeZlibInflate.init();
|
||||||
this.deflateCtx = deflate.init(correctedLevel);
|
this.deflateCtx = NativeZlibDeflate.init(correctedLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -38,7 +36,7 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor {
|
|||||||
long sourceAddress = source.memoryAddress() + source.readerIndex();
|
long sourceAddress = source.memoryAddress() + source.readerIndex();
|
||||||
long destinationAddress = destination.memoryAddress() + destination.writerIndex();
|
long destinationAddress = destination.memoryAddress() + destination.writerIndex();
|
||||||
|
|
||||||
inflate.process(inflateCtx, sourceAddress, source.readableBytes(), destinationAddress,
|
NativeZlibInflate.process(inflateCtx, sourceAddress, source.readableBytes(), destinationAddress,
|
||||||
uncompressedSize);
|
uncompressedSize);
|
||||||
destination.writerIndex(destination.writerIndex() + uncompressedSize);
|
destination.writerIndex(destination.writerIndex() + uncompressedSize);
|
||||||
}
|
}
|
||||||
@ -51,7 +49,7 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor {
|
|||||||
long sourceAddress = source.memoryAddress() + source.readerIndex();
|
long sourceAddress = source.memoryAddress() + source.readerIndex();
|
||||||
long destinationAddress = destination.memoryAddress() + destination.writerIndex();
|
long destinationAddress = destination.memoryAddress() + destination.writerIndex();
|
||||||
|
|
||||||
int produced = deflate.process(deflateCtx, sourceAddress, source.readableBytes(),
|
int produced = NativeZlibDeflate.process(deflateCtx, sourceAddress, source.readableBytes(),
|
||||||
destinationAddress, destination.writableBytes());
|
destinationAddress, destination.writableBytes());
|
||||||
if (produced > 0) {
|
if (produced > 0) {
|
||||||
destination.writerIndex(destination.writerIndex() + produced);
|
destination.writerIndex(destination.writerIndex() + produced);
|
||||||
@ -70,8 +68,8 @@ public class LibdeflateVelocityCompressor implements VelocityCompressor {
|
|||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (!disposed) {
|
if (!disposed) {
|
||||||
inflate.free(inflateCtx);
|
NativeZlibInflate.free(inflateCtx);
|
||||||
deflate.free(deflateCtx);
|
NativeZlibDeflate.free(deflateCtx);
|
||||||
}
|
}
|
||||||
disposed = true;
|
disposed = true;
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ package com.velocitypowered.natives.compression;
|
|||||||
*/
|
*/
|
||||||
class NativeZlibDeflate {
|
class NativeZlibDeflate {
|
||||||
|
|
||||||
native long init(int level);
|
static native long init(int level);
|
||||||
|
|
||||||
native long free(long ctx);
|
static native long free(long ctx);
|
||||||
|
|
||||||
native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress,
|
static native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress,
|
||||||
int destinationLength);
|
int destinationLength);
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ import java.util.zip.DataFormatException;
|
|||||||
*/
|
*/
|
||||||
class NativeZlibInflate {
|
class NativeZlibInflate {
|
||||||
|
|
||||||
native long init();
|
static native long init();
|
||||||
|
|
||||||
native long free(long ctx);
|
static native long free(long ctx);
|
||||||
|
|
||||||
native boolean process(long ctx, long sourceAddress, int sourceLength, long destinationAddress,
|
static native boolean process(long ctx, long sourceAddress, int sourceLength,
|
||||||
int destinationLength) throws DataFormatException;
|
long destinationAddress, int destinationLength) throws DataFormatException;
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,11 @@ public class NativeVelocityCipher implements VelocityCipher {
|
|||||||
return new NativeVelocityCipher(false, key);
|
return new NativeVelocityCipher(false, key);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private static final OpenSslCipherImpl impl = new OpenSslCipherImpl();
|
|
||||||
|
|
||||||
private final long ctx;
|
private final long ctx;
|
||||||
private boolean disposed = false;
|
private boolean disposed = false;
|
||||||
|
|
||||||
private NativeVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
|
private NativeVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
|
||||||
this.ctx = impl.init(key.getEncoded(), encrypt);
|
this.ctx = OpenSslCipherImpl.init(key.getEncoded(), encrypt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -35,13 +33,13 @@ public class NativeVelocityCipher implements VelocityCipher {
|
|||||||
long base = source.memoryAddress() + source.readerIndex();
|
long base = source.memoryAddress() + source.readerIndex();
|
||||||
int len = source.readableBytes();
|
int len = source.readableBytes();
|
||||||
|
|
||||||
impl.process(ctx, base, len, base);
|
OpenSslCipherImpl.process(ctx, base, len, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (!disposed) {
|
if (!disposed) {
|
||||||
impl.free(ctx);
|
OpenSslCipherImpl.free(ctx);
|
||||||
}
|
}
|
||||||
disposed = true;
|
disposed = true;
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ import java.security.GeneralSecurityException;
|
|||||||
|
|
||||||
class OpenSslCipherImpl {
|
class OpenSslCipherImpl {
|
||||||
|
|
||||||
native long init(byte[] key, boolean encrypt) throws GeneralSecurityException;
|
static native long init(byte[] key, boolean encrypt) throws GeneralSecurityException;
|
||||||
|
|
||||||
native void process(long ctx, long source, int len, long dest);
|
static native void process(long ctx, long source, int len, long dest);
|
||||||
|
|
||||||
native void free(long ptr);
|
static native void free(long ptr);
|
||||||
}
|
}
|
||||||
|
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
@ -2,6 +2,7 @@ package com.velocitypowered.natives.encryption;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
import static org.junit.jupiter.api.condition.OS.LINUX;
|
||||||
|
|
||||||
import com.velocitypowered.natives.util.Natives;
|
import com.velocitypowered.natives.util.Natives;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
@ -13,6 +14,7 @@ import java.util.function.Supplier;
|
|||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||||
|
|
||||||
class VelocityCipherTest {
|
class VelocityCipherTest {
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ class VelocityCipherTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@EnabledOnOs({LINUX})
|
||||||
void nativeIntegrityCheck() throws GeneralSecurityException {
|
void nativeIntegrityCheck() throws GeneralSecurityException {
|
||||||
VelocityCipherFactory factory = Natives.cipher.get();
|
VelocityCipherFactory factory = Natives.cipher.get();
|
||||||
if (factory == JavaVelocityCipher.FACTORY) {
|
if (factory == JavaVelocityCipher.FACTORY) {
|
||||||
|
@ -415,8 +415,11 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
.toArray((IntFunction<CompletableFuture<Void>[]>) CompletableFuture[]::new));
|
.toArray((IntFunction<CompletableFuture<Void>[]>) CompletableFuture[]::new));
|
||||||
|
|
||||||
playersTeardownFuture.get(10, TimeUnit.SECONDS);
|
playersTeardownFuture.get(10, TimeUnit.SECONDS);
|
||||||
} catch (TimeoutException | ExecutionException e) {
|
} catch (TimeoutException e) {
|
||||||
timedOut = true;
|
timedOut = true;
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
timedOut = true;
|
||||||
|
logger.error("Exception while tearing down player connections", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
eventManager.fireShutdownEvent();
|
eventManager.fireShutdownEvent();
|
||||||
|
@ -3,19 +3,35 @@ package com.velocitypowered.proxy.command.builtin;
|
|||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.command.SimpleCommand;
|
import com.velocitypowered.api.command.SimpleCommand;
|
||||||
import com.velocitypowered.api.permission.Tristate;
|
import com.velocitypowered.api.permission.Tristate;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.util.ProxyVersion;
|
import com.velocitypowered.api.util.ProxyVersion;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
|
import com.velocitypowered.proxy.util.InformationUtils;
|
||||||
|
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import net.kyori.adventure.identity.Identity;
|
import net.kyori.adventure.identity.Identity;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.TextComponent;
|
import net.kyori.adventure.text.TextComponent;
|
||||||
@ -25,6 +41,10 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
|||||||
import net.kyori.adventure.text.format.TextDecoration;
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.asynchttpclient.AsyncHttpClient;
|
||||||
|
import org.asynchttpclient.BoundRequestBuilder;
|
||||||
|
import org.asynchttpclient.ListenableFuture;
|
||||||
|
import org.asynchttpclient.Response;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
public class VelocityCommand implements SimpleCommand {
|
public class VelocityCommand implements SimpleCommand {
|
||||||
@ -52,6 +72,7 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
.put("version", new Info(server))
|
.put("version", new Info(server))
|
||||||
.put("plugins", new Plugins(server))
|
.put("plugins", new Plugins(server))
|
||||||
.put("reload", new Reload(server))
|
.put("reload", new Reload(server))
|
||||||
|
.put("dump", new Dump(server))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,4 +310,140 @@ public class VelocityCommand implements SimpleCommand {
|
|||||||
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
|
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Dump implements SubCommand {
|
||||||
|
|
||||||
|
private static final Logger logger = LogManager.getLogger(Dump.class);
|
||||||
|
private final ProxyServer server;
|
||||||
|
|
||||||
|
private Dump(ProxyServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandSource source, String @NonNull [] args) {
|
||||||
|
if (args.length != 0) {
|
||||||
|
source.sendMessage(Identity.nil(), Component.text("/velocity dump", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<RegisteredServer> allServers = ImmutableSet.copyOf(server.getAllServers());
|
||||||
|
JsonObject servers = new JsonObject();
|
||||||
|
for (RegisteredServer iter : allServers) {
|
||||||
|
servers.add(iter.getServerInfo().getName(),
|
||||||
|
InformationUtils.collectServerInfo(iter));
|
||||||
|
}
|
||||||
|
JsonArray connectOrder = new JsonArray();
|
||||||
|
List<String> attemptedConnectionOrder = ImmutableList.copyOf(
|
||||||
|
server.getConfiguration().getAttemptConnectionOrder());
|
||||||
|
for (int i = 0; i < attemptedConnectionOrder.size(); i++) {
|
||||||
|
connectOrder.add(attemptedConnectionOrder.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject proxyConfig = InformationUtils.collectProxyConfig(server.getConfiguration());
|
||||||
|
proxyConfig.add("servers", servers);
|
||||||
|
proxyConfig.add("connectOrder", connectOrder);
|
||||||
|
proxyConfig.add("forcedHosts",
|
||||||
|
InformationUtils.collectForcedHosts(server.getConfiguration()));
|
||||||
|
|
||||||
|
JsonObject dump = new JsonObject();
|
||||||
|
dump.add("versionInfo", InformationUtils.collectProxyInfo(server.getVersion()));
|
||||||
|
dump.add("platform", InformationUtils.collectEnvironmentInfo());
|
||||||
|
dump.add("config", proxyConfig);
|
||||||
|
dump.add("plugins", InformationUtils.collectPluginInfo(server));
|
||||||
|
|
||||||
|
source.sendMessage(Component.text().content("Uploading gathered information...").build());
|
||||||
|
AsyncHttpClient httpClient = ((VelocityServer) server).getAsyncHttpClient();
|
||||||
|
|
||||||
|
BoundRequestBuilder request =
|
||||||
|
httpClient.preparePost("https://dump.velocitypowered.com/documents");
|
||||||
|
request.setHeader("Content-Type", "text/plain");
|
||||||
|
request.addHeader("User-Agent", server.getVersion().getName() + "/"
|
||||||
|
+ server.getVersion().getVersion());
|
||||||
|
request.setBody(
|
||||||
|
InformationUtils.toHumanReadableString(dump).getBytes(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
ListenableFuture<Response> future = request.execute();
|
||||||
|
future.addListener(() -> {
|
||||||
|
try {
|
||||||
|
Response response = future.get();
|
||||||
|
if (response.getStatusCode() != 200) {
|
||||||
|
source.sendMessage(Component.text()
|
||||||
|
.content("An error occurred while communicating with the Velocity servers. "
|
||||||
|
+ "The servers may be temporarily unavailable or there is an issue "
|
||||||
|
+ "with your network settings. You can find more information in the "
|
||||||
|
+ "log or console of your Velocity server.")
|
||||||
|
.color(NamedTextColor.RED).build());
|
||||||
|
logger.error("Invalid status code while POST-ing Velocity dump: "
|
||||||
|
+ response.getStatusCode());
|
||||||
|
logger.error("Headers: \n--------------BEGIN HEADERS--------------\n"
|
||||||
|
+ response.getHeaders().toString()
|
||||||
|
+ "\n---------------END HEADERS---------------");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JsonObject key = InformationUtils.parseString(
|
||||||
|
response.getResponseBody(StandardCharsets.UTF_8));
|
||||||
|
if (!key.has("key")) {
|
||||||
|
throw new JsonSyntaxException("Missing Dump-Url-response");
|
||||||
|
}
|
||||||
|
String url = "https://dump.velocitypowered.com/"
|
||||||
|
+ key.get("key").getAsString() + ".json";
|
||||||
|
source.sendMessage(Component.text()
|
||||||
|
.content("Created an anonymised report containing useful information about "
|
||||||
|
+ "this proxy. If a developer requested it, you may share the "
|
||||||
|
+ "following link with them:")
|
||||||
|
.append(Component.newline())
|
||||||
|
.append(Component.text(">> " + url)
|
||||||
|
.color(NamedTextColor.GREEN)
|
||||||
|
.clickEvent(ClickEvent.openUrl(url)))
|
||||||
|
.append(Component.newline())
|
||||||
|
.append(Component.text("Note: This link is only valid for a few days")
|
||||||
|
.color(NamedTextColor.GRAY)
|
||||||
|
).build());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
source.sendMessage(Component.text()
|
||||||
|
.content("Could not complete the request, the command was interrupted."
|
||||||
|
+ "Please refer to the proxy-log or console for more information.")
|
||||||
|
.color(NamedTextColor.RED).build());
|
||||||
|
logger.error("Failed to complete dump command, "
|
||||||
|
+ "the executor was interrupted: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
TextComponent.Builder message = Component.text()
|
||||||
|
.content("An error occurred while attempting to upload the gathered "
|
||||||
|
+ "information to the Velocity servers.")
|
||||||
|
.append(Component.newline())
|
||||||
|
.color(NamedTextColor.RED);
|
||||||
|
if (e.getCause() instanceof UnknownHostException
|
||||||
|
|| e.getCause() instanceof ConnectException) {
|
||||||
|
message.append(Component.text(
|
||||||
|
"Likely cause: Invalid system DNS settings or no internet connection"));
|
||||||
|
}
|
||||||
|
source.sendMessage(message
|
||||||
|
.append(Component.newline()
|
||||||
|
.append(Component.text(
|
||||||
|
"Error details can be found in the proxy log / console"))
|
||||||
|
).build());
|
||||||
|
|
||||||
|
logger.error("Failed to complete dump command, "
|
||||||
|
+ "the executor encountered an Exception: " + e.getCause().getMessage());
|
||||||
|
e.getCause().printStackTrace();
|
||||||
|
} catch (JsonParseException e) {
|
||||||
|
source.sendMessage(Component.text()
|
||||||
|
.content("An error occurred on the Velocity-servers and the dump could not "
|
||||||
|
+ "be completed. Please contact the Velocity staff about this problem. "
|
||||||
|
+ "If you do, provide the details about this error from the Velocity "
|
||||||
|
+ "console or server log.")
|
||||||
|
.color(NamedTextColor.RED).build());
|
||||||
|
logger.error("Invalid response from the Velocity servers: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}, MoreExecutors.directExecutor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(final CommandSource source, final String @NonNull [] args) {
|
||||||
|
return source.getPermissionValue("velocity.command.plugins") == Tristate.TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import com.electronwill.nightconfig.toml.TomlFormat;
|
|||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||||
import com.velocitypowered.api.util.Favicon;
|
import com.velocitypowered.api.util.Favicon;
|
||||||
import com.velocitypowered.proxy.util.AddressUtil;
|
import com.velocitypowered.proxy.util.AddressUtil;
|
||||||
@ -42,20 +43,20 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(VelocityConfiguration.class);
|
private static final Logger logger = LogManager.getLogger(VelocityConfiguration.class);
|
||||||
|
|
||||||
private String bind = "0.0.0.0:25577";
|
@Expose private String bind = "0.0.0.0:25577";
|
||||||
private String motd = "&3A Velocity Server";
|
@Expose private String motd = "&3A Velocity Server";
|
||||||
private int showMaxPlayers = 500;
|
@Expose private int showMaxPlayers = 500;
|
||||||
private boolean onlineMode = true;
|
@Expose private boolean onlineMode = true;
|
||||||
private boolean preventClientProxyConnections = false;
|
@Expose private boolean preventClientProxyConnections = false;
|
||||||
private PlayerInfoForwarding playerInfoForwardingMode = PlayerInfoForwarding.NONE;
|
@Expose private PlayerInfoForwarding playerInfoForwardingMode = PlayerInfoForwarding.NONE;
|
||||||
private byte[] forwardingSecret = generateRandomString(12).getBytes(StandardCharsets.UTF_8);
|
private byte[] forwardingSecret = generateRandomString(12).getBytes(StandardCharsets.UTF_8);
|
||||||
private boolean announceForge = false;
|
@Expose private boolean announceForge = false;
|
||||||
private boolean onlineModeKickExistingPlayers = false;
|
@Expose private boolean onlineModeKickExistingPlayers = false;
|
||||||
private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED;
|
@Expose private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED;
|
||||||
private final Servers servers;
|
private final Servers servers;
|
||||||
private final ForcedHosts forcedHosts;
|
private final ForcedHosts forcedHosts;
|
||||||
private final Advanced advanced;
|
@Expose private final Advanced advanced;
|
||||||
private final Query query;
|
@Expose private final Query query;
|
||||||
private final Metrics metrics;
|
private final Metrics metrics;
|
||||||
private final Messages messages;
|
private final Messages messages;
|
||||||
private net.kyori.adventure.text.@MonotonicNonNull Component motdAsComponent;
|
private net.kyori.adventure.text.@MonotonicNonNull Component motdAsComponent;
|
||||||
@ -612,18 +613,18 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
|
|
||||||
private static class Advanced {
|
private static class Advanced {
|
||||||
|
|
||||||
private int compressionThreshold = 256;
|
@Expose private int compressionThreshold = 256;
|
||||||
private int compressionLevel = -1;
|
@Expose private int compressionLevel = -1;
|
||||||
private int loginRatelimit = 3000;
|
@Expose private int loginRatelimit = 3000;
|
||||||
private int connectionTimeout = 5000;
|
@Expose private int connectionTimeout = 5000;
|
||||||
private int readTimeout = 30000;
|
@Expose private int readTimeout = 30000;
|
||||||
private boolean proxyProtocol = false;
|
@Expose private boolean proxyProtocol = false;
|
||||||
private boolean tcpFastOpen = false;
|
@Expose private boolean tcpFastOpen = false;
|
||||||
private boolean bungeePluginMessageChannel = true;
|
@Expose private boolean bungeePluginMessageChannel = true;
|
||||||
private boolean showPingRequests = false;
|
@Expose private boolean showPingRequests = false;
|
||||||
private boolean failoverOnUnexpectedServerDisconnect = true;
|
@Expose private boolean failoverOnUnexpectedServerDisconnect = true;
|
||||||
private boolean announceProxyCommands = true;
|
@Expose private boolean announceProxyCommands = true;
|
||||||
private boolean logCommandExecutions = false;
|
@Expose private boolean logCommandExecutions = false;
|
||||||
|
|
||||||
private Advanced() {
|
private Advanced() {
|
||||||
}
|
}
|
||||||
@ -715,10 +716,10 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
|
|
||||||
private static class Query {
|
private static class Query {
|
||||||
|
|
||||||
private boolean queryEnabled = false;
|
@Expose private boolean queryEnabled = false;
|
||||||
private int queryPort = 25577;
|
@Expose private int queryPort = 25577;
|
||||||
private String queryMap = "Velocity";
|
@Expose private String queryMap = "Velocity";
|
||||||
private boolean showPlugins = false;
|
@Expose private boolean showPlugins = false;
|
||||||
|
|
||||||
private Query() {
|
private Query() {
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
Unpooled.wrappedBuffer(copy));
|
Unpooled.wrappedBuffer(copy));
|
||||||
playerConnection.write(copied);
|
playerConnection.write(copied);
|
||||||
}
|
}
|
||||||
}, playerConnection.eventLoop());
|
}, playerConnection.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling plugin message {}", packet, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +190,11 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
server.getEventManager().fire(
|
server.getEventManager().fire(
|
||||||
new PlayerAvailableCommandsEvent(serverConn.getPlayer(), rootNode))
|
new PlayerAvailableCommandsEvent(serverConn.getPlayer(), rootNode))
|
||||||
.thenAcceptAsync(event -> playerConnection.write(commands), playerConnection.eventLoop());
|
.thenAcceptAsync(event -> playerConnection.write(commands), playerConnection.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling available commands for {}", playerConnection, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
|||||||
@SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "Most methods in this class open "
|
@SuppressFBWarnings(value = "OS_OPEN_STREAM", justification = "Most methods in this class open "
|
||||||
+ "instances of ByteBufDataOutput backed by heap-allocated ByteBufs. Closing them does "
|
+ "instances of ByteBufDataOutput backed by heap-allocated ByteBufs. Closing them does "
|
||||||
+ "nothing.")
|
+ "nothing.")
|
||||||
class BungeeCordMessageResponder {
|
public class BungeeCordMessageResponder {
|
||||||
|
|
||||||
private static final MinecraftChannelIdentifier MODERN_CHANNEL = MinecraftChannelIdentifier
|
private static final MinecraftChannelIdentifier MODERN_CHANNEL = MinecraftChannelIdentifier
|
||||||
.create("bungeecord", "main");
|
.create("bungeecord", "main");
|
||||||
@ -45,6 +45,11 @@ class BungeeCordMessageResponder {
|
|||||||
this.player = player;
|
this.player = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBungeeCordMessage(PluginMessage message) {
|
||||||
|
return MODERN_CHANNEL.getId().equals(message.getChannel()) && !LEGACY_CHANNEL.getId()
|
||||||
|
.equals(message.getChannel());
|
||||||
|
}
|
||||||
|
|
||||||
private void processConnect(ByteBufDataInput in) {
|
private void processConnect(ByteBufDataInput in) {
|
||||||
String serverName = in.readUTF();
|
String serverName = in.readUTF();
|
||||||
proxy.getServer(serverName).ifPresent(server -> player.createConnectionRequest(server)
|
proxy.getServer(serverName).ifPresent(server -> player.createConnectionRequest(server)
|
||||||
@ -332,8 +337,7 @@ class BungeeCordMessageResponder {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MODERN_CHANNEL.getId().equals(message.getChannel()) && !LEGACY_CHANNEL.getId()
|
if (!isBungeeCordMessage(message)) {
|
||||||
.equals(message.getChannel())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import com.velocitypowered.proxy.VelocityServer;
|
|||||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases;
|
import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases;
|
||||||
|
import com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder;
|
||||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
@ -157,7 +158,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
smc.write(packet);
|
smc.write(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, smc.eventLoop());
|
}, smc.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling player chat for {}", player, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -190,6 +195,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
} else if (PluginMessageUtil.isMcBrand(packet)) {
|
} else if (PluginMessageUtil.isMcBrand(packet)) {
|
||||||
backendConn.write(PluginMessageUtil
|
backendConn.write(PluginMessageUtil
|
||||||
.rewriteMinecraftBrand(packet, server.getVersion(), player.getProtocolVersion()));
|
.rewriteMinecraftBrand(packet, server.getVersion(), player.getProtocolVersion()));
|
||||||
|
} else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) {
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (serverConn.getPhase() == BackendConnectionPhases.IN_TRANSITION) {
|
if (serverConn.getPhase() == BackendConnectionPhases.IN_TRANSITION) {
|
||||||
// We must bypass the currently-connected server when forwarding Forge packets.
|
// We must bypass the currently-connected server when forwarding Forge packets.
|
||||||
@ -225,7 +232,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
Unpooled.wrappedBuffer(copy));
|
Unpooled.wrappedBuffer(copy));
|
||||||
backendConn.write(message);
|
backendConn.write(message);
|
||||||
}
|
}
|
||||||
}, backendConn.eventLoop());
|
}, backendConn.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling plugin message packet for {}",
|
||||||
|
player, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -423,7 +435,12 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
resp.getOffers().addAll(offers);
|
resp.getOffers().addAll(offers);
|
||||||
player.getConnection().write(resp);
|
player.getConnection().write(resp);
|
||||||
}
|
}
|
||||||
}, player.getConnection().eventLoop());
|
}, player.getConnection().eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling command tab completion for player {} executing {}",
|
||||||
|
player, command, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
return true; // Sorry, handler; we're just gonna have to lie to you here.
|
return true; // Sorry, handler; we're just gonna have to lie to you here.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,7 +492,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
player.getUsername(),
|
player.getUsername(),
|
||||||
command, e);
|
command, e);
|
||||||
}
|
}
|
||||||
}, player.getConnection().eventLoop());
|
}, player.getConnection().eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error(
|
||||||
|
"Exception while finishing command tab completion, with request {} and response {}",
|
||||||
|
request, response, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishRegularTabComplete(TabCompleteRequest request, TabCompleteResponse response) {
|
private void finishRegularTabComplete(TabCompleteRequest request, TabCompleteResponse response) {
|
||||||
@ -490,7 +513,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
response.getOffers().add(new Offer(s));
|
response.getOffers().add(new Offer(s));
|
||||||
}
|
}
|
||||||
player.getConnection().write(response);
|
player.getConnection().write(response);
|
||||||
}, player.getConnection().eventLoop());
|
}, player.getConnection().eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error(
|
||||||
|
"Exception while finishing regular tab completion, with request {} and response{}",
|
||||||
|
request, response, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<Void> processCommandExecuteResult(String originalCommand,
|
private CompletableFuture<Void> processCommandExecuteResult(String originalCommand,
|
||||||
|
@ -637,7 +637,13 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DisconnectEvent event = new DisconnectEvent(this, status);
|
DisconnectEvent event = new DisconnectEvent(this, status);
|
||||||
server.getEventManager().fire(event).thenRun(() -> this.teardownFuture.complete(null));
|
server.getEventManager().fire(event).whenComplete((val, ex) -> {
|
||||||
|
if (ex == null) {
|
||||||
|
this.teardownFuture.complete(null);
|
||||||
|
} else {
|
||||||
|
this.teardownFuture.completeExceptionally(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Void> getTeardownFuture() {
|
public CompletableFuture<Void> getTeardownFuture() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
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.backend.VelocityServerConnection;
|
||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
@ -25,6 +26,8 @@ public class InitialConnectSessionHandler implements MinecraftSessionHandler {
|
|||||||
player.getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
|
player.getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
|
||||||
} else if (PluginMessageUtil.isUnregister(packet)) {
|
} else if (PluginMessageUtil.isUnregister(packet)) {
|
||||||
player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
|
player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
|
||||||
|
} else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
serverConn.ensureConnected().write(packet.retain());
|
serverConn.ensureConnected().write(packet.retain());
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
} else {
|
} else {
|
||||||
initializePlayer(GameProfile.forOfflinePlayer(login.getUsername()), false);
|
initializePlayer(GameProfile.forOfflinePlayer(login.getUsername()), false);
|
||||||
}
|
}
|
||||||
}, mcConnection.eventLoop());
|
}, mcConnection.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception in pre-login stage", ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private EncryptionRequest generateEncryptionRequest() {
|
private EncryptionRequest generateEncryptionRequest() {
|
||||||
@ -202,6 +206,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
server.getConfiguration().getPlayerInfoForwardingMode());
|
server.getConfiguration().getPlayerInfoForwardingMode());
|
||||||
GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(inbound, profile,
|
GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(inbound, profile,
|
||||||
onlineMode);
|
onlineMode);
|
||||||
|
final GameProfile finalProfile = profile;
|
||||||
|
|
||||||
server.getEventManager().fire(profileRequestEvent).thenCompose(profileEvent -> {
|
server.getEventManager().fire(profileRequestEvent).thenCompose(profileEvent -> {
|
||||||
if (mcConnection.isClosed()) {
|
if (mcConnection.isClosed()) {
|
||||||
@ -229,6 +234,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
completeLoginProtocolPhaseAndInitialize(player);
|
completeLoginProtocolPhaseAndInitialize(player);
|
||||||
}
|
}
|
||||||
}, mcConnection.eventLoop());
|
}, mcConnection.eventLoop());
|
||||||
|
}).exceptionally((ex) -> {
|
||||||
|
logger.error("Exception during connection of {}", finalProfile, ex);
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +282,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
server.getEventManager().fire(new PostLoginEvent(player))
|
server.getEventManager().fire(new PostLoginEvent(player))
|
||||||
.thenRun(() -> connectToInitialServer(player));
|
.thenRun(() -> connectToInitialServer(player));
|
||||||
}
|
}
|
||||||
}, mcConnection.eventLoop());
|
}, mcConnection.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while completing login initialisation phase for {}", player, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectToInitialServer(ConnectedPlayer player) {
|
private void connectToInitialServer(ConnectedPlayer player) {
|
||||||
@ -291,7 +303,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player.createConnectionRequest(toTry.get()).fireAndForget();
|
player.createConnectionRequest(toTry.get()).fireAndForget();
|
||||||
}, mcConnection.eventLoop());
|
}, mcConnection.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while connecting {} to initial server", player, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -120,6 +120,10 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.getDescriptionComponent() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return new ServerPing(
|
return new ServerPing(
|
||||||
fallback.getVersion(),
|
fallback.getVersion(),
|
||||||
fallback.getPlayers().orElse(null),
|
fallback.getPlayers().orElse(null),
|
||||||
@ -163,7 +167,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
|
|||||||
.thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inbound, ping)))
|
.thenCompose(ping -> server.getEventManager().fire(new ProxyPingEvent(inbound, ping)))
|
||||||
.thenAcceptAsync(event -> connection.closeWith(
|
.thenAcceptAsync(event -> connection.closeWith(
|
||||||
LegacyDisconnect.fromServerPing(event.getPing(), packet.getVersion())),
|
LegacyDisconnect.fromServerPing(event.getPing(), packet.getVersion())),
|
||||||
connection.eventLoop());
|
connection.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling legacy ping {}", packet, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +197,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
|
|||||||
.toJson(event.getPing(), json);
|
.toJson(event.getPing(), json);
|
||||||
connection.write(new StatusResponse(json));
|
connection.write(new StatusResponse(json));
|
||||||
},
|
},
|
||||||
connection.eventLoop());
|
connection.eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling status request {}", packet, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,12 +113,10 @@ public class VelocityEventManager implements EventManager {
|
|||||||
return CompletableFuture.completedFuture(event);
|
return CompletableFuture.completedFuture(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<E> eventFuture = new CompletableFuture<>();
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
service.execute(() -> {
|
|
||||||
fireEvent(event);
|
fireEvent(event);
|
||||||
eventFuture.complete(event);
|
return event;
|
||||||
});
|
}, service);
|
||||||
return eventFuture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -30,6 +30,7 @@ import java.util.Optional;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
|
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket> {
|
public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket> {
|
||||||
|
|
||||||
@ -163,7 +164,12 @@ public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket>
|
|||||||
// Send the response
|
// Send the response
|
||||||
DatagramPacket responsePacket = new DatagramPacket(queryResponse, msg.sender());
|
DatagramPacket responsePacket = new DatagramPacket(queryResponse, msg.sender());
|
||||||
ctx.writeAndFlush(responsePacket, ctx.voidPromise());
|
ctx.writeAndFlush(responsePacket, ctx.voidPromise());
|
||||||
}, ctx.channel().eventLoop());
|
}, ctx.channel().eventLoop())
|
||||||
|
.exceptionally((ex) -> {
|
||||||
|
LogManager.getLogger(getClass()).error(
|
||||||
|
"Exception while writing GS4 response for query from {}", senderAddress, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
240
proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java
Normale Datei
240
proxy/src/main/java/com/velocitypowered/proxy/util/InformationUtils.java
Normale Datei
@ -0,0 +1,240 @@
|
|||||||
|
package com.velocitypowered.proxy.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
|
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||||
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
|
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||||
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
|
import com.velocitypowered.api.util.ProxyVersion;
|
||||||
|
|
||||||
|
import java.net.Inet4Address;
|
||||||
|
import java.net.Inet6Address;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum InformationUtils {
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a {@link JsonArray} containing basic information about all
|
||||||
|
* running plugins on the {@link ProxyServer} instance.
|
||||||
|
*
|
||||||
|
* @param proxy the proxy instance to retrieve from
|
||||||
|
* @return {@link JsonArray} containing zero or more {@link JsonObject}
|
||||||
|
*/
|
||||||
|
public static JsonArray collectPluginInfo(ProxyServer proxy) {
|
||||||
|
List<PluginContainer> allPlugins = ImmutableList.copyOf(
|
||||||
|
proxy.getPluginManager().getPlugins());
|
||||||
|
JsonArray plugins = new JsonArray();
|
||||||
|
|
||||||
|
for (PluginContainer plugin : allPlugins) {
|
||||||
|
PluginDescription desc = plugin.getDescription();
|
||||||
|
JsonObject current = new JsonObject();
|
||||||
|
current.addProperty("id", desc.getId());
|
||||||
|
if (desc.getName().isPresent()) {
|
||||||
|
current.addProperty("name", desc.getName().get());
|
||||||
|
}
|
||||||
|
if (desc.getVersion().isPresent()) {
|
||||||
|
current.addProperty("version", desc.getVersion().get());
|
||||||
|
}
|
||||||
|
if (!desc.getAuthors().isEmpty()) {
|
||||||
|
JsonArray authorsArray = new JsonArray();
|
||||||
|
for (String author : desc.getAuthors()) {
|
||||||
|
authorsArray.add(author);
|
||||||
|
}
|
||||||
|
current.add("authors", authorsArray);
|
||||||
|
}
|
||||||
|
if (desc.getDescription().isPresent()) {
|
||||||
|
current.addProperty("description", desc.getDescription().get());
|
||||||
|
}
|
||||||
|
if (desc.getUrl().isPresent()) {
|
||||||
|
current.addProperty("url", desc.getUrl().get());
|
||||||
|
}
|
||||||
|
if (!desc.getDependencies().isEmpty()) {
|
||||||
|
JsonArray dependencies = new JsonArray();
|
||||||
|
for (PluginDependency dependency : desc.getDependencies()) {
|
||||||
|
dependencies.add(dependency.getId());
|
||||||
|
}
|
||||||
|
current.add("dependencies", dependencies);
|
||||||
|
}
|
||||||
|
plugins.add(current);
|
||||||
|
}
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link JsonObject} containing information about the
|
||||||
|
* current environment the project is run under.
|
||||||
|
*
|
||||||
|
* @return {@link JsonObject} containing environment info
|
||||||
|
*/
|
||||||
|
public static JsonObject collectEnvironmentInfo() {
|
||||||
|
JsonObject envInfo = new JsonObject();
|
||||||
|
envInfo.addProperty("operatingSystemType", System.getProperty("os.name"));
|
||||||
|
envInfo.addProperty("operatingSystemVersion", System.getProperty("os.version"));
|
||||||
|
envInfo.addProperty("operatingSystemArchitecture", System.getProperty("os.arch"));
|
||||||
|
envInfo.addProperty("javaVersion", System.getProperty("java.version"));
|
||||||
|
envInfo.addProperty("javaVendor", System.getProperty("java.vendor"));
|
||||||
|
return envInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link JsonObject} containing information about the
|
||||||
|
* forced hosts of the {@link ProxyConfig} instance.
|
||||||
|
*
|
||||||
|
* @return {@link JsonArray} containing forced hosts
|
||||||
|
*/
|
||||||
|
public static JsonObject collectForcedHosts(ProxyConfig config) {
|
||||||
|
JsonObject forcedHosts = new JsonObject();
|
||||||
|
Map<String, List<String>> allForcedHosts = ImmutableMap.copyOf(
|
||||||
|
config.getForcedHosts());
|
||||||
|
for (Map.Entry<String, List<String>> entry : allForcedHosts.entrySet()) {
|
||||||
|
JsonArray host = new JsonArray();
|
||||||
|
for (int i = 0; i < entry.getValue().size(); i++) {
|
||||||
|
host.add(entry.getValue().get(i));
|
||||||
|
}
|
||||||
|
forcedHosts.add(entry.getKey(), host);
|
||||||
|
}
|
||||||
|
return forcedHosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Anonymises or redacts a given {@link InetAddress}
|
||||||
|
* public address bits.
|
||||||
|
*
|
||||||
|
* @param address The address to redact
|
||||||
|
* @return {@link String} address with public parts redacted
|
||||||
|
*/
|
||||||
|
public static String anonymizeInetAddress(InetAddress address) {
|
||||||
|
if (address instanceof Inet4Address) {
|
||||||
|
Inet4Address v4 = (Inet4Address) address;
|
||||||
|
if (v4.isAnyLocalAddress() || v4.isLoopbackAddress()
|
||||||
|
|| v4.isLinkLocalAddress()
|
||||||
|
|| v4.isSiteLocalAddress()) {
|
||||||
|
return address.getHostAddress();
|
||||||
|
} else {
|
||||||
|
byte[] addr = v4.getAddress();
|
||||||
|
return (addr[0] & 0xff) + "." + (addr[1] & 0xff) + ".XXX.XXX";
|
||||||
|
}
|
||||||
|
} else if (address instanceof Inet6Address) {
|
||||||
|
Inet6Address v6 = (Inet6Address) address;
|
||||||
|
if (v6.isAnyLocalAddress() || v6.isLoopbackAddress()
|
||||||
|
|| v6.isSiteLocalAddress()
|
||||||
|
|| v6.isSiteLocalAddress()) {
|
||||||
|
return address.getHostAddress();
|
||||||
|
} else {
|
||||||
|
String[] bits = v6.getHostAddress().split(":");
|
||||||
|
String ret = "";
|
||||||
|
boolean flag = false;
|
||||||
|
for (int iter = 0; iter < bits.length; iter++) {
|
||||||
|
if (flag) {
|
||||||
|
ret += ":X";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!bits[iter].equals("0")) {
|
||||||
|
if (iter == 0) {
|
||||||
|
ret = bits[iter];
|
||||||
|
} else {
|
||||||
|
ret = "::" + bits[iter];
|
||||||
|
}
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return address.getHostAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link JsonObject} containing most relevant
|
||||||
|
* information of the {@link RegisteredServer} for diagnosis.
|
||||||
|
*
|
||||||
|
* @param server the server to evaluate
|
||||||
|
* @return {@link JsonObject} containing server and diagnostic info
|
||||||
|
*/
|
||||||
|
public static JsonObject collectServerInfo(RegisteredServer server) {
|
||||||
|
JsonObject info = new JsonObject();
|
||||||
|
info.addProperty("currentPlayers", server.getPlayersConnected().size());
|
||||||
|
InetSocketAddress iaddr = server.getServerInfo().getAddress();
|
||||||
|
if (iaddr.isUnresolved()) {
|
||||||
|
// Greetings form Netty 4aa10db9
|
||||||
|
info.addProperty("host", iaddr.getHostString());
|
||||||
|
} else {
|
||||||
|
info.addProperty("host", anonymizeInetAddress(iaddr.getAddress()));
|
||||||
|
}
|
||||||
|
info.addProperty("port", iaddr.getPort());
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link JsonObject} containing information about the
|
||||||
|
* current environment the project is run under.
|
||||||
|
*
|
||||||
|
* @param version the proxy instance to retrieve from
|
||||||
|
* @return {@link JsonObject} containing environment info
|
||||||
|
*/
|
||||||
|
public static JsonObject collectProxyInfo(ProxyVersion version) {
|
||||||
|
return (JsonObject) serializeObject(version, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link JsonObject} containing most relevant
|
||||||
|
* information of the {@link ProxyConfig} for diagnosis.
|
||||||
|
*
|
||||||
|
* @param config the config instance to retrieve from
|
||||||
|
* @return {@link JsonObject} containing select config values
|
||||||
|
*/
|
||||||
|
public static JsonObject collectProxyConfig(ProxyConfig config) {
|
||||||
|
return (JsonObject) serializeObject(config, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a human-readable String from a {@link JsonElement}.
|
||||||
|
*
|
||||||
|
* @param json the {@link JsonElement} object
|
||||||
|
* @return the human-readable String
|
||||||
|
*/
|
||||||
|
public static String toHumanReadableString(JsonElement json) {
|
||||||
|
return GSON_WITHOUT_EXCLUDES.toJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link JsonObject} from a String.
|
||||||
|
*
|
||||||
|
* @param toParse the String to parse
|
||||||
|
* @return {@link JsonObject} object
|
||||||
|
*/
|
||||||
|
public static JsonObject parseString(String toParse) {
|
||||||
|
return GSON_WITHOUT_EXCLUDES.fromJson(toParse, JsonObject.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonElement serializeObject(Object toSerialize, boolean withExcludes) {
|
||||||
|
return JsonParser.parseString(
|
||||||
|
withExcludes ? GSON_WITH_EXCLUDES.toJson(toSerialize) :
|
||||||
|
GSON_WITHOUT_EXCLUDES.toJson(toSerialize));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Gson GSON_WITH_EXCLUDES = new GsonBuilder()
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.excludeFieldsWithoutExposeAnnotation()
|
||||||
|
.create();
|
||||||
|
|
||||||
|
private static final Gson GSON_WITHOUT_EXCLUDES = new GsonBuilder()
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.create();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren