From 9757e76cd661a6afe47944f83174162dcc2e6bda Mon Sep 17 00:00:00 2001 From: creeper123123321 <7974274+creeper123123321@users.noreply.github.com> Date: Wed, 25 Aug 2021 20:54:12 -0300 Subject: [PATCH] better algorithm for lilypad workaround, update deps test if native compressor works before using it --- build.gradle.kts | 4 +- .../aas/codec/CompressionCodec.java | 121 +++++++++--------- 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d651e67..062b3f5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -68,7 +68,7 @@ dependencies { implementation("com.github.ViaVersion.ViaRewind:viarewind-all:$vrVer") { isTransitive = false } implementation("io.netty:netty-all:4.1.67.Final") - implementation("io.netty:netty-tcnative-boringssl-static:2.0.40.Final") + implementation("io.netty:netty-tcnative-boringssl-static:2.0.41.Final") implementation("io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.8.Final:linux-x86_64") implementation("com.google.guava:guava:30.1.1-jre") @@ -142,7 +142,7 @@ tasks.named("processResources") { ) } filesMatching("**/*.js") { - //filter() + filter() } filesMatching("**/*.html") { filter() diff --git a/src/main/java/com/viaversion/aas/codec/CompressionCodec.java b/src/main/java/com/viaversion/aas/codec/CompressionCodec.java index 201c7a5..c9b654b 100644 --- a/src/main/java/com/viaversion/aas/codec/CompressionCodec.java +++ b/src/main/java/com/viaversion/aas/codec/CompressionCodec.java @@ -1,8 +1,5 @@ package com.viaversion.aas.codec; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; import com.velocitypowered.natives.compression.JavaVelocityCompressor; import com.velocitypowered.natives.compression.VelocityCompressor; import com.velocitypowered.natives.util.MoreByteBufUtils; @@ -11,28 +8,21 @@ import com.viaversion.aas.config.VIAaaSConfig; import com.viaversion.aas.handler.MinecraftHandler; import com.viaversion.viaversion.api.type.Type; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.MessageToMessageCodec; -import kotlin.Pair; -import java.net.SocketAddress; import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.DataFormatException; public class CompressionCodec extends MessageToMessageCodec { // stolen from Krypton (GPL) and modified // https://github.com/astei/krypton/blob/master/src/main/java/me/steinborn/krypton/mod/shared/network/compression/MinecraftCompressEncoder.java private static final int UNCOMPRESSED_CAP = 8 * 1024 * 1024; // 8MiB - // Workaround for Lilypad backend servers - private static final LoadingCache> nativeFails = CacheBuilder - .newBuilder() - .expireAfterWrite(2, TimeUnit.HOURS) - .build(CacheLoader.from(() -> new Pair<>(new AtomicInteger(), new AtomicInteger()))); private int threshold; private VelocityCompressor compressor; + private VelocityCompressor testingCompressor; public CompressionCodec(int threshold) { this.threshold = threshold; @@ -44,29 +34,39 @@ public class CompressionCodec extends MessageToMessageCodec { @Override public void handlerAdded(ChannelHandlerContext ctx) { - var useNative = true; - - var attempts = getAttempts(ctx); - if (attempts != null && Math.random() <= 0.95) { - // We'll use Java when the native compression fail rate is high - var probabilityNo = attempts.getSecond().get() + 1; - var divisor = attempts.getFirst().get() + 2; - useNative = !(Math.random() <= (double) probabilityNo / divisor); - } - var level = VIAaaSConfig.INSTANCE.getCompressionLevel(); - if (useNative) { - compressor = Natives.compress.get().create(level); - } else { - compressor = JavaVelocityCompressor.FACTORY.create(level); - } - recordHandlerAdded(ctx); + var cNative = Natives.compress.get().create(level); + if (isBackend(ctx) && !Natives.compress.getLoadedVariant().equalsIgnoreCase("java")) { + // Workaround for Lilypad backend servers + compressor = JavaVelocityCompressor.FACTORY.create(level); + testingCompressor = cNative; + } else { + compressor = cNative; + } + } + + private boolean isBackend(ChannelHandlerContext ctx) { + var handler = ctx.pipeline().get(MinecraftHandler.class); + return handler != null && !handler.getFrontEnd(); + } + + private void useTestCompressor() { + compressor.close(); + compressor = testingCompressor; + testingCompressor = null; + } + + private void discardTestCompressor() { + if (testingCompressor == null) return; + testingCompressor.close(); + testingCompressor = null; // Discard it, compressor doesn't know how to decompress this } @Override public void handlerRemoved(ChannelHandlerContext ctx) { compressor.close(); + discardTestCompressor(); } @Override @@ -99,32 +99,6 @@ public class CompressionCodec extends MessageToMessageCodec { return MoreByteBufUtils.preferredBuffer(ctx.alloc(), compressor, initialBufferSize); } - private Pair getAttempts(ChannelHandlerContext ctx) { - var handler = ctx.pipeline().get(MinecraftHandler.class); - if (handler == null || handler.getFrontEnd() || !ctx.channel().isActive()) return null; - var addr = handler.getEndRemoteAddress(); - - return nativeFails.getUnchecked(addr); - } - - private void recordHandlerAdded(ChannelHandlerContext ctx) { - if (compressor instanceof JavaVelocityCompressor) return; // Only record errors happened with native - - var attempts = getAttempts(ctx); - if (attempts != null) { - attempts.getFirst().incrementAndGet(); - } - } - - private void recordDecompressFailed(ChannelHandlerContext ctx) { - if (compressor instanceof JavaVelocityCompressor) return; // Only record errors happened with native - - var attempts = getAttempts(ctx); - if (attempts != null) { - attempts.getSecond().incrementAndGet(); - } - } - @Override protected void decode(ChannelHandlerContext ctx, ByteBuf input, List out) throws Exception { if (!input.isReadable() || !ctx.channel().isActive()) return; @@ -144,18 +118,41 @@ public class CompressionCodec extends MessageToMessageCodec { var compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, input); var decompressed = MoreByteBufUtils.preferredBuffer(ctx.alloc(), compressor, claimedUncompressedSize); try { - compressor.inflate(compatibleIn, decompressed, claimedUncompressedSize); - input.clear(); - out.add(decompressed.retain()); - } catch (DataFormatException ex) { - if (ex.getMessage().startsWith("Received a deflate stream that was too large, wanted ")) { - return; // workaround for lilypad + var readerI = compatibleIn.readerIndex(); + try { + compressor.inflate(compatibleIn, decompressed, claimedUncompressedSize); + } catch (DataFormatException ex) { + // workaround for lilypad + if (!ex.getMessage().startsWith("Received a deflate stream that was too large, wanted ")) { + throw ex; + } } - recordDecompressFailed(ctx); - throw ex; + out.add(decompressed.retain()); + + if (testingCompressor != null) { + compatibleIn.readerIndex(readerI); + testCompressor(compatibleIn, claimedUncompressedSize); + } + + input.clear(); } finally { decompressed.release(); compatibleIn.release(); } } + + private void testCompressor(ByteBuf in, int claimedUncompressedSize) { + var testOut = ByteBufAllocator.DEFAULT.buffer(); + try { + testingCompressor.inflate(in, testOut, claimedUncompressedSize); + + if (Math.random() <= 0.001) { // Runs more tests + useTestCompressor(); + } + } catch (DataFormatException eTest) { + discardTestCompressor(); + } finally { + testOut.release(); + } + } }