Compare commits

...

38 Commits

Author SHA1 Message Date
Outfluencer
7340f1a035
#3765: Fix forgotten boolean write 2024-12-08 07:50:03 +11:00
md_5
8a80435e64
Minecraft 1.21.4 support 2024-12-04 03:20:00 +11:00
md_5
20a71b06a9
Minecraft 1.21.4-rc3 support 2024-11-30 10:11:33 +11:00
md_5
b376f61578
Minecraft 1.21.4-pre2 support 2024-11-26 20:38:03 +11:00
md_5
373dab05ad
Minecraft 1.21.4-pre1 support 2024-11-23 12:27:38 +11:00
Outfluencer
f6b40b1186
#3758: Handle LoginPayloadResponse in UpstreamBridge 2024-11-19 20:20:17 +11:00
Outfluencer
81b118a8ba
#3759: Remove unnecessary protocol version check for UnsignedClientCommand 2024-11-17 11:44:17 +11:00
BoomEaro
7a42f12716
#3760: Fix possible NPE when trying to get encoder/decoder protocol 2024-11-17 11:43:31 +11:00
md_5
4886c4be01
Minecraft 1.21.2 support 2024-10-23 02:15:00 +11:00
md_5
7338d0f444
Minecraft 1.21.2-rc2 support 2024-10-22 07:32:49 +11:00
Outfluencer
8212e10c7c
#3756, #3757: Queue PlayerListItemRemove packets for disconnecting players 2024-10-21 21:05:01 +11:00
md_5
2593130b3e
Minecraft 1.21.2-rc1 support 2024-10-19 08:56:06 +11:00
md_5
6ea49962c5
Minecraft 1.21.2-pre3 support 2024-10-13 09:53:59 +11:00
Outfluencer
672db9fe47
#3753, #3754: Don't disconnect during login if the player is on a server 2024-10-13 09:38:59 +11:00
Outfluencer
2bacf6572b
#3743: Fix infinite encrypting screen on miss configured ip-forwarding 2024-10-06 19:03:45 +11:00
Valentine
9813e46e66
#3746, #3666: Fix potential race conditions when connecting to multiple servers at the same time 2024-10-06 18:55:12 +11:00
Valentine
01a5f36012
#3751: Fix potential overriding of cipher by other libraries 2024-09-29 19:44:15 +10:00
md_5
f0a30c43cd
Minecraft 24w39a support 2024-09-28 08:51:09 +10:00
Outfluencer
acb85e30fa
#3742: Add more checks to InitialHandler 2024-09-21 09:05:50 +10:00
Outfluencer
9437cedc48
#3748: Minecraft 24w38a support 2024-09-21 09:02:28 +10:00
Outfluencer
a89cf5f36d
#3736: Add simple login payload API 2024-09-09 21:06:48 +10:00
Outfluencer
b309e4ac50
#3737: Use composite buffers where possible 2024-09-09 21:01:19 +10:00
md_5
477ea5983c
Remove unused field 2024-09-08 13:15:40 +10:00
Outfluencer
eca6090f1e
#3739: Support aarch64 natives 2024-09-08 09:15:02 +10:00
md_5
8f8c270f3b
Minecraft 24w36a support 2024-09-07 09:13:23 +10:00
md_5
84ac7ab944
Minecraft 24w35a support 2024-09-02 21:06:47 +10:00
Outfluencer
5fbcc6b119
#3732: Fix protocol state issue 2024-08-26 20:06:34 +10:00
Janmm14
79f85a2ce2
#3662: Add deprecation warning to ComponentSerializer.toString(Object)
It taking all objects is error-prone, deprecate it and create overloads
for used acceptable types.
2024-08-25 09:28:49 +10:00
Outfluencer
d32eedd333
#3727: 24w34a snapshot support 2024-08-25 09:15:03 +10:00
Outfluencer
e1d4b6adc7
#3731: Update cookie handling with vanilla limits and don't allow unrequested cookies 2024-08-25 09:10:00 +10:00
Outfluencer
534148763f
#3721: Improve same uuid and name checks
We didn't return so the login event was fired for a disconnected player
2024-08-22 19:25:45 +10:00
lax1dude
cd56fb32c2
#3722: Disable GZIP in native compress library (no longer requires PCLMUL) 2024-08-09 19:07:38 +10:00
lax1dude
6b612302e1
#3718, #3717: Add check for SSE 4.2 and PCLMUL support to native zlib 2024-08-08 18:19:20 +10:00
lax1dude
e49759025f
#3716, #3707: Fix native-cipher segfault when using musl libc 2024-08-08 18:19:20 +10:00
Outfluencer
c310e3339f
#3720: Replace some println calls with proxy logger 2024-08-07 19:57:09 +10:00
Nick
b64615e298
#3715: Fix maximum length for command packets 2024-07-28 21:07:49 +10:00
Raraph84
45d2f44003
#3713: Add default admin permissions for /find and /send 2024-07-28 21:04:41 +10:00
Raraph84
a57adcce00
#3711, #3712: Don't try to reconnect player when it disconnects manually
* Set server obsolete when disconnected by the proxy
2024-07-28 21:02:31 +10:00
60 changed files with 755 additions and 164 deletions

View File

@ -113,4 +113,18 @@ public interface PendingConnection extends Connection
*/
@ApiStatus.Experimental
CompletableFuture<byte[]> retrieveCookie(String cookie);
/**
* Sends a login payload request to the client.
*
* @param channel the channel to send this data via
* @param data the data to send
* @return a {@link CompletableFuture} that will be completed when the Login
* Payload response is received. If the Vanilla client doesn't know the
* channel, the {@link CompletableFuture} will complete with a null value
* @throws IllegalStateException if the player's version is not at least
* 1.13
*/
@ApiStatus.Experimental
CompletableFuture<byte[]> sendData(String channel, byte[] data);
}

View File

@ -22,7 +22,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<version>2.11.0</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@ -1,5 +1,6 @@
package net.md_5.bungee.api.chat;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
@ -129,6 +130,10 @@ public abstract class BaseComponent
{
setColor( component.getColorRaw() );
}
if ( replace || !style.hasShadowColor() )
{
setShadowColor( component.getShadowColorRaw() );
}
if ( replace || !style.hasFont() )
{
setFont( component.getFontRaw() );
@ -175,6 +180,7 @@ public abstract class BaseComponent
if ( retention == FormatRetention.EVENTS || retention == FormatRetention.NONE )
{
setColor( null );
setShadowColor( null );
setBold( null );
setItalic( null );
setUnderlined( null );
@ -295,6 +301,46 @@ public abstract class BaseComponent
return style.getColor();
}
/**
* Set this component's shadow color.
*
* @param color the component shadow color, or null to use the default
*/
public void setShadowColor(Color color)
{
this.style.setShadowColor( color );
}
/**
* Returns the shadow color of this component. This uses the parent's shadow color if this
* component doesn't have one. null is returned if no shadow color is found.
*
* @return the shadow color of this component
*/
public Color getShadowColor()
{
if ( !style.hasShadowColor() )
{
if ( parent == null )
{
return null;
}
return parent.getShadowColor();
}
return style.getShadowColor();
}
/**
* Returns the shadow color of this component without checking the parents
* shadow color. May return null
*
* @return the shadow color of this component
*/
public Color getShadowColorRaw()
{
return style.getShadowColor();
}
/**
* Set this component's font.
*
@ -536,6 +582,10 @@ public abstract class BaseComponent
{
setColor( style.getColor() );
}
if ( style.hasShadowColor() )
{
setShadowColor( style.getShadowColor() );
}
if ( style.hasFont() )
{
setFont( style.getFont() );

View File

@ -1,5 +1,6 @@
package net.md_5.bungee.api.chat;
import java.awt.Color;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@ -23,6 +24,10 @@ public final class ComponentStyle implements Cloneable
* {@link ChatColor#color} should not be null).</b>
*/
private ChatColor color;
/**
* The shadow color of this style.
*/
private Color shadowColor;
/**
* The font of this style.
*/
@ -68,6 +73,26 @@ public final class ComponentStyle implements Cloneable
return ( color != null );
}
/**
* Returns the shadow color of this style. May return null.
*
* @return the shadow color of this style, or null if default color
*/
public Color getShadowColor()
{
return shadowColor;
}
/**
* Returns whether or not this style has a shadow color set.
*
* @return whether a shadow color is set
*/
public boolean hasShadowColor()
{
return ( shadowColor != null );
}
/**
* Returns the font of this style. May return null.
*
@ -195,7 +220,7 @@ public final class ComponentStyle implements Cloneable
*/
public boolean isEmpty()
{
return color == null && font == null && bold == null
return color == null && shadowColor == null && font == null && bold == null
&& italic == null && underlined == null
&& strikethrough == null && obfuscated == null;
}
@ -203,7 +228,7 @@ public final class ComponentStyle implements Cloneable
@Override
public ComponentStyle clone()
{
return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated );
}
/**
@ -227,6 +252,7 @@ public final class ComponentStyle implements Cloneable
{
return new ComponentStyleBuilder()
.color( other.color )
.shadowColor( other.shadowColor )
.font( other.font )
.bold( other.bold )
.italic( other.italic )

View File

@ -1,5 +1,6 @@
package net.md_5.bungee.api.chat;
import java.awt.Color;
import net.md_5.bungee.api.ChatColor;
/**
@ -26,6 +27,7 @@ public final class ComponentStyleBuilder
{
private ChatColor color;
private Color shadowColor;
private String font;
private Boolean bold, italic, underlined, strikethrough, obfuscated;
@ -41,6 +43,18 @@ public final class ComponentStyleBuilder
return this;
}
/**
* Set the style shadow color.
*
* @param shadowColor the shadow color to set, or null to use the default
* @return this ComponentStyleBuilder for chaining
*/
public ComponentStyleBuilder shadowColor(Color shadowColor)
{
this.shadowColor = shadowColor;
return this;
}
/**
* Set the style font.
*
@ -121,6 +135,6 @@ public final class ComponentStyleBuilder
*/
public ComponentStyle build()
{
return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated );
return new ComponentStyle( color, shadowColor, font, bold, italic, underlined, strikethrough, obfuscated );
}
}

