better algorithm for lilypad workaround, update deps

test if native compressor works before using it
This commit is contained in:
creeper123123321 2021-08-25 20:54:12 -03:00
parent 5fac0e2ae2
commit 9757e76cd6
2 changed files with 61 additions and 64 deletions

View File

@ -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>("processResources") {
)
}
filesMatching("**/*.js") {
//filter<JsMinifyFilter>()
filter<JsMinifyFilter>()
}
filesMatching("**/*.html") {
filter<HtmlMinifyFilter>()

View File

@ -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<ByteBuf, ByteBuf> {
// 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<SocketAddress, Pair<AtomicInteger, AtomicInteger>> 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<ByteBuf, ByteBuf> {
@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<ByteBuf, ByteBuf> {
return MoreByteBufUtils.preferredBuffer(ctx.alloc(), compressor, initialBufferSize);
}
private Pair<AtomicInteger, AtomicInteger> 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<Object> out) throws Exception {
if (!input.isReadable() || !ctx.channel().isActive()) return;
@ -144,18 +118,41 @@ public class CompressionCodec extends MessageToMessageCodec<ByteBuf, ByteBuf> {
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();
}
}
}