From 0d569ac0d1ed6f38854fc4cc57cde8d2fade36a9 Mon Sep 17 00:00:00 2001 From: md_5 Date: Sun, 1 Feb 2015 21:05:16 +1100 Subject: [PATCH] Refactor native code and implement our own JNI wrapper around zlib. The previous native cipher code has been refactored so that it may be loaded and used slightly more generically, allowing more native components to be easily added as time goes on. I have also written a new native code compression module, which wraps around zlib in the same manner that Inflater / Deflater does, however it operates directly on the memory addresses of it's input / output buffers which means that we can save one, or maybe even two copies. To support this, the VarInt decoder has been adjusted to always use a native buffer. --- native/compile-native.sh | 5 +- native/src/main/c/NativeCipherImpl.cpp | 11 ++- native/src/main/c/NativeCompressImpl.cpp | 82 +++++++++++++++++ .../main/c/net_md_5_bungee_NativeCipherImpl.h | 37 -------- ..._md_5_bungee_jni_cipher_NativeCipherImpl.h | 37 ++++++++ ..._md_5_bungee_jni_zlib_NativeCompressImpl.h | 53 +++++++++++ .../java/net/md_5/bungee/jni/NativeCode.java | 84 ++++++++++++++++++ .../bungee/{ => jni/cipher}/BungeeCipher.java | 2 +- .../cipher/JavaCipher.java} | 6 +- .../bungee/{ => jni/cipher}/NativeCipher.java | 53 +---------- .../{ => jni/cipher}/NativeCipherImpl.java | 2 +- .../net/md_5/bungee/jni/zlib/BungeeZlib.java | 14 +++ .../net/md_5/bungee/jni/zlib/JavaZlib.java | 76 ++++++++++++++++ .../bungee/jni/zlib/NativeCompressImpl.java | 23 +++++ .../net/md_5/bungee/jni/zlib/NativeZlib.java | 61 +++++++++++++ native/src/main/resources/native-cipher.so | Bin 8348 -> 8384 bytes native/src/main/resources/native-compress.so | Bin 0 -> 13968 bytes .../net/md_5/bungee/NativeCipherTest.java | 18 ++-- .../java/net/md_5/bungee/NativeZlibTest.java | 65 ++++++++++++++ .../bungee/protocol/Varint21FrameDecoder.java | 6 +- .../main/java/net/md_5/bungee/BungeeCord.java | 10 ++- .../java/net/md_5/bungee/EncryptionUtil.java | 14 ++- .../md_5/bungee/compress/CompressFactory.java | 12 +++ .../bungee/compress}/PacketCompressor.java | 36 ++++---- .../bungee/compress}/PacketDecompressor.java | 31 ++++--- .../bungee/connection/InitialHandler.java | 1 + .../net/md_5/bungee/netty/ChannelWrapper.java | 4 +- .../bungee/netty/cipher/CipherDecoder.java | 2 +- .../bungee/netty/cipher/CipherEncoder.java | 2 +- 29 files changed, 599 insertions(+), 148 deletions(-) create mode 100644 native/src/main/c/NativeCompressImpl.cpp delete mode 100644 native/src/main/c/net_md_5_bungee_NativeCipherImpl.h create mode 100644 native/src/main/c/net_md_5_bungee_jni_cipher_NativeCipherImpl.h create mode 100644 native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h create mode 100644 native/src/main/java/net/md_5/bungee/jni/NativeCode.java rename native/src/main/java/net/md_5/bungee/{ => jni/cipher}/BungeeCipher.java (93%) rename native/src/main/java/net/md_5/bungee/{FallbackCipher.java => jni/cipher/JavaCipher.java} (94%) rename native/src/main/java/net/md_5/bungee/{ => jni/cipher}/NativeCipher.java (58%) rename native/src/main/java/net/md_5/bungee/{ => jni/cipher}/NativeCipherImpl.java (83%) create mode 100644 native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java create mode 100644 native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java create mode 100644 native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java create mode 100644 native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java create mode 100755 native/src/main/resources/native-compress.so create mode 100644 native/src/test/java/net/md_5/bungee/NativeZlibTest.java create mode 100644 proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java rename {protocol/src/main/java/net/md_5/bungee/protocol => proxy/src/main/java/net/md_5/bungee/compress}/PacketCompressor.java (52%) rename {protocol/src/main/java/net/md_5/bungee/protocol => proxy/src/main/java/net/md_5/bungee/compress}/PacketDecompressor.java (50%) diff --git a/native/compile-native.sh b/native/compile-native.sh index 1baca889..f6e6a35a 100755 --- a/native/compile-native.sh +++ b/native/compile-native.sh @@ -1,3 +1,6 @@ #!/bin/sh -g++ -shared -fPIC -O3 -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so -lcrypto +CXX="g++ -std=c++11 -shared -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" + +$CXX src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so -lcrypto +$CXX src/main/c/NativeCompressImpl.cpp -o src/main/resources/native-compress.so -lz diff --git a/native/src/main/c/NativeCipherImpl.cpp b/native/src/main/c/NativeCipherImpl.cpp index 70aff5c8..cff3dea4 100644 --- a/native/src/main/c/NativeCipherImpl.cpp +++ b/native/src/main/c/NativeCipherImpl.cpp @@ -1,11 +1,12 @@ #include -#include "net_md_5_bungee_NativeCipherImpl.h" +#include "net_md_5_bungee_jni_cipher_NativeCipherImpl.h" typedef unsigned char byte; -jlong JNICALL Java_net_md_15_bungee_NativeCipherImpl_init(JNIEnv* env, jobject obj, jboolean forEncryption, jbyteArray key) { +jlong JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_init(JNIEnv* env, jobject obj, jboolean forEncryption, jbyteArray key) { jbyte *keyBytes = env->GetByteArrayElements(key, NULL); + // TODO: Perhaps we need to throw some exceptions in the unlikely event this fails? EVP_CIPHER_CTX *cipherCtx = EVP_CIPHER_CTX_new(); EVP_CipherInit(cipherCtx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes, forEncryption); @@ -13,10 +14,12 @@ jlong JNICALL Java_net_md_15_bungee_NativeCipherImpl_init(JNIEnv* env, jobject o return (jlong) cipherCtx; } -void Java_net_md_15_bungee_NativeCipherImpl_free(JNIEnv* env, jobject obj, jlong ctx) { +void Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_free(JNIEnv* env, jobject obj, jlong ctx) { + // TODO: Perhaps we need to throw some exceptions in the unlikely event this fails? EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*) ctx); } -void Java_net_md_15_bungee_NativeCipherImpl_cipher(JNIEnv* env, jobject obj, jlong ctx, jlong in, jlong out, jint length) { +void Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_cipher(JNIEnv* env, jobject obj, jlong ctx, jlong in, jlong out, jint length) { + // TODO: Perhaps we need to throw some exceptions in the unlikely event this fails? EVP_CipherUpdate((EVP_CIPHER_CTX*) ctx, (byte*) out, &length, (byte*) in, length); } diff --git a/native/src/main/c/NativeCompressImpl.cpp b/native/src/main/c/NativeCompressImpl.cpp new file mode 100644 index 00000000..0dc668c9 --- /dev/null +++ b/native/src/main/c/NativeCompressImpl.cpp @@ -0,0 +1,82 @@ +#include +#include +#include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" + +using namespace std; +typedef unsigned char byte; + +jint throwException(JNIEnv *env, string message) { + jclass exClass = env->FindClass("java/lang/RuntimeException"); + // Can never actually happen on a sane JVM, but better be safe anyway + if (exClass == NULL) { + return -1; + } + + return env->ThrowNew(exClass, message.c_str()); +} + +static jfieldID consumedID; +static jfieldID finishedID; + +void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields(JNIEnv* env, jclass clazz) { + // We trust that these fields will be there + consumedID = env->GetFieldID(clazz, "consumed", "I"); + finishedID = env->GetFieldID(clazz, "finished", "Z"); +} + +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); + + if (ret != Z_OK) { + throwException(env, "Could not reset z_stream: " + to_string(ret)); + } +} + +void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_end(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) { + z_stream* stream = (z_stream*) ctx; + int ret = (compress) ? deflateEnd(stream) : inflateEnd(stream); + + free(stream); + + if (ret != Z_OK) { + throwException(env, "Could not free z_stream: " + to_string(ret)); + } +} + +jlong JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_init(JNIEnv* env, jobject obj, jboolean compress, jint level) { + z_stream* stream = (z_stream*) calloc(1, sizeof (z_stream)); + int ret = (compress) ? deflateInit(stream, level) : inflateInit(stream); + + if (ret != Z_OK) { + throwException(env, "Could not init z_stream: " + to_string(ret)); + } + + return (jlong) stream; +} + +jint JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_process(JNIEnv* env, jobject obj, jlong ctx, jlong in, jint inLength, jlong out, jint outLength, jboolean compress) { + z_stream* stream = (z_stream*) ctx; + + stream->avail_in = inLength; + stream->next_in = (byte*) in; + + stream->avail_out = outLength; + stream->next_out = (byte*) out; + + int ret = (compress) ? deflate(stream, Z_FINISH) : inflate(stream, Z_PARTIAL_FLUSH); + + switch (ret) { + case Z_STREAM_END: + env->SetBooleanField(obj, finishedID, true); + break; + case Z_OK: + break; + default: + throwException(env, "Unknown z_stream return code: " + to_string(ret)); + } + + env->SetIntField(obj, consumedID, inLength - stream->avail_in); + + return outLength - stream->avail_out; +} diff --git a/native/src/main/c/net_md_5_bungee_NativeCipherImpl.h b/native/src/main/c/net_md_5_bungee_NativeCipherImpl.h deleted file mode 100644 index c4187cc9..00000000 --- a/native/src/main/c/net_md_5_bungee_NativeCipherImpl.h +++ /dev/null @@ -1,37 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class net_md_5_bungee_NativeCipherImpl */ - -#ifndef _Included_net_md_5_bungee_NativeCipherImpl -#define _Included_net_md_5_bungee_NativeCipherImpl -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: net_md_5_bungee_NativeCipherImpl - * Method: init - * Signature: (Z[B)J - */ -JNIEXPORT jlong JNICALL Java_net_md_15_bungee_NativeCipherImpl_init - (JNIEnv *, jobject, jboolean, jbyteArray); - -/* - * Class: net_md_5_bungee_NativeCipherImpl - * Method: free - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_net_md_15_bungee_NativeCipherImpl_free - (JNIEnv *, jobject, jlong); - -/* - * Class: net_md_5_bungee_NativeCipherImpl - * Method: cipher - * Signature: (JJJI)V - */ -JNIEXPORT void JNICALL Java_net_md_15_bungee_NativeCipherImpl_cipher - (JNIEnv *, jobject, jlong, jlong, jlong, jint); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/native/src/main/c/net_md_5_bungee_jni_cipher_NativeCipherImpl.h b/native/src/main/c/net_md_5_bungee_jni_cipher_NativeCipherImpl.h new file mode 100644 index 00000000..87cb45f2 --- /dev/null +++ b/native/src/main/c/net_md_5_bungee_jni_cipher_NativeCipherImpl.h @@ -0,0 +1,37 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class net_md_5_bungee_jni_cipher_NativeCipherImpl */ + +#ifndef _Included_net_md_5_bungee_jni_cipher_NativeCipherImpl +#define _Included_net_md_5_bungee_jni_cipher_NativeCipherImpl +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: net_md_5_bungee_jni_cipher_NativeCipherImpl + * Method: init + * Signature: (Z[B)J + */ +JNIEXPORT jlong JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_init + (JNIEnv *, jobject, jboolean, jbyteArray); + +/* + * Class: net_md_5_bungee_jni_cipher_NativeCipherImpl + * Method: free + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_free + (JNIEnv *, jobject, jlong); + +/* + * Class: net_md_5_bungee_jni_cipher_NativeCipherImpl + * Method: cipher + * Signature: (JJJI)V + */ +JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_cipher + (JNIEnv *, jobject, jlong, jlong, jlong, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h b/native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h new file mode 100644 index 00000000..befb7dcc --- /dev/null +++ b/native/src/main/c/net_md_5_bungee_jni_zlib_NativeCompressImpl.h @@ -0,0 +1,53 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class net_md_5_bungee_jni_zlib_NativeCompressImpl */ + +#ifndef _Included_net_md_5_bungee_jni_zlib_NativeCompressImpl +#define _Included_net_md_5_bungee_jni_zlib_NativeCompressImpl +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: net_md_5_bungee_jni_zlib_NativeCompressImpl + * Method: initFields + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields + (JNIEnv *, jclass); + +/* + * Class: net_md_5_bungee_jni_zlib_NativeCompressImpl + * Method: end + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_end + (JNIEnv *, jobject, jlong, jboolean); + +/* + * Class: net_md_5_bungee_jni_zlib_NativeCompressImpl + * Method: reset + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset + (JNIEnv *, jobject, jlong, jboolean); + +/* + * Class: net_md_5_bungee_jni_zlib_NativeCompressImpl + * Method: init + * Signature: (ZI)J + */ +JNIEXPORT jlong JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_init + (JNIEnv *, jobject, jboolean, jint); + +/* + * Class: net_md_5_bungee_jni_zlib_NativeCompressImpl + * Method: process + * Signature: (JJIJIZ)I + */ +JNIEXPORT jint JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_process + (JNIEnv *, jobject, jlong, jlong, jint, jlong, jint, jboolean); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/native/src/main/java/net/md_5/bungee/jni/NativeCode.java b/native/src/main/java/net/md_5/bungee/jni/NativeCode.java new file mode 100644 index 00000000..8fe93b29 --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/jni/NativeCode.java @@ -0,0 +1,84 @@ +package net.md_5.bungee.jni; + +import com.google.common.io.ByteStreams; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import net.md_5.bungee.jni.cipher.BungeeCipher; + +public final class NativeCode +{ + + private final String name; + private final Class javaImpl; + private final Class nativeImpl; + // + private boolean loaded; + + public NativeCode(String name, Class javaImpl, Class nativeImpl) + { + this.name = name; + this.javaImpl = javaImpl; + this.nativeImpl = nativeImpl; + + load(); + } + + public T newInstance() + { + try + { + return ( loaded ) ? nativeImpl.newInstance() : javaImpl.newInstance(); + } catch ( IllegalAccessException | InstantiationException ex ) + { + throw new RuntimeException( "Error getting instance", ex ); + } + } + + public boolean load() + { + if ( !loaded && isSupported() ) + { + String fullName = "bungeecord-" + name; + + try + { + System.loadLibrary( fullName ); + loaded = true; + } catch ( Throwable t ) + { + } + + if ( !loaded ) + { + try ( InputStream soFile = BungeeCipher.class.getClassLoader().getResourceAsStream( name + ".so" ) ) + { + // Else we will create and copy it to a temp file + File temp = File.createTempFile( fullName, ".so" ); + // Don't leave cruft on filesystem + temp.deleteOnExit(); + + try ( OutputStream outputStream = new FileOutputStream( temp ) ) + { + ByteStreams.copy( soFile, outputStream ); + } + + System.load( temp.getPath() ); + loaded = true; + } catch ( IOException ex ) + { + // Can't write to tmp? + } + } + } + + return loaded; + } + + public static boolean isSupported() + { + return "Linux".equals( System.getProperty( "os.name" ) ) && "amd64".equals( System.getProperty( "os.arch" ) ); + } +} diff --git a/native/src/main/java/net/md_5/bungee/BungeeCipher.java b/native/src/main/java/net/md_5/bungee/jni/cipher/BungeeCipher.java similarity index 93% rename from native/src/main/java/net/md_5/bungee/BungeeCipher.java rename to native/src/main/java/net/md_5/bungee/jni/cipher/BungeeCipher.java index a165d5ad..ebfbb5d5 100644 --- a/native/src/main/java/net/md_5/bungee/BungeeCipher.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/BungeeCipher.java @@ -1,4 +1,4 @@ -package net.md_5.bungee; +package net.md_5.bungee.jni.cipher; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; diff --git a/native/src/main/java/net/md_5/bungee/FallbackCipher.java b/native/src/main/java/net/md_5/bungee/jni/cipher/JavaCipher.java similarity index 94% rename from native/src/main/java/net/md_5/bungee/FallbackCipher.java rename to native/src/main/java/net/md_5/bungee/jni/cipher/JavaCipher.java index 1b2fc267..7b2249f8 100644 --- a/native/src/main/java/net/md_5/bungee/FallbackCipher.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/JavaCipher.java @@ -1,4 +1,4 @@ -package net.md_5.bungee; +package net.md_5.bungee.jni.cipher; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -8,7 +8,7 @@ import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import java.security.GeneralSecurityException; -public class FallbackCipher implements BungeeCipher +public class JavaCipher implements BungeeCipher { private final Cipher cipher; @@ -25,7 +25,7 @@ public class FallbackCipher implements BungeeCipher } } - public FallbackCipher() throws GeneralSecurityException + public JavaCipher() throws GeneralSecurityException { this.cipher = Cipher.getInstance( "AES/CFB8/NoPadding" ); } diff --git a/native/src/main/java/net/md_5/bungee/NativeCipher.java b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java similarity index 58% rename from native/src/main/java/net/md_5/bungee/NativeCipher.java rename to native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java index 28a1c712..dea5e846 100644 --- a/native/src/main/java/net/md_5/bungee/NativeCipher.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipher.java @@ -1,15 +1,10 @@ -package net.md_5.bungee; +package net.md_5.bungee.jni.cipher; import com.google.common.base.Preconditions; -import com.google.common.io.ByteStreams; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import lombok.Getter; import javax.crypto.SecretKey; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; import java.security.GeneralSecurityException; public class NativeCipher implements BungeeCipher @@ -18,54 +13,8 @@ public class NativeCipher implements BungeeCipher @Getter private final NativeCipherImpl nativeCipher = new NativeCipherImpl(); /*============================================================================*/ - private static boolean loaded; private long ctx; - public static boolean isSupported() - { - return "Linux".equals( System.getProperty( "os.name" ) ) && "amd64".equals( System.getProperty( "os.arch" ) ); - } - - public static boolean load() - { - if ( !loaded && isSupported() ) - { - try - { - System.loadLibrary( "bungeecord-native-cipher" ); - loaded = true; - } catch ( Throwable t ) - { - } - - if ( !loaded ) - { - try ( InputStream lib = BungeeCipher.class.getClassLoader().getResourceAsStream( "native-cipher.so" ) ) - { - // Else we will create and copy it to a temp file - File temp = File.createTempFile( "bungeecord-native-cipher", ".so" ); - temp.deleteOnExit(); - - try ( OutputStream outputStream = new FileOutputStream( temp ) ) - { - ByteStreams.copy( lib, outputStream ); - System.load( temp.getPath() ); - } - loaded = true; - } catch ( Throwable t ) - { - } - } - } - - return loaded; - } - - public static boolean isLoaded() - { - return loaded; - } - @Override public void init(boolean forEncryption, SecretKey key) throws GeneralSecurityException { diff --git a/native/src/main/java/net/md_5/bungee/NativeCipherImpl.java b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipherImpl.java similarity index 83% rename from native/src/main/java/net/md_5/bungee/NativeCipherImpl.java rename to native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipherImpl.java index 2167b1c4..df784080 100644 --- a/native/src/main/java/net/md_5/bungee/NativeCipherImpl.java +++ b/native/src/main/java/net/md_5/bungee/jni/cipher/NativeCipherImpl.java @@ -1,4 +1,4 @@ -package net.md_5.bungee; +package net.md_5.bungee.jni.cipher; class NativeCipherImpl { diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java new file mode 100644 index 00000000..0c98ad40 --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/BungeeZlib.java @@ -0,0 +1,14 @@ +package net.md_5.bungee.jni.zlib; + +import io.netty.buffer.ByteBuf; +import java.util.zip.DataFormatException; + +public interface BungeeZlib +{ + + void init(boolean compress, int level); + + void free(); + + void process(ByteBuf in, ByteBuf out) throws DataFormatException; +} diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java new file mode 100644 index 00000000..59810a5b --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/JavaZlib.java @@ -0,0 +1,76 @@ +package net.md_5.bungee.jni.zlib; + +import io.netty.buffer.ByteBuf; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +public class JavaZlib implements BungeeZlib +{ + + private final byte[] buffer = new byte[ 8192 ]; + // + private boolean compress; + private Deflater deflater; + private Inflater inflater; + + @Override + public void init(boolean compress, int level) + { + this.compress = compress; + free(); + + if ( compress ) + { + deflater = new Deflater( level ); + } else + { + inflater = new Inflater(); + } + } + + @Override + public void free() + { + if ( deflater != null ) + { + deflater.end(); + } + if ( inflater != null ) + { + inflater.end(); + } + } + + @Override + public void process(ByteBuf in, ByteBuf out) throws DataFormatException + { + byte[] inData = new byte[ in.readableBytes() ]; + in.readBytes( inData ); + + if ( compress ) + { + deflater.setInput( inData ); + deflater.finish(); + + while ( !deflater.finished() ) + { + int count = deflater.deflate( buffer ); + out.writeBytes( buffer, 0, count ); + } + + deflater.reset(); + } else + { + inflater.setInput( inData ); + + while ( !inflater.finished() ) + { + int count = inflater.inflate( buffer ); + out.writeBytes( buffer, 0, count ); + } + + inflater.reset(); + } + } +} diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java new file mode 100644 index 00000000..3bcc6ddb --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java @@ -0,0 +1,23 @@ +package net.md_5.bungee.jni.zlib; + +public class NativeCompressImpl +{ + + int consumed; + boolean finished; + + static + { + initFields(); + } + + static native void initFields(); + + native void end(long ctx, boolean compress); + + native void reset(long ctx, boolean compress); + + native long init(boolean compress, int compressionLevel); + + native int process(long ctx, long in, int inLength, long out, int outLength, boolean compress); +} diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java new file mode 100644 index 00000000..9b334b4f --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java @@ -0,0 +1,61 @@ +package net.md_5.bungee.jni.zlib; + +import com.google.common.base.Preconditions; +import io.netty.buffer.ByteBuf; +import java.util.zip.DataFormatException; +import lombok.Getter; + +public class NativeZlib implements BungeeZlib +{ + + @Getter + private final NativeCompressImpl nativeCompress = new NativeCompressImpl(); + /*============================================================================*/ + private boolean compress; + private long ctx; + + @Override + public void init(boolean compress, int level) + { + free(); + + this.compress = compress; + this.ctx = nativeCompress.init( compress, level ); + } + + @Override + public void free() + { + if ( ctx != 0 ) + { + nativeCompress.end( ctx, compress ); + ctx = 0; + } + + nativeCompress.consumed = 0; + nativeCompress.finished = false; + } + + @Override + public void process(ByteBuf in, ByteBuf out) throws DataFormatException + { + // Smoke tests + in.memoryAddress(); + out.memoryAddress(); + Preconditions.checkState( ctx != 0, "Invalid pointer to compress!" ); + + while ( !nativeCompress.finished ) + { + out.ensureWritable( 8192 ); + + int processed = nativeCompress.process( ctx, in.memoryAddress() + in.readerIndex(), in.readableBytes(), out.memoryAddress() + out.writerIndex(), out.writableBytes(), compress ); + + in.readerIndex( in.readerIndex() + nativeCompress.consumed ); + out.writerIndex( out.writerIndex() + processed ); + } + + nativeCompress.reset( ctx, compress ); + nativeCompress.consumed = 0; + nativeCompress.finished = false; + } +} diff --git a/native/src/main/resources/native-cipher.so b/native/src/main/resources/native-cipher.so index 3bea4ebbfd73b6fa5776ac010d8f12cd30e6900f..d50d7bbc08b308d99be65083de935d4558982e52 100755 GIT binary patch delta 2283 zcmZ`)eQXp(6rb7KyWSq~4F#K^9Y}6>ZnnD7`P$h%oPou$_+^&~ElG%K5EOd{es^XY5T$AiEZHKM zeEYyzCG2#Y!09%PESY9ru%!pCZ}es)6HG~1PMgC2=U?XjWw<~?PaRCwPrf?1>qc(T znLVp7<^!74_73xA!>6E5a|a>gw&!e>(ZfsowOtTfbd7SML0{ z5EcrRv=6okD`*l1$`mTI9=;Mv=r?d)Q0N7te;>TIHNjKpT_yptRaVj6{q_;u#W{mE zXS|Yc?FFv9j?zbFkwMP$lPB}h6XSdhP{r}w7b!%677vtr#-tFCu_ZzotF$Z zQ(eCLX*9_%*n+($REHymTbMqV$zxlvmSMJfE^Xih!z}r^l!2dMnB_S)WZ)>nBEy6E zZe$iLW`vy)Ck%sm47017JA_#4uMH!i_1l?f)D}%>`Yo+@!o5GB9kCxPFyoh!`OHpF zsGgAIrJbxrJfYXo1@5W8_9ipxufBl-=TEZvBa=2wzoT`JWLIc(R2!S@EJ1JExVJ*n zo9SSt!$Y+LXhymo(|T(Q*r*X5b{1+!YC8~Rf1J$cvl<4T9JRL~LH~F-pnpURsCNbF zYMzyQ7ER5_F@Q0Lxt$#InNKHkb*PO$6GwO@zNN82|8BE(q(IZ}#r11d-{()H@Ypo{ z%k0;piz2+itsY_X*eyd zq=(?9R9%*Em-O3Kxz(d$+`aHkd%e4|*CA|hFRXW~iQ%YgudQ2R=SfJ)xK_6SJYiy@6hY5%)%4#$nRjDN!9T z&l3q7H4im-ZhM)570exu$KgfKaq0vrE74It_ZpW!bNM%y4jw@&P`-q;ip@9rp{|RFB9vjPv^ZD(Rp0kN1Sjo_(22VrQWh) zSrD+a&5S9+`F1NEd_tV_DHxlxBsz1=ey+9351&Nd#Q`NOc8Hd)4yI;o?FU26LLDew zgIT&>dK?e5wQL{!S>~_$7L)m3)z^;IMkd7%)psw1=PJP})UsJKYkUjS(!?Yo zfKU{3sr)723rG}2yb05kSPvuvJw-K=_4xW=1kn(`;X^?EHC`*_mOKj$eq573&vEGS z*GL(TGBDz=aamVVc7YIxxvZZKW;KD(x(xVUUkk)wS)dX!f(W+*g>WDs)AX=Z{23Km Tyzqtcq|2$8@%|;iVPE;bOg`vI delta 2142 zcmZ`)Yitx%6rQ`Y({|Q6`{-_Ww~u`&Xu(+6M!Ghk(zPkf@{o;Mr6ilSgxW0;Sjtw6 z8r#r9GGfEF2aOb{?k{)kIOn@^`-dK} zcb+zO;1cyF;!}zbkM_OHc+ghbBva0gPZ>T>w|xKm!r4)mk% zrw-u(A;$Js!c^N~o9JJVWw4vE3VNZ|9n3n&_->dL-SRuSFIkAe4be@8KsN7#_1+qo zF^9n+H9=fj3Li_cD8U`C(c>|e2Oismhh-98MNS{=Ab2d}Y*KO`-!Ty_T2A_hO9Fzr zE0dCPt8leCc(UY#;ij-;6jg=!`|xe`H{p!p1Xbp<1swC=qL_X)KcnNF6w|ZIXLS4; z#q^l+$8_9Fv6u+GTZcPT66K zpHTNmr;ClqZ^jE*8X($-4Eu%m*iX)hs`iySd3BjeX4KiaK7Fz4u)g?gs{dwO)usv; zse^&W8^{WMkE(->-E`K9A54|1dm7Io%H96EP{<`Pu=tcTjRd)M;w3t2t^Og5nI^9> zA>p#rBN*K>w4iydXvGaYi!}Nw%*69Aq64dcfZFF*6$)z#ch{#QFR0p?h;}Y?Hqsi^ zzKuNJ`kcC_Sk?Y2q%)W%RXdeCk3mGWletNhE!N{xs+L!^tE;suxQpCD*d|w+GnzQvq$oCmB z)p%fi;3;{!nr2uKzJo&nKW@GXhU^=oeA|Z3c#C*-@C5vuFUEtrm*Ms$!Vw5L+}M*l z9WK!#BvHD_bsd!oLok9Ujc=Ez2pL^k1p$I(E@A_JnOn3AHi|@t;6Zsf%J;>;MGMe` zZI`Ey^RA~=Akg3~$9tyS+}^ciBb0iZ9nW;8dS2;j?nx%P ZdZU|@FSqx=HFE{tL)Fmd4Z$zoz&{~3yd(es diff --git a/native/src/main/resources/native-compress.so b/native/src/main/resources/native-compress.so new file mode 100755 index 0000000000000000000000000000000000000000..3a679b76cabe1aa30d7e262437cd05773d62cfd1 GIT binary patch literal 13968 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 literal 0 HcmV?d00001 diff --git a/native/src/test/java/net/md_5/bungee/NativeCipherTest.java b/native/src/test/java/net/md_5/bungee/NativeCipherTest.java index b8857cb0..d6af2d95 100644 --- a/native/src/test/java/net/md_5/bungee/NativeCipherTest.java +++ b/native/src/test/java/net/md_5/bungee/NativeCipherTest.java @@ -1,5 +1,8 @@ package net.md_5.bungee; +import net.md_5.bungee.jni.cipher.NativeCipher; +import net.md_5.bungee.jni.cipher.JavaCipher; +import net.md_5.bungee.jni.cipher.BungeeCipher; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.junit.Assert; @@ -8,6 +11,7 @@ import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import net.md_5.bungee.jni.NativeCode; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class NativeCipherTest @@ -20,13 +24,15 @@ public class NativeCipherTest }; private final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" ); private static final int BENCHMARK_COUNT = 50000; + // + private static final NativeCode factory = new NativeCode( "native-cipher", JavaCipher.class, NativeCipher.class ); @Test public void testOpenSSL() throws Exception { - if ( NativeCipher.isSupported() ) + if ( NativeCode.isSupported() ) { - boolean loaded = NativeCipher.load(); + boolean loaded = factory.load(); Assert.assertTrue( "Native cipher failed to load!", loaded ); NativeCipher cipher = new NativeCipher(); @@ -38,9 +44,9 @@ public class NativeCipherTest @Test public void testOpenSSLBenchmark() throws Exception { - if ( NativeCipher.isSupported() ) + if ( NativeCode.isSupported() ) { - boolean loaded = NativeCipher.load(); + boolean loaded = factory.load(); Assert.assertTrue( "Native cipher failed to load!", loaded ); NativeCipher cipher = new NativeCipher(); @@ -54,7 +60,7 @@ public class NativeCipherTest public void testJDK() throws Exception { // Create JDK cipher - BungeeCipher cipher = new FallbackCipher(); + BungeeCipher cipher = new JavaCipher(); System.out.println( "Testing Java cipher..." ); testACipher( cipher ); @@ -64,7 +70,7 @@ public class NativeCipherTest public void testJDKBenchmark() throws Exception { // Create JDK cipher - BungeeCipher cipher = new FallbackCipher(); + BungeeCipher cipher = new JavaCipher(); System.out.println( "Benchmarking Java cipher..." ); testBenchmark( cipher ); diff --git a/native/src/test/java/net/md_5/bungee/NativeZlibTest.java b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java new file mode 100644 index 00000000..3bba0160 --- /dev/null +++ b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java @@ -0,0 +1,65 @@ +package net.md_5.bungee; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import java.util.Arrays; +import java.util.Random; +import java.util.zip.DataFormatException; +import net.md_5.bungee.jni.NativeCode; +import net.md_5.bungee.jni.zlib.BungeeZlib; +import net.md_5.bungee.jni.zlib.JavaZlib; +import net.md_5.bungee.jni.zlib.NativeZlib; +import org.junit.Assert; +import org.junit.Test; + +public class NativeZlibTest +{ + + private final NativeCode factory = new NativeCode( "native-compress", JavaZlib.class, NativeZlib.class ); + + @Test + public void doTest() throws DataFormatException + { + test( factory.newInstance() ); + test( new JavaZlib() ); + } + + private void test(BungeeZlib zlib) throws DataFormatException + { + System.out.println( "Testing: " + zlib ); + long start = System.currentTimeMillis(); + + byte[] dataBuf = new byte[ 1 << 25 ]; // 32 megabytes + new Random().nextBytes( dataBuf ); + + zlib.init( true, 9 ); + + ByteBuf originalBuf = Unpooled.directBuffer(); + originalBuf.writeBytes( dataBuf ); + + ByteBuf compressed = Unpooled.directBuffer(); + + zlib.process( originalBuf, compressed ); + + // Repeat here to test .reset() + originalBuf = Unpooled.directBuffer(); + originalBuf.writeBytes( dataBuf ); + + compressed = Unpooled.directBuffer(); + + zlib.process( originalBuf, compressed ); + + ByteBuf uncompressed = Unpooled.directBuffer(); + + zlib.init( false, 0 ); + zlib.process( compressed, uncompressed ); + + byte[] check = new byte[ uncompressed.readableBytes() ]; + uncompressed.readBytes( check ); + + long elapsed = System.currentTimeMillis() - start; + System.out.println( "Took: " + elapsed + "ms" ); + + Assert.assertTrue( "Results do not match", Arrays.equals( dataBuf, check ) ); + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java index c6441445..c8d75c35 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java @@ -36,7 +36,11 @@ public class Varint21FrameDecoder extends ByteToMessageDecoder return; } else { - out.add( in.readBytes( length ) ); + // TODO: Really should be a slice! + ByteBuf dst = ctx.alloc().directBuffer( length ); + in.readBytes( dst ); + + out.add( dst ); return; } } diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index d2fdcd26..50ab8d2c 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -68,6 +68,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.command.*; +import net.md_5.bungee.compress.CompressFactory; import net.md_5.bungee.conf.YamlConfig; import net.md_5.bungee.forge.ForgeConstants; import net.md_5.bungee.log.LoggingOutputStream; @@ -198,13 +199,20 @@ public class BungeeCord extends ProxyServer System.setErr( new PrintStream( new LoggingOutputStream( logger, Level.SEVERE ), true ) ); System.setOut( new PrintStream( new LoggingOutputStream( logger, Level.INFO ), true ) ); - if ( NativeCipher.load() ) + if ( EncryptionUtil.nativeFactory.load() ) { logger.info( "Using OpenSSL based native cipher." ); } else { logger.info( "Using standard Java JCE cipher. To enable the OpenSSL based native cipher, please make sure you are using 64 bit Ubuntu or Debian with libssl installed." ); } + if ( CompressFactory.zlib.load() ) + { + logger.info( "Using native code compressor" ); + } else + { + logger.info( "Using standard Java compressor. To enable zero copy compression, run on 64 bit Linux" ); + } } /** diff --git a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java index 2a5ebecf..d464e926 100644 --- a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java +++ b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java @@ -14,6 +14,10 @@ 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; +import net.md_5.bungee.jni.cipher.NativeCipher; import net.md_5.bungee.protocol.packet.EncryptionResponse; import net.md_5.bungee.protocol.packet.EncryptionRequest; @@ -27,6 +31,7 @@ public class EncryptionUtil public static final KeyPair keys; @Getter private static final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" ); + public static final NativeCode nativeFactory = new NativeCode( "native-cipher", JavaCipher.class, NativeCipher.class ); static { @@ -65,14 +70,7 @@ public class EncryptionUtil public static BungeeCipher getCipher(boolean forEncryption, SecretKey shared) throws GeneralSecurityException { - BungeeCipher cipher; - if ( NativeCipher.isLoaded() ) - { - cipher = new NativeCipher(); - } else - { - cipher = new FallbackCipher(); - } + BungeeCipher cipher = nativeFactory.newInstance(); cipher.init( forEncryption, shared ); return cipher; diff --git a/proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java b/proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java new file mode 100644 index 00000000..32b2b3e6 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/compress/CompressFactory.java @@ -0,0 +1,12 @@ +package net.md_5.bungee.compress; + +import net.md_5.bungee.jni.NativeCode; +import net.md_5.bungee.jni.zlib.BungeeZlib; +import net.md_5.bungee.jni.zlib.JavaZlib; +import net.md_5.bungee.jni.zlib.NativeZlib; + +public class CompressFactory +{ + + public static final NativeCode zlib = new NativeCode( "native-compress", JavaZlib.class, NativeZlib.class ); +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/PacketCompressor.java b/proxy/src/main/java/net/md_5/bungee/compress/PacketCompressor.java similarity index 52% rename from protocol/src/main/java/net/md_5/bungee/protocol/PacketCompressor.java rename to proxy/src/main/java/net/md_5/bungee/compress/PacketCompressor.java index 216a92d3..d07cf462 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/PacketCompressor.java +++ b/proxy/src/main/java/net/md_5/bungee/compress/PacketCompressor.java @@ -1,20 +1,32 @@ -package net.md_5.bungee.protocol; +package net.md_5.bungee.compress; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import lombok.Setter; - import java.util.zip.Deflater; +import lombok.Setter; +import net.md_5.bungee.jni.zlib.BungeeZlib; +import net.md_5.bungee.protocol.DefinedPacket; public class PacketCompressor extends MessageToByteEncoder { - private final byte[] buffer = new byte[ 8192 ]; - private final Deflater deflater = new Deflater(); + private final BungeeZlib zlib = CompressFactory.zlib.newInstance(); @Setter private int threshold = 256; + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception + { + zlib.init( true, Deflater.DEFAULT_COMPRESSION ); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception + { + zlib.free(); + } + @Override protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception { @@ -25,19 +37,9 @@ public class PacketCompressor extends MessageToByteEncoder out.writeBytes( msg ); } else { - byte[] data = new byte[ origSize ]; - msg.readBytes( data ); + DefinedPacket.writeVarInt( origSize, out ); - DefinedPacket.writeVarInt( data.length, out ); - - deflater.setInput( data ); - deflater.finish(); - while ( !deflater.finished() ) - { - int count = deflater.deflate( buffer ); - out.writeBytes( buffer, 0, count ); - } - deflater.reset(); + zlib.process( msg, out ); } } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/PacketDecompressor.java b/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java similarity index 50% rename from protocol/src/main/java/net/md_5/bungee/protocol/PacketDecompressor.java rename to proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java index 5496a604..c1770016 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/PacketDecompressor.java +++ b/proxy/src/main/java/net/md_5/bungee/compress/PacketDecompressor.java @@ -1,17 +1,28 @@ -package net.md_5.bungee.protocol; +package net.md_5.bungee.compress; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; - import java.util.List; -import java.util.zip.Inflater; +import net.md_5.bungee.jni.zlib.BungeeZlib; +import net.md_5.bungee.protocol.DefinedPacket; public class PacketDecompressor extends ByteToMessageDecoder { - private final Inflater inflater = new Inflater(); + private final BungeeZlib zlib = CompressFactory.zlib.newInstance(); + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception + { + zlib.init( false, 0 ); + } + + @Override + public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception + { + zlib.free(); + } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception @@ -27,14 +38,10 @@ public class PacketDecompressor extends ByteToMessageDecoder out.add( in.readBytes( in.readableBytes() ) ); } else { - byte[] compressedData = new byte[ in.readableBytes() ]; - in.readBytes( compressedData ); - inflater.setInput( compressedData ); + ByteBuf decompressed = ctx.alloc().directBuffer(); + zlib.process( in, decompressed ); - byte[] data = new byte[ size ]; - inflater.inflate( data ); - out.add( Unpooled.wrappedBuffer( data ) ); - inflater.reset(); + out.add( decompressed ); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 8dfcc3cb..12b90b57 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -48,6 +48,7 @@ import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.event.PlayerHandshakeEvent; import net.md_5.bungee.api.event.PreLoginEvent; +import net.md_5.bungee.jni.cipher.BungeeCipher; import net.md_5.bungee.protocol.Protocol; import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.LegacyHandshake; diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index 9106f937..06d19c3c 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -1,7 +1,7 @@ package net.md_5.bungee.netty; -import net.md_5.bungee.protocol.PacketCompressor; -import net.md_5.bungee.protocol.PacketDecompressor; +import net.md_5.bungee.compress.PacketCompressor; +import net.md_5.bungee.compress.PacketDecompressor; import net.md_5.bungee.protocol.PacketWrapper; import com.google.common.base.Preconditions; import io.netty.channel.Channel; diff --git a/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherDecoder.java b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherDecoder.java index 7fcc323a..9838be86 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherDecoder.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherDecoder.java @@ -4,7 +4,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import lombok.RequiredArgsConstructor; -import net.md_5.bungee.BungeeCipher; +import net.md_5.bungee.jni.cipher.BungeeCipher; import java.util.List; @RequiredArgsConstructor diff --git a/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java index df89b99b..fc19ded0 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/cipher/CipherEncoder.java @@ -4,7 +4,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import lombok.RequiredArgsConstructor; -import net.md_5.bungee.BungeeCipher; +import net.md_5.bungee.jni.cipher.BungeeCipher; @RequiredArgsConstructor public class CipherEncoder extends MessageToByteEncoder