View File

@ -20,6 +20,7 @@ import net.md_5.bungee.api.chat.ScoreComponent;
import net.md_5.bungee.api.chat.SelectorComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import net.md_5.bungee.api.chat.hover.content.Content;
import net.md_5.bungee.api.chat.hover.content.Entity;
import net.md_5.bungee.api.chat.hover.content.EntitySerializer;
import net.md_5.bungee.api.chat.hover.content.Item;
@ -158,11 +159,28 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent>
return gson.toJsonTree( style );
}
/**
* @param object the object to serialize
* @return the JSON string representation of the object
* @deprecated Error-prone, be careful which object you input here
*/
@Deprecated
public static String toString(Object object)
{
return gson.toJson( object );
}
/**
* @param content the content to serialize
* @return the JSON string representation of the object
* @deprecated for legacy internal use only
*/
@Deprecated
public static String toString(Content content)
{
return gson.toJson( content );
}
public static String toString(BaseComponent component)
{
return gson.toJson( component );

View File

@ -1,5 +1,6 @@
package net.md_5.bungee.chat;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
@ -8,6 +9,7 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.awt.Color;
import java.lang.reflect.Type;
import java.util.Map;
import net.md_5.bungee.api.ChatColor;
@ -67,6 +69,10 @@ public class ComponentStyleSerializer implements JsonSerializer<ComponentStyle>,
{
object.addProperty( "color", style.getColor().getName() );
}
if ( style.hasShadowColor() )
{
object.addProperty( "shadow_color", style.getShadowColor().getRGB() );
}
if ( style.hasFont() )
{
object.addProperty( "font", style.getFont() );
@ -102,6 +108,17 @@ public class ComponentStyleSerializer implements JsonSerializer<ComponentStyle>,
case "color":
builder.color( ChatColor.of( value.getAsString() ) );
break;
case "shadow_color":
if ( value.isJsonArray() )
{
JsonArray array = value.getAsJsonArray();
builder.shadowColor( new Color( array.get( 0 ).getAsFloat(), array.get( 1 ).getAsFloat(), array.get( 2 ).getAsFloat(), array.get( 3 ).getAsFloat() ) );
} else if ( value.isJsonPrimitive() )
{
builder.shadowColor( new Color( value.getAsNumber().intValue(), true ) );
}
break;
case "font":
builder.font( value.getAsString() );
break;

View File

@ -22,7 +22,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<version>2.11.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>

29
native/compile-native-arm.sh Executable file
View File

@ -0,0 +1,29 @@
#!/bin/sh
set -eu
CWD=$(pwd)
echo "Compiling mbedtls"
(cd mbedtls && CFLAGS="-fPIC -I$CWD/src/main/c -DMBEDTLS_USER_CONFIG_FILE='<mbedtls_custom_config.h>'" make CC=aarch64-linux-gnu-gcc AR=aarch64-linux-gnu-ar no_test)
echo "Compiling zlib"
(cd zlib && CFLAGS="-fPIC -DNO_GZIP" CC=aarch64-linux-gnu-gcc CHOST=arm64 ./configure --target="aarch64" --static && make CFLAGS="-fPIC -march=armv8-a+crc" CC=aarch64-linux-gnu-gcc AR=aarch64-linux-gnu-ar)
CC="aarch64-linux-gnu-gcc"
CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/"
LDFLAGS="-shared"
echo "Compiling bungee"
$CC $CFLAGS -o shared.o src/main/c/shared.c
$CC $CFLAGS -Imbedtls/include -o NativeCipherImpl.o src/main/c/NativeCipherImpl.c
$CC $CFLAGS -Izlib -o NativeCompressImpl.o src/main/c/NativeCompressImpl.c
echo "Linking native-cipher-arm.so"
$CC $LDFLAGS -o src/main/resources/native-cipher-arm.so shared.o NativeCipherImpl.o mbedtls/library/libmbedcrypto.a
echo "Linking native-compress-arm.so"
$CC $LDFLAGS -o src/main/resources/native-compress-arm.so shared.o NativeCompressImpl.o zlib/libz.a
echo "Cleaning up"
rm shared.o NativeCipherImpl.o NativeCompressImpl.o

View File

@ -2,11 +2,13 @@
set -eu
CWD=$(pwd)
echo "Compiling mbedtls"
(cd mbedtls && make no_test)
(cd mbedtls && CFLAGS="-fPIC -I$CWD/src/main/c -DMBEDTLS_USER_CONFIG_FILE='<mbedtls_custom_config.h>'" make no_test)
echo "Compiling zlib"
(cd zlib && CFLAGS=-fPIC ./configure --static && make)
(cd zlib && CFLAGS="-fPIC -DNO_GZIP" ./configure --static && make)
CC="gcc"
CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/"

View File

@ -5,11 +5,15 @@
#include "shared.h"
#include "net_md_5_bungee_jni_cipher_NativeCipherImpl.h"
// Hack to keep the compiler from optimizing the memset away
static void *(*const volatile memset_func)(void *, int, size_t) = memset;
typedef unsigned char byte;
typedef struct crypto_context {
int mode;
mbedtls_aes_context cipher;
int keyLen;
byte key[];
} crypto_context;
@ -22,6 +26,7 @@ jlong JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_init(JNIEnv* env
return 0;
}
crypto->keyLen = (int) keyLen;
(*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) &crypto->key);
mbedtls_aes_init(&crypto->cipher);
@ -36,6 +41,7 @@ void Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_free(JNIEnv* env, jobject
crypto_context *crypto = (crypto_context*) ctx;
mbedtls_aes_free(&crypto->cipher);
memset_func(crypto->key, 0, (size_t) crypto->keyLen);
free(crypto);
}

View File

@ -3,6 +3,9 @@
#include <zlib.h>
#include "shared.h"
#if !defined(__aarch64__)
#include "cpuid_helper.h"
#endif
#include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h"
typedef unsigned char byte;
@ -26,6 +29,14 @@ jint throwException(JNIEnv *env, const char* message, int err) {
return (*env)->Throw(env, throwable);
}
JNIEXPORT jboolean JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_checkSupported(JNIEnv* env, jobject obj) {
#if !defined(__aarch64__)
return (jboolean) checkCompressionNativesSupport();
#else
return JNI_TRUE;
#endif
}
void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) {
z_stream* stream = (z_stream*) ctx;
int ret = (compress) ? deflateReset(stream) : inflateReset(stream);

View File

@ -0,0 +1,19 @@
// Header to check for SSE 2, SSSE 3, and SSE 4.2 support in compression natives
// GCC only!
#ifndef _INCLUDE_CPUID_HELPER_H
#define _INCLUDE_CPUID_HELPER_H
#include <stdbool.h>
#include <cpuid.h>
static inline bool checkCompressionNativesSupport() {
unsigned int eax, ebx, ecx, edx;
if(__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
return (edx & bit_SSE2) != 0 && (ecx & bit_SSSE3) != 0 && (ecx & bit_SSE4_2) != 0;
}else {
return false;
}
}
#endif // _INCLUDE_CPUID_HELPER_H

View File

@ -0,0 +1,31 @@
// This is a hack to deal with a glitch that happens when mbedtls is compiled against glibc
// but then run on a linux distro that uses musl libc. This implementation of the zeroize
// is compatible with both glibc and musl without requiring the library to be recompiled.
// I checked with a disassembler and for BungeeCord's usage of the library, implementing
// this function as a static function only resulted in 2 different subroutines referencing
// different versions of memset_func, so we might as well keep things simple and use a
// static function here instead of requiring the mbedtls makefile to be modified to add
// additional source files.
#ifndef _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H
#define _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H
#include <string.h>
#define MBEDTLS_PLATFORM_ZEROIZE_ALT
#define mbedtls_platform_zeroize mbedtls_platform_zeroize_impl
// hack to prevent compilers from optimizing the memset away
static void *(*const volatile memset_func)(void *, int, size_t) = memset;
static void mbedtls_platform_zeroize_impl(void *buf, size_t len) {
if (len > 0) {
memset_func(buf, 0, len);
}
}
#endif // _INCLUDE_MBEDTLS_CUSTOM_CONFIG_H

View File

@ -15,6 +15,14 @@ extern "C" {
JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields
(JNIEnv *, jclass);
/*
* Class: net_md_5_bungee_jni_zlib_NativeCompressImpl
* Method: checkSupported
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_checkSupported
(JNIEnv *, jobject);
/*
* Class: net_md_5_bungee_jni_zlib_NativeCompressImpl
* Method: end

View File

@ -15,14 +15,23 @@ public final class NativeCode<T>
private final String name;
private final Supplier<? extends T> javaImpl;
private final Supplier<? extends T> nativeImpl;
private final boolean enableNativeFlag;
private final boolean extendedSupportCheck;
//
private boolean loaded;
public NativeCode(String name, Supplier<? extends T> javaImpl, Supplier<? extends T> nativeImpl)
{
this( name, javaImpl, nativeImpl, false );
}
public NativeCode(String name, Supplier<? extends T> javaImpl, Supplier<? extends T> nativeImpl, boolean extendedSupportCheck)
{
this.name = name;
this.javaImpl = javaImpl;
this.nativeImpl = nativeImpl;
this.enableNativeFlag = Boolean.parseBoolean( System.getProperty( "net.md_5.bungee.jni." + name + ".enable", "true" ) );
this.extendedSupportCheck = extendedSupportCheck;
}
public T newInstance()
@ -32,8 +41,9 @@ public final class NativeCode<T>
public boolean load()
{
if ( !loaded && isSupported() )
if ( enableNativeFlag && !loaded && isSupported() )
{
String name = this.name + ( isAarch64() ? "-arm" : "" );
String fullName = "bungeecord-" + name;
try
@ -59,6 +69,13 @@ public final class NativeCode<T>
}
System.load( temp.getPath() );
if ( extendedSupportCheck )
{
// Should throw NativeCodeException if incompatible
nativeImpl.get();
}
loaded = true;
} catch ( IOException ex )
{
@ -66,6 +83,9 @@ public final class NativeCode<T>
} catch ( UnsatisfiedLinkError ex )
{
System.out.println( "Could not load native library: " + ex.getMessage() );
} catch ( NativeCodeException ex )
{
System.out.println( "Native library " + name + " is incompatible: " + ex.getMessage() );
}
}
}
@ -75,6 +95,16 @@ public final class NativeCode<T>
public static boolean isSupported()
{
return "Linux".equals( System.getProperty( "os.name" ) ) && "amd64".equals( System.getProperty( "os.arch" ) );
return "Linux".equals( System.getProperty( "os.name" ) ) && ( isAmd64() || isAarch64() );
}
private static boolean isAmd64()
{
return "amd64".equals( System.getProperty( "os.arch" ) );
}
private static boolean isAarch64()
{
return "aarch64".equals( System.getProperty( "os.arch" ) );
}
}

View File

@ -7,4 +7,10 @@ public class NativeCodeException extends RuntimeException
{
super( message + " : " + reason );
}
public NativeCodeException(String message)
{
super( message );
}
}

View File

@ -18,4 +18,10 @@ public interface BungeeCipher
void cipher(ByteBuf in, ByteBuf out) throws GeneralSecurityException;
ByteBuf cipher(ChannelHandlerContext ctx, ByteBuf in) throws GeneralSecurityException;
/*
* This indicates whether the input ByteBuf is allowed to be a CompositeByteBuf.
* If you need access to a memory address, you should not allow composite buffers.
*/
boolean allowComposite();
}

View File

@ -2,6 +2,7 @@ package net.md_5.bungee.jni.cipher;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.FastThreadLocal;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
@ -12,10 +13,10 @@ public class JavaCipher implements BungeeCipher
{
private final Cipher cipher;
private static final ThreadLocal<byte[]> heapInLocal = new EmptyByteThreadLocal();
private static final ThreadLocal<byte[]> heapOutLocal = new EmptyByteThreadLocal();
private static final FastThreadLocal<byte[]> heapInLocal = new EmptyByteThreadLocal();
private static final FastThreadLocal<byte[]> heapOutLocal = new EmptyByteThreadLocal();
private static class EmptyByteThreadLocal extends ThreadLocal<byte[]>
private static class EmptyByteThreadLocal extends FastThreadLocal<byte[]>
{
@Override
@ -88,4 +89,10 @@ public class JavaCipher implements BungeeCipher
in.readBytes( heapIn, 0, readableBytes );
return heapIn;
}
@Override
public boolean allowComposite()
{
return true;
}
}

View File

@ -71,4 +71,10 @@ public class NativeCipher implements BungeeCipher
return heapOut;
}
@Override
public boolean allowComposite()
{
return false;
}
}

View File

@ -6,9 +6,17 @@ import java.util.zip.DataFormatException;
public interface BungeeZlib
{
public static final int OUTPUT_BUFFER_SIZE = 8192;
void init(boolean compress, int level);
void free();
void process(ByteBuf in, ByteBuf out) throws DataFormatException;
/*
* This indicates whether the input ByteBuf is allowed to be a CompositeByteBuf.
* If you need access to a memory address, you should not allow composite buffers.
*/
boolean allowComposite();
}

View File

@ -73,4 +73,10 @@ public class JavaZlib implements BungeeZlib
inflater.reset();
}
}
@Override
public boolean allowComposite()
{
return true;
}
}

View File

@ -15,6 +15,8 @@ public class NativeCompressImpl
static native void initFields();
native boolean checkSupported();
native void end(long ctx, boolean compress);
native void reset(long ctx, boolean compress);

View File

@ -15,6 +15,14 @@ public class NativeZlib implements BungeeZlib
private boolean compress;
private long ctx;
public NativeZlib()
{
if ( !nativeCompress.checkSupported() )
{
throw new NativeCodeException( "This CPU does not support the required SSE 4.2 and/or PCLMUL extensions!" );
}
}
@Override
public void init(boolean compress, int level)
{
@ -47,7 +55,7 @@ public class NativeZlib implements BungeeZlib
while ( !nativeCompress.finished && ( compress || in.isReadable() ) )
{
out.ensureWritable( 8192 );
out.ensureWritable( OUTPUT_BUFFER_SIZE );
int processed;
try
@ -66,4 +74,10 @@ public class NativeZlib implements BungeeZlib
nativeCompress.consumed = 0;
nativeCompress.finished = false;
}
@Override
public boolean allowComposite()
{
return false;
}
}

Binary file not shown.

Binary file not shown.

View File

@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test;
public class NativeZlibTest
{
private final NativeCode<BungeeZlib> factory = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new );
private final NativeCode<BungeeZlib> factory = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new, true );
@Test
public void doTest() throws DataFormatException

View File

@ -83,7 +83,7 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-bom</artifactId>
<version>4.1.110.Final</version>
<version>4.1.115.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@ -99,7 +99,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
<version>33.3.1-jre</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -499,7 +499,7 @@ public abstract class DefinedPacket
public static BitSet readFixedBitSet(int i, ByteBuf buf)
{
byte[] bits = new byte[ ( i + 8 ) >> 3 ];
byte[] bits = new byte[ ( i + 7 ) >> 3 ];
buf.readBytes( bits );
return BitSet.valueOf( bits );
@ -511,7 +511,7 @@ public abstract class DefinedPacket
{
throw new OverflowPacketException( "BitSet too large (expected " + size + " got " + bits.size() + ")" );
}
buf.writeBytes( Arrays.copyOf( bits.toByteArray(), ( size + 8 ) >> 3 ) );
buf.writeBytes( Arrays.copyOf( bits.toByteArray(), ( size + 7 ) >> 3 ) );
}
public void read(ByteBuf buf)

View File

@ -98,7 +98,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x23 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x24 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x26 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x26 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x27 )
);
TO_CLIENT.registerPacket(
Login.class,
@ -115,7 +116,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x24 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x28 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x29 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x2B )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x2B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x2C )
);
TO_CLIENT.registerPacket( Chat.class,
Chat::new,
@ -146,7 +148,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x41 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x43 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x45 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x47 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x4C )
);
TO_CLIENT.registerPacket(
BossBar.class,
@ -206,7 +209,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x58 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5A ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x5E ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x64 )
);
TO_CLIENT.registerPacket(
ScoreboardScore.class,
@ -224,13 +228,15 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5B ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5D ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x68 )
);
TO_CLIENT.registerPacket(
ScoreboardScoreReset.class,
ScoreboardScoreReset::new,
map( ProtocolConstants.MINECRAFT_1_20_3, 0x42 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x44 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x49 )
);
TO_CLIENT.registerPacket(
ScoreboardDisplay.class,
@ -248,7 +254,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x51 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x53 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x55 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x57 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x5C )
);
TO_CLIENT.registerPacket(
Team.class,
@ -266,7 +273,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5A ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5C ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x5E ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x60 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x67 )
);
TO_CLIENT.registerPacket(
PluginMessage.class,
@ -321,7 +329,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x63 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x65 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6C )
);
TO_CLIENT.registerPacket(
ClearTitles.class,
@ -342,7 +351,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x5D ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x61 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x63 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6A )
);
TO_CLIENT.registerPacket(
TitleTimes.class,
@ -354,7 +364,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x60 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x62 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x64 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x66 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x6D )
);
TO_CLIENT.registerPacket(
SystemChat.class,
@ -365,7 +376,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x64 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x67 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x69 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6C ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x73 )
);
TO_CLIENT.registerPacket(
PlayerListHeaderFooter.class,
@ -387,7 +399,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x65 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x68 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x6A ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6D ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x74 )
);
TO_CLIENT.registerPacket(
EntityStatus.class,
@ -432,7 +445,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x1C ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x20 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x22 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x22 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x23 )
);
TO_CLIENT.registerPacket(
ViewDistance.class,
@ -447,7 +461,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x4F ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x51 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x53 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x55 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x55 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x59 )
);
TO_CLIENT.registerPacket(
ServerData.class,
@ -458,7 +473,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x45 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x47 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x49 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x4B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x50 )
);
TO_CLIENT.registerPacket(
PlayerListItemRemove.class,
@ -466,7 +482,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x35 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x39 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3B ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3D ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x3F )
);
TO_CLIENT.registerPacket(
PlayerListItemUpdate.class,
@ -474,14 +491,16 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x36 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x3A ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x3C ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x3E ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x40 )
);
TO_CLIENT.registerPacket(
StartConfiguration.class,
StartConfiguration::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x65 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x67 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x69 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x70 )
);
TO_CLIENT.registerPacket(
CookieRequest.class,
@ -491,22 +510,26 @@ public enum Protocol
TO_CLIENT.registerPacket(
StoreCookie.class,
StoreCookie::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x6B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x72 )
);
TO_CLIENT.registerPacket(
Transfer.class,
Transfer::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x73 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x73 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x7A )
);
TO_CLIENT.registerPacket(
DisconnectReportDetails.class,
DisconnectReportDetails::new,
map( ProtocolConstants.MINECRAFT_1_21, 0x7A )
map( ProtocolConstants.MINECRAFT_1_21, 0x7A ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x81 )
);
TO_CLIENT.registerPacket(
ServerLinks.class,
ServerLinks::new,
map( ProtocolConstants.MINECRAFT_1_21, 0x7B )
map( ProtocolConstants.MINECRAFT_1_21, 0x7B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x82 )
);
TO_SERVER.registerPacket(
@ -526,7 +549,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x12 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x14 ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x15 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x18 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x18 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x1A )
);
TO_SERVER.registerPacket( Chat.class,
Chat::new,
@ -542,19 +566,23 @@ public enum Protocol
ClientCommand::new,
map( ProtocolConstants.MINECRAFT_1_19, 0x03 ),
map( ProtocolConstants.MINECRAFT_1_19_1, 0x04 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x05 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x05 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x06 )
);
TO_SERVER.registerPacket(
UnsignedClientCommand.class,
UnsignedClientCommand::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x04 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x04 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x05 )
);
TO_SERVER.registerPacket(
ClientChat.class,
ClientChat::new,
map( ProtocolConstants.MINECRAFT_1_19, 0x04 ),
map( ProtocolConstants.MINECRAFT_1_19_1, 0x05 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x06 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x06 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x07 )
);
TO_SERVER.registerPacket(
TabCompleteRequest.class,
@ -570,7 +598,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x08 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x09 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0A ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x0B )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x0B ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x0D )
);
TO_SERVER.registerPacket(
ClientSettings.class,
@ -585,7 +614,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_3, 0x07 ),
map( ProtocolConstants.MINECRAFT_1_19_4, 0x08 ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x09 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x0A )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x0A ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x0C )
);
TO_SERVER.registerPacket(
PluginMessage.class,
@ -603,18 +633,21 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_19_4, 0x0D ),
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_20_3, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x12 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x12 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x14 )
);
TO_SERVER.registerPacket(
StartConfiguration.class,
StartConfiguration::new,
map( ProtocolConstants.MINECRAFT_1_20_2, 0x0B ),
map( ProtocolConstants.MINECRAFT_1_20_5, 0x0C )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x0C ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x0E )
);
TO_SERVER.registerPacket(
CookieResponse.class,
CookieResponse::new,
map( ProtocolConstants.MINECRAFT_1_20_5, 0x11 )
map( ProtocolConstants.MINECRAFT_1_20_5, 0x11 ),
map( ProtocolConstants.MINECRAFT_1_21_2, 0x13 )
);
}
},

View File

@ -47,6 +47,8 @@ public class ProtocolConstants
public static final int MINECRAFT_1_20_3 = 765;
public static final int MINECRAFT_1_20_5 = 766;
public static final int MINECRAFT_1_21 = 767;
public static final int MINECRAFT_1_21_2 = 768;
public static final int MINECRAFT_1_21_4 = 769;
public static final List<String> SUPPORTED_VERSIONS;
public static final List<Integer> SUPPORTED_VERSION_IDS;
@ -108,13 +110,15 @@ public class ProtocolConstants
ProtocolConstants.MINECRAFT_1_20_2,
ProtocolConstants.MINECRAFT_1_20_3,
ProtocolConstants.MINECRAFT_1_20_5,
ProtocolConstants.MINECRAFT_1_21
ProtocolConstants.MINECRAFT_1_21,
ProtocolConstants.MINECRAFT_1_21_2,
ProtocolConstants.MINECRAFT_1_21_4
);
if ( SNAPSHOT_SUPPORT )
{
// supportedVersions.add( "1.21.x" );
// supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21 );
// supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_21_4 );
}
SUPPORTED_VERSIONS = supportedVersions.build();

View File

@ -1,27 +1,38 @@
package net.md_5.bungee.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
import lombok.Setter;
/**
* Prepend length of the message as a Varint21 by writing length and data to a
* new buffer
*/
@ChannelHandler.Sharable
public class Varint21LengthFieldPrepender extends MessageToByteEncoder<ByteBuf>
public class Varint21LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf>
{
@Setter
private boolean compose = true;
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> list) throws Exception
{
int bodyLen = msg.readableBytes();
int headerLen = varintSize( bodyLen );
out.ensureWritable( headerLen + bodyLen );
DefinedPacket.writeVarInt( bodyLen, out );
out.writeBytes( msg );
if ( compose )
{
ByteBuf buf = ctx.alloc().directBuffer( headerLen );
DefinedPacket.writeVarInt( bodyLen, buf );
list.add( ctx.alloc().compositeDirectBuffer( 2 ).addComponents( true, buf, msg.retain() ) );
} else
{
ByteBuf buf = ctx.alloc().directBuffer( headerLen + bodyLen );
DefinedPacket.writeVarInt( bodyLen, buf );
buf.writeBytes( msg );
list.add( buf );
}
}
static int varintSize(int paramInt)

View File

@ -32,7 +32,7 @@ public class ClientCommand extends DefinedPacket
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
command = readString( buf, 256 );
command = readString( buf, ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 ) ? 32767 : 256 );
timestamp = buf.readLong();
salt = buf.readLong();

View File

@ -25,6 +25,7 @@ public class ClientSettings extends DefinedPacket
private int mainHand;
private boolean disableTextFiltering;
private boolean allowServerListing;
private ParticleStatus particleStatus;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@ -46,6 +47,10 @@ public class ClientSettings extends DefinedPacket
{
allowServerListing = buf.readBoolean();
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 )
{
particleStatus = ParticleStatus.values()[readVarInt( buf )];
}
}
@Override
@ -74,6 +79,10 @@ public class ClientSettings extends DefinedPacket
{
buf.writeBoolean( allowServerListing );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 )
{
writeVarInt( particleStatus.ordinal(), buf );
}
}
@Override
@ -81,4 +90,11 @@ public class ClientSettings extends DefinedPacket
{
handler.handle( this );
}
public enum ParticleStatus
{
ALL,
DECREASED,
MINIMAL;
}
}

View File

@ -23,7 +23,7 @@ public class CookieResponse extends DefinedPacket
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
cookie = readString( buf );
data = readNullable( DefinedPacket::readArray, buf );
data = readNullable( read -> DefinedPacket.readArray( read, 5120 ), buf );
}
@Override

View File

@ -45,6 +45,10 @@ public class EncryptionResponse extends DefinedPacket
writeArray( verifyToken, buf );
} else
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 && protocolVersion <= ProtocolConstants.MINECRAFT_1_19_3 )
{
buf.writeBoolean( false );
}
buf.writeLong( encryptionData.getSalt() );
writeArray( encryptionData.getSignature(), buf );
}

View File

@ -41,6 +41,7 @@ public class Login extends DefinedPacket
private boolean flat;
private Location deathLocation;
private int portalCooldown;
private int seaLevel;
private boolean secureProfile;
@Override
@ -161,7 +162,10 @@ public class Login extends DefinedPacket
{
portalCooldown = readVarInt( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 )
{
seaLevel = readVarInt( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
{
secureProfile = buf.readBoolean();
@ -293,7 +297,10 @@ public class Login extends DefinedPacket
{
writeVarInt( portalCooldown, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 )
{
writeVarInt( seaLevel, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
{
buf.writeBoolean( secureProfile );

View File

@ -37,7 +37,7 @@ public class LoginSuccess extends DefinedPacket
{
properties = readProperties( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 && protocolVersion < ProtocolConstants.MINECRAFT_1_21_2 )
{
// Whether the client should disconnect on its own if it receives invalid data from the server
buf.readBoolean();
@ -59,7 +59,7 @@ public class LoginSuccess extends DefinedPacket
{
writeProperties( properties, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_5 && protocolVersion < ProtocolConstants.MINECRAFT_1_21_2 )
{
// Whether the client should disconnect on its own if it receives invalid data from the server
// Vanilla sends true so we also send true

View File

@ -145,5 +145,10 @@ public class PlayerListItem extends DefinedPacket
// ADD_PLAYER & UPDATE_DISPLAY_NAME
BaseComponent displayName;
// UPDATE_LIST_ORDER 1.21.2
Integer listOrder;
// UPDATE_HAT 1.21.4
Boolean showHat;
}
}

View File

@ -61,6 +61,12 @@ public class PlayerListItemUpdate extends DefinedPacket
item.displayName = DefinedPacket.readBaseComponent( buf, protocolVersion );
}
break;
case UPDATE_LIST_ORDER:
item.listOrder = DefinedPacket.readVarInt( buf );
break;
case UPDATE_HAT:
item.showHat = buf.readBoolean();
break;
}
}
}
@ -109,6 +115,12 @@ public class PlayerListItemUpdate extends DefinedPacket
DefinedPacket.writeBaseComponent( item.displayName, buf, protocolVersion );
}
break;
case UPDATE_LIST_ORDER:
DefinedPacket.writeVarInt( item.listOrder, buf );
break;
case UPDATE_HAT:
buf.writeBoolean( item.showHat );
break;
}
}
}
@ -128,6 +140,8 @@ public class PlayerListItemUpdate extends DefinedPacket
UPDATE_GAMEMODE,
UPDATE_LISTED,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME;
UPDATE_DISPLAY_NAME,
UPDATE_LIST_ORDER,
UPDATE_HAT;
}
}

View File

@ -30,6 +30,7 @@ public class Respawn extends DefinedPacket
private byte copyMeta;
private Location deathLocation;
private int portalCooldown;
private int seaLevel;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@ -84,6 +85,10 @@ public class Respawn extends DefinedPacket
{
portalCooldown = readVarInt( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 )
{
seaLevel = readVarInt( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_2 )
{
copyMeta = buf.readByte();
@ -148,6 +153,10 @@ public class Respawn extends DefinedPacket
{
writeVarInt( portalCooldown, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_21_2 )
{
writeVarInt( seaLevel, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_20_2 )
{
buf.writeByte( copyMeta );

View File

@ -21,7 +21,7 @@ public class UnsignedClientCommand extends DefinedPacket
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
command = readString( buf, 256 );
command = readString( buf );
}
@Override

View File

@ -11,19 +11,18 @@ import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Random;
import java.util.UUID;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import lombok.Getter;
import net.md_5.bungee.jni.NativeCode;
import net.md_5.bungee.jni.cipher.BungeeCipher;
import net.md_5.bungee.jni.cipher.JavaCipher;
@ -41,8 +40,6 @@ public class EncryptionUtil
private static final Random random = new Random();
private static final Base64.Encoder MIME_ENCODER = Base64.getMimeEncoder( 76, "\n".getBytes( StandardCharsets.UTF_8 ) );
public static final KeyPair keys;
@Getter
private static final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" );
public static final NativeCode<BungeeCipher> nativeFactory = new NativeCode<>( "native-cipher", JavaCipher::new, NativeCipher::new );
private static final PublicKey MOJANG_KEY;
@ -111,17 +108,17 @@ public class EncryptionUtil
return signature.verify( resp.getEncryptionData().getSignature() );
} else
{
Cipher cipher = Cipher.getInstance( "RSA" );
Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" );
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
byte[] decrypted = cipher.doFinal( resp.getVerifyToken() );
return Arrays.equals( request.getVerifyToken(), decrypted );
return MessageDigest.isEqual( request.getVerifyToken(), decrypted );
}
}
public static SecretKey getSecret(EncryptionResponse resp, EncryptionRequest request) throws GeneralSecurityException
{
Cipher cipher = Cipher.getInstance( "RSA" );
Cipher cipher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" );
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
return new SecretKeySpec( cipher.doFinal( resp.getSharedSecret() ), "AES" );
}
@ -146,7 +143,7 @@ public class EncryptionUtil
public static byte[] encrypt(Key key, byte[] b) throws GeneralSecurityException
{
Cipher hasher = Cipher.getInstance( "RSA" );
Cipher hasher = Cipher.getInstance( "RSA/ECB/PKCS1Padding" );
hasher.init( Cipher.ENCRYPT_MODE, key );
return hasher.doFinal( b );
}

View File

@ -5,7 +5,6 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -32,7 +31,7 @@ public class ServerConnection implements Server
private final boolean forgeServer = false;
@Getter
private final Queue<KeepAliveData> keepAlives = new ArrayDeque<>();
private final Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
private final Queue<DefinedPacket> packetQueue = new ArrayDeque<>();
private final Unsafe unsafe = new Unsafe()
{
@ -45,23 +44,37 @@ public class ServerConnection implements Server
public void sendPacketQueued(DefinedPacket packet)
{
Protocol encodeProtocol = ch.getEncodeProtocol();
if ( !encodeProtocol.TO_SERVER.hasPacket( packet.getClass(), ch.getEncodeVersion() ) )
ch.scheduleIfNecessary( () ->
{
packetQueue.add( packet );
} else
{
unsafe().sendPacket( packet );
}
if ( ch.isClosed() )
{
return;
}
Protocol encodeProtocol = ch.getEncodeProtocol();
if ( !encodeProtocol.TO_SERVER.hasPacket( packet.getClass(), ch.getEncodeVersion() ) )
{
packetQueue.add( packet );
} else
{
unsafe().sendPacket( packet );
}
} );
}
public void sendQueuedPackets()
{
DefinedPacket packet;
while ( ( packet = packetQueue.poll() ) != null )
ch.scheduleIfNecessary( () ->
{
unsafe().sendPacket( packet );
}
if ( ch.isClosed() )
{
return;
}
DefinedPacket packet;
while ( ( packet = packetQueue.poll() ) != null )
{
unsafe().sendPacket( packet );
}
} );
}
@Override
@ -81,6 +94,7 @@ public class ServerConnection implements Server
{
Preconditions.checkArgument( reason.length == 0, "Server cannot have disconnect reason" );
isObsolete = true;
ch.close();
}

View File

@ -137,6 +137,15 @@ public class ServerConnector extends PacketHandler
public void disconnected(ChannelWrapper channel) throws Exception
{
user.getPendingConnects().remove( target );
if ( user.getServer() == null && !obsolete && user.getPendingConnects().isEmpty() && thisState == State.LOGIN_SUCCESS )
{
// this is called if we get disconnected but not have received any response after we send the handshake
// in this case probably an exception was thrown because the handshake could not be read correctly
// because of the extra ip forward data, also we skip the disconnect if another server is also in the
// pendingConnects queue because we don't want to lose the player
user.disconnect( "Unexpected disconnect during server login, did you forget to enable BungeeCord / IP forwarding on your server?" );
}
}
@Override
@ -252,7 +261,7 @@ public class ServerConnector extends PacketHandler
// Set tab list size, TODO: what shall we do about packet mutability
Login modLogin = new Login( login.getEntityId(), login.isHardcore(), login.getGameMode(), login.getPreviousGameMode(), login.getWorldNames(), login.getDimensions(), login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(),
(byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType(), login.getViewDistance(), login.getSimulationDistance(), login.isReducedDebugInfo(), login.isNormalRespawn(), login.isLimitedCrafting(), login.isDebug(), login.isFlat(), login.getDeathLocation(),
login.getPortalCooldown(), login.isSecureProfile() );
login.getPortalCooldown(), login.getSeaLevel(), login.isSecureProfile() );
user.unsafe().sendPacket( modLogin );
@ -270,7 +279,7 @@ public class ServerConnector extends PacketHandler
user.getSentBossBars().clear();
user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), (byte) 0, login.getDeathLocation(),
login.getPortalCooldown() ) );
login.getPortalCooldown(), login.getSeaLevel() ) );
} else
{
user.unsafe().sendPacket( BungeeCord.getInstance().registerChannels( user.getPendingConnection().getVersion() ) );
@ -332,12 +341,12 @@ public class ServerConnector extends PacketHandler
if ( login.getDimension() == user.getDimension() )
{
user.unsafe().sendPacket( new Respawn( (Integer) login.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(),
(byte) 0, login.getDeathLocation(), login.getPortalCooldown() ) );
(byte) 0, login.getDeathLocation(), login.getPortalCooldown(), login.getSeaLevel() ) );
}
user.setServerEntityId( login.getEntityId() );
user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(),
(byte) 0, login.getDeathLocation(), login.getPortalCooldown() ) );
(byte) 0, login.getDeathLocation(), login.getPortalCooldown(), login.getSeaLevel() ) );
if ( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_14 )
{
user.unsafe().sendPacket( new ViewDistance( login.getViewDistance() ) );
@ -361,7 +370,10 @@ public class ServerConnector extends PacketHandler
if ( user.getServer() != null )
{
// Begin config mode
user.unsafe().sendPacket( new StartConfiguration() );
if ( user.getCh().getEncodeProtocol() != Protocol.CONFIGURATION )
{
user.unsafe().sendPacket( new StartConfiguration() );
}
} else
{
LoginResult loginProfile = user.getPendingConnection().getLoginProfile();
@ -373,7 +385,6 @@ public class ServerConnector extends PacketHandler
// Remove from old servers
if ( user.getServer() != null )
{
user.getServer().setObsolete( true );
user.getServer().disconnect( "Quitting" );
}

View File

@ -11,6 +11,7 @@ import io.netty.channel.ChannelOption;
import io.netty.util.internal.PlatformDependent;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@ -21,7 +22,6 @@ import java.util.Objects;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import lombok.Getter;
import lombok.NonNull;
@ -146,7 +146,7 @@ public final class UserConnection implements ProxiedPlayer
@Setter
private ForgeServerHandler forgeServerHandler;
/*========================================================================*/
private final Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
private final Queue<DefinedPacket> packetQueue = new ArrayDeque<>();
private final Unsafe unsafe = new Unsafe()
{
@Override
@ -186,23 +186,37 @@ public final class UserConnection implements ProxiedPlayer
public void sendPacketQueued(DefinedPacket packet)
{
Protocol encodeProtocol = ch.getEncodeProtocol();
if ( !encodeProtocol.TO_CLIENT.hasPacket( packet.getClass(), getPendingConnection().getVersion() ) )
ch.scheduleIfNecessary( () ->
{
packetQueue.add( packet );
} else
{
unsafe().sendPacket( packet );
}
if ( ch.isClosed() )
{
return;
}
Protocol encodeProtocol = ch.getEncodeProtocol();
if ( !encodeProtocol.TO_CLIENT.hasPacket( packet.getClass(), getPendingConnection().getVersion() ) )
{
packetQueue.add( packet );
} else
{
unsafe().sendPacket( packet );
}
} );
}
public void sendQueuedPackets()
{
DefinedPacket packet;
while ( ( packet = packetQueue.poll() ) != null )
ch.scheduleIfNecessary( () ->
{
unsafe().sendPacket( packet );
}
if ( ch.isClosed() )
{
return;
}
DefinedPacket packet;
while ( ( packet = packetQueue.poll() ) != null )
{
unsafe().sendPacket( packet );
}
} );
}
@Deprecated
@ -306,6 +320,11 @@ public final class UserConnection implements ProxiedPlayer
{
Preconditions.checkNotNull( request, "request" );
ch.getHandle().eventLoop().execute( () -> connect0( request ) );
}
private void connect0(final ServerConnectRequest request)
{
final Callback<ServerConnectRequest.Result> callback = request.getCallback();
ServerConnectEvent event = new ServerConnectEvent( this, request.getTarget(), request.getReason(), request );
if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
@ -315,10 +334,6 @@ public final class UserConnection implements ProxiedPlayer
callback.done( ServerConnectRequest.Result.EVENT_CANCEL, null );
}
if ( getServer() == null && !ch.isClosing() )
{
throw new IllegalStateException( "Cancelled ServerConnectEvent with no server or disconnect." );
}
return;
}
@ -439,7 +454,6 @@ public final class UserConnection implements ProxiedPlayer
if ( server != null )
{
server.setObsolete( true );
server.disconnect( "Quitting" );
}
}

View File

@ -8,5 +8,5 @@ import net.md_5.bungee.jni.zlib.NativeZlib;
public class CompressFactory
{
public static final NativeCode<BungeeZlib> zlib = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new );
public static final NativeCode<BungeeZlib> zlib = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new, true );
}

View File

@ -2,18 +2,23 @@ package net.md_5.bungee.compress;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
import java.util.zip.Deflater;
import lombok.Getter;
import lombok.Setter;
import net.md_5.bungee.jni.zlib.BungeeZlib;
import net.md_5.bungee.protocol.DefinedPacket;
public class PacketCompressor extends MessageToByteEncoder<ByteBuf>
public class PacketCompressor extends MessageToMessageEncoder<ByteBuf>
{
@Getter
private final BungeeZlib zlib = CompressFactory.zlib.newInstance();
@Setter
private int threshold = 256;
@Setter
private boolean compose = true;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception
@ -28,18 +33,25 @@ public class PacketCompressor extends MessageToByteEncoder<ByteBuf>
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception
{
int origSize = msg.readableBytes();
if ( origSize < threshold )
{
DefinedPacket.writeVarInt( 0, out );
out.writeBytes( msg );
if ( compose )
{
// create a virtual buffer to avoid copying of data
out.add( ctx.alloc().compositeDirectBuffer( 2 ).addComponents( true, ctx.alloc().directBuffer( 1 ).writeByte( 0 ), msg.retain() ) );
} else
{
out.add( ctx.alloc().directBuffer( origSize + 1 ).writeByte( 0 ).writeBytes( msg ) );
}
} else
{
DefinedPacket.writeVarInt( origSize, out );
zlib.process( msg, out );
ByteBuf buf = ctx.alloc().directBuffer( BungeeZlib.OUTPUT_BUFFER_SIZE );
DefinedPacket.writeVarInt( origSize, buf );
zlib.process( msg, buf );
out.add( buf );
}
}
}

View File

@ -95,7 +95,7 @@ public class YamlConfig implements ConfigurationAdapter
} ) );
set( "permissions.admin", Arrays.asList( new String[]
{
"bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", "bungeecord.command.kick"
"bungeecord.command.alert", "bungeecord.command.end", "bungeecord.command.ip", "bungeecord.command.reload", "bungeecord.command.kick", "bungeecord.command.send", "bungeecord.command.find"
} ) );
}

View File

@ -10,8 +10,10 @@ import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
@ -66,6 +68,8 @@ import net.md_5.bungee.protocol.packet.Handshake;
import net.md_5.bungee.protocol.packet.Kick;
import net.md_5.bungee.protocol.packet.LegacyHandshake;
import net.md_5.bungee.protocol.packet.LegacyPing;
import net.md_5.bungee.protocol.packet.LoginAcknowledged;
import net.md_5.bungee.protocol.packet.LoginPayloadRequest;
import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
import net.md_5.bungee.protocol.packet.LoginRequest;
import net.md_5.bungee.protocol.packet.LoginSuccess;
@ -95,6 +99,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Getter
private final Set<String> registeredChannels = new HashSet<>();
private State thisState = State.HANDSHAKE;
private int loginPayloadId;
private final Map<Integer, CompletableFuture<byte[]>> requestedLoginPayloads = new HashMap<>();
private final Queue<CookieFuture> requestedCookies = new LinkedList<>();
@Data
@ -190,6 +196,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void handle(LegacyHandshake legacyHandshake) throws Exception
{
Preconditions.checkState( !this.legacy, "Not expecting LegacyHandshake" );
this.legacy = true;
ch.close( bungee.getTranslation( "outdated_client", bungee.getGameVersion() ) );
}
@ -197,6 +204,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void handle(LegacyPing ping) throws Exception
{
Preconditions.checkState( !this.legacy, "Not expecting LegacyPing" );
this.legacy = true;
final boolean v1_5 = ping.isV1_5();
@ -336,7 +344,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void handle(Handshake handshake) throws Exception
{
Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" );
Preconditions.checkState( thisState == State.HANDSHAKE && !this.legacy, "Not expecting HANDSHAKE" );
this.handshake = handshake;
ch.setVersion( handshake.getProtocolVersion() );
ch.getHandle().pipeline().remove( PipelineUtils.LEGACY_KICKER );
@ -414,7 +422,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void handle(LoginRequest loginRequest) throws Exception
{
Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" );
Preconditions.checkState( thisState == State.USERNAME && this.loginRequest == null, "Not expecting USERNAME" );
if ( !AllowedCharacters.isValidName( loginRequest.getData(), onlineMode ) )
{
@ -507,6 +515,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
ch.addBefore( PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
BungeeCipher encrypt = EncryptionUtil.getCipher( true, sharedKey );
ch.addBefore( PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
// disable use of composite buffers if we use natives
ch.updateComposite();
String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" );
@ -580,34 +590,24 @@ public class InitialHandler extends PacketHandler implements PendingConnection
}
}
ProxiedPlayer oldName = bungee.getPlayer( getName() );
if ( oldName != null )
{
// TODO See #1218
disconnect( bungee.getTranslation( "already_connected_proxy" ) );
return;
}
if ( isOnlineMode() )
{
// Check for multiple connections
// We have to check for the old name first
ProxiedPlayer oldName = bungee.getPlayer( getName() );
if ( oldName != null )
{
// TODO See #1218
disconnect( bungee.getTranslation( "already_connected_proxy" ) );
}
// And then also for their old UUID
ProxiedPlayer oldID = bungee.getPlayer( getUniqueId() );
if ( oldID != null )
{
// TODO See #1218
disconnect( bungee.getTranslation( "already_connected_proxy" ) );
}
} else
{
// In offline mode the existing user stays and we kick the new one
ProxiedPlayer oldName = bungee.getPlayer( getName() );
if ( oldName != null )
{
// TODO See #1218
disconnect( bungee.getTranslation( "already_connected_proxy" ) );
return;
}
}
Callback<LoginEvent> complete = new Callback<LoginEvent>()
@ -697,7 +697,20 @@ public class InitialHandler extends PacketHandler implements PendingConnection
@Override
public void handle(LoginPayloadResponse response) throws Exception
{
disconnect( "Unexpected custom LoginPayloadResponse" );
CompletableFuture<byte[]> future;
synchronized ( requestedLoginPayloads )
{
future = requestedLoginPayloads.remove( response.getId() );
}
Preconditions.checkState( future != null, "Unexpected custom LoginPayloadResponse" );
future.complete( response.getData() );
}
@Override
public void handle(LoginAcknowledged loginAcknowledged) throws Exception
{
// this packet should only be sent after the login success (it should be handled in the UpstreamBridge)
disconnect( "Unexpected LoginAcknowledged" );
}
@Override
@ -726,6 +739,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection
throw CancelSendSignal.INSTANCE;
}
// if there is no userCon we can't have a connection to a backend server that could have requested this cookie
// which means that this cookie is invalid as the proxy also has not requested it
Preconditions.checkState( userCon != null, "not requested cookie received" );
}
@Override
@ -882,4 +899,22 @@ public class InitialHandler extends PacketHandler implements PendingConnection
return future;
}
@Override
public CompletableFuture<byte[]> sendData(String channel, byte[] data)
{
Preconditions.checkState( getVersion() >= ProtocolConstants.MINECRAFT_1_13, "LoginPayloads are only supported in 1.13 and above" );
Preconditions.checkState( ch.getEncodeProtocol() == Protocol.LOGIN, "LoginPayloads are only supported in the login phase" );
CompletableFuture<byte[]> future = new CompletableFuture<>();
final int id;
synchronized ( requestedLoginPayloads )
{
// thread safe loginPayloadId
id = loginPayloadId++;
requestedLoginPayloads.put( id, future );
}
unsafe.sendPacket( new LoginPayloadRequest( id, channel, data ) );
return future;
}
}

View File

@ -36,6 +36,7 @@ import net.md_5.bungee.protocol.packet.CookieResponse;
import net.md_5.bungee.protocol.packet.FinishConfiguration;
import net.md_5.bungee.protocol.packet.KeepAlive;
import net.md_5.bungee.protocol.packet.LoginAcknowledged;
import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
import net.md_5.bungee.protocol.packet.PlayerListItem;
import net.md_5.bungee.protocol.packet.PlayerListItemRemove;
import net.md_5.bungee.protocol.packet.PluginMessage;
@ -101,7 +102,8 @@ public class UpstreamBridge extends PacketHandler
{
if ( player.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_19_3 )
{
player.unsafe().sendPacket( newPacket );
// need to queue, because players in config state could receive it
( (UserConnection) player ).sendPacketQueued( newPacket );
} else
{
player.unsafe().sendPacket( oldPacket );
@ -377,6 +379,12 @@ public class UpstreamBridge extends PacketHandler
con.getPendingConnection().handle( cookieResponse );
}
@Override
public void handle(LoginPayloadResponse loginPayloadResponse) throws Exception
{
con.getPendingConnection().handle( loginPayloadResponse );
}
@Override
public String toString()
{

View File

@ -89,6 +89,10 @@ public abstract class EntityMap
case ProtocolConstants.MINECRAFT_1_20_5:
case ProtocolConstants.MINECRAFT_1_21:
return EntityMap_1_16_2.INSTANCE_1_20_5;
case ProtocolConstants.MINECRAFT_1_21_2:
return EntityMap_1_16_2.INSTANCE_1_21_2;
case ProtocolConstants.MINECRAFT_1_21_4:
return EntityMap_1_16_2.INSTANCE_1_21_4;
}
throw new RuntimeException( "Version " + version + " has no entity map" );
}

View File

@ -23,6 +23,8 @@ class EntityMap_1_16_2 extends EntityMap
static final EntityMap_1_16_2 INSTANCE_1_20_2 = new EntityMap_1_16_2( -1, 0x33 );
static final EntityMap_1_16_2 INSTANCE_1_20_3 = new EntityMap_1_16_2( -1, 0x34 );
static final EntityMap_1_16_2 INSTANCE_1_20_5 = new EntityMap_1_16_2( -1, 0x37 );
static final EntityMap_1_16_2 INSTANCE_1_21_2 = new EntityMap_1_16_2( -1, 0x39 );
static final EntityMap_1_16_2 INSTANCE_1_21_4 = new EntityMap_1_16_2( -1, 0x3B );
//
private final int spawnPlayerId;
private final int spectateId;
@ -31,6 +33,10 @@ class EntityMap_1_16_2 extends EntityMap
@SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
public void rewriteClientbound(ByteBuf packet, int oldId, int newId, int protocolVersion)
{
if ( spawnPlayerId == -1 )
{
return;
}
// Special cases
int readerIndex = packet.readerIndex();
int packetId = DefinedPacket.readVarInt( packet );

View File

@ -7,6 +7,7 @@ import java.net.URL;
import java.net.URLConnection;
import lombok.Data;
import net.md_5.bungee.Util;
import net.md_5.bungee.api.ProxyServer;
@Data
public class JenkinsModuleSource implements ModuleSource
@ -15,7 +16,7 @@ public class JenkinsModuleSource implements ModuleSource
@Override
public void retrieve(ModuleSpec module, ModuleVersion version)
{
System.out.println( "Attempting to Jenkins download module " + module.getName() + " v" + version.getBuild() );
ProxyServer.getInstance().getLogger().info( "Attempting to Jenkins download module " + module.getName() + " v" + version.getBuild() );
try
{
URL website = new URL( "https://ci.md-5.net/job/BungeeCord/" + version.getBuild() + "/artifact/module/" + module.getName().replace( '_', '-' ) + "/target/" + module.getName() + ".jar" );
@ -25,10 +26,10 @@ public class JenkinsModuleSource implements ModuleSource
con.setReadTimeout( 15000 );
Files.write( ByteStreams.toByteArray( con.getInputStream() ), module.getFile() );
System.out.println( "Download complete" );
ProxyServer.getInstance().getLogger().info( "Download complete" );
} catch ( IOException ex )
{
System.out.println( "Failed to download: " + Util.exception( ex ) );
ProxyServer.getInstance().getLogger().warning( "Failed to download: " + Util.exception( ex ) );
}
}
}

View File

@ -44,7 +44,7 @@ public class ModuleManager
ModuleVersion bungeeVersion = ModuleVersion.parse( proxy.getVersion() );
if ( bungeeVersion == null )
{
System.out.println( "Couldn't detect bungee version. Custom build?" );
proxy.getLogger().warning( "Couldn't detect bungee version. Custom build?" );
return;
}
@ -105,19 +105,19 @@ public class ModuleManager
ModuleSource source = knownSources.get( uri.getScheme() );
if ( source == null )
{
System.out.println( "Unknown module source: " + s );
proxy.getLogger().warning( "Unknown module source: " + s );
continue;
}
String name = uri.getAuthority();
if ( name == null )
{
System.out.println( "Unknown module host: " + s );
proxy.getLogger().warning( "Unknown module host: " + s );
continue;
}
ModuleSpec spec = new ModuleSpec( name, new File( moduleDirectory, name + ".jar" ), source );
modules.add( spec );
System.out.println( "Discovered module: " + spec );
proxy.getLogger().info( "Discovered module: " + spec );
}
for ( ModuleSpec module : modules )
@ -126,7 +126,7 @@ public class ModuleManager
if ( !bungeeVersion.equals( moduleVersion ) )
{
System.out.println( "Attempting to update plugin from " + moduleVersion + " to " + bungeeVersion );
proxy.getLogger().info( "Attempting to update plugin from " + moduleVersion + " to " + bungeeVersion );
module.getProvider().retrieve( module, bungeeVersion );
}
}

View File

@ -7,15 +7,19 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import lombok.Getter;
import lombok.Setter;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.compress.PacketCompressor;
import net.md_5.bungee.compress.PacketDecompressor;
import net.md_5.bungee.netty.cipher.CipherEncoder;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.MinecraftDecoder;
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
import net.md_5.bungee.protocol.packet.Kick;
public class ChannelWrapper
@ -38,23 +42,22 @@ public class ChannelWrapper
public Protocol getDecodeProtocol()
{
return ch.pipeline().get( MinecraftDecoder.class ).getProtocol();
return getMinecraftDecoder().getProtocol();
}
public void setDecodeProtocol(Protocol protocol)
{
ch.pipeline().get( MinecraftDecoder.class ).setProtocol( protocol );
getMinecraftDecoder().setProtocol( protocol );
}
public Protocol getEncodeProtocol()
{
return ch.pipeline().get( MinecraftEncoder.class ).getProtocol();
return getMinecraftEncoder().getProtocol();
}
public void setEncodeProtocol(Protocol protocol)
{
ch.pipeline().get( MinecraftEncoder.class ).setProtocol( protocol );
getMinecraftEncoder().setProtocol( protocol );
}
public void setProtocol(Protocol protocol)
@ -65,13 +68,23 @@ public class ChannelWrapper
public void setVersion(int protocol)
{
ch.pipeline().get( MinecraftDecoder.class ).setProtocolVersion( protocol );
ch.pipeline().get( MinecraftEncoder.class ).setProtocolVersion( protocol );
getMinecraftDecoder().setProtocolVersion( protocol );
getMinecraftEncoder().setProtocolVersion( protocol );
}
public MinecraftDecoder getMinecraftDecoder()
{
return ch.pipeline().get( MinecraftDecoder.class );
}
public MinecraftEncoder getMinecraftEncoder()
{
return ch.pipeline().get( MinecraftEncoder.class );
}
public int getEncodeVersion()
{
return ch.pipeline().get( MinecraftEncoder.class ).getProtocolVersion();
return getMinecraftEncoder().getProtocolVersion();
}
public void write(Object packet)
@ -187,5 +200,47 @@ public class ChannelWrapper
{
ch.pipeline().remove( "decompress" );
}
// disable use of composite buffers if we use natives
updateComposite();
}
/*
* Should be called on encryption add and on compressor add or remove
*/
public void updateComposite()
{
CipherEncoder cipherEncoder = ch.pipeline().get( CipherEncoder.class );
PacketCompressor packetCompressor = ch.pipeline().get( PacketCompressor.class );
Varint21LengthFieldPrepender prepender = ch.pipeline().get( Varint21LengthFieldPrepender.class );
boolean compressorCompose = cipherEncoder == null || cipherEncoder.getCipher().allowComposite();
boolean prependerCompose = compressorCompose && ( packetCompressor == null || packetCompressor.getZlib().allowComposite() );
if ( prepender != null )
{
ProxyServer.getInstance().getLogger().log( Level.FINE, "set prepender compose to {0} for {1}", new Object[]
{
prependerCompose, ch
} );
prepender.setCompose( prependerCompose );
}
if ( packetCompressor != null )
{
ProxyServer.getInstance().getLogger().log( Level.FINE, "set packetCompressor compose to {0} for {1}", new Object[]
{
compressorCompose, ch
} );
packetCompressor.setCompose( compressorCompose );
}
}
public void scheduleIfNecessary(Runnable task)
{
if ( ch.eventLoop().inEventLoop() )
{
task.run();
return;
}
ch.eventLoop().execute( task );
}
}

View File

@ -93,7 +93,6 @@ public class PipelineUtils
public static final Base BASE = new Base( false );
public static final Base BASE_SERVERSIDE = new Base( true );
private static final KickStringWriter legacyKicker = new KickStringWriter();
private static final Varint21LengthFieldPrepender framePrepender = new Varint21LengthFieldPrepender();
private static final Varint21LengthFieldExtraBufPrepender serverFramePrepender = new Varint21LengthFieldExtraBufPrepender();
public static final String TIMEOUT_HANDLER = "timeout";
public static final String PACKET_DECODER = "packet-decoder";
@ -202,7 +201,7 @@ public class PipelineUtils
ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
// No encryption bungee -> server, therefore use extra buffer to avoid copying everything for length prepending
// Not used bungee -> client as header would need to be encrypted separately through expensive JNI call
ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : framePrepender );
ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : new Varint21LengthFieldPrepender() );
ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
}

View File

@ -3,6 +3,7 @@ package net.md_5.bungee.netty.cipher;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.jni.cipher.BungeeCipher;
@ -10,6 +11,7 @@ import net.md_5.bungee.jni.cipher.BungeeCipher;
public class CipherEncoder extends MessageToByteEncoder<ByteBuf>
{
@Getter
private final BungeeCipher cipher;
@Override