Use velocity native compression

This commit is contained in:
themode 2021-03-28 20:40:27 +02:00
parent 85d01e5009
commit 1e817ee4b5
5 changed files with 55 additions and 50 deletions

View File

@ -39,6 +39,9 @@ allprojects {
name 'sponge'
url 'https://repo.spongepowered.org/maven'
}
maven {
url "https://repo.velocitypowered.com/snapshots/"
}
}
javadoc {
options {
@ -148,6 +151,9 @@ dependencies {
api "org.ow2.asm:asm-commons:${asmVersion}"
api "org.spongepowered:mixin:${mixinVersion}"
// Compression
implementation "com.velocitypowered:velocity-native:1.1.0-SNAPSHOT"
// Path finding
api 'com.github.MadMartian:hydrazine-path-finding:1.5.4'

View File

@ -16,8 +16,10 @@
package net.minestom.server.network.netty.codec;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.velocitypowered.natives.util.MoreByteBufUtils;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import io.netty.handler.codec.DecoderException;
@ -25,8 +27,6 @@ import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Utils;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public class PacketCompressor extends ByteToMessageCodec<ByteBuf> {
@ -34,8 +34,7 @@ public class PacketCompressor extends ByteToMessageCodec<ByteBuf> {
private final int threshold;
private final Deflater deflater = new Deflater();
private final Inflater inflater = new Inflater();
private final VelocityCompressor compressor = Natives.compress.get().create(4);
public PacketCompressor(int threshold) {
this.threshold = threshold;
@ -43,36 +42,38 @@ public class PacketCompressor extends ByteToMessageCodec<ByteBuf> {
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf from, ByteBuf to) {
PacketUtils.compressBuffer(deflater, from, to);
PacketUtils.compressBuffer(compressor, from, to);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
if (buf.readableBytes() != 0) {
final int i = Utils.readVarInt(buf);
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() != 0) {
final int claimedUncompressedSize = Utils.readVarInt(in);
if (i == 0) {
out.add(buf.readRetainedSlice(buf.readableBytes()));
if (claimedUncompressedSize == 0) {
out.add(in.readRetainedSlice(in.readableBytes()));
} else {
if (i < this.threshold) {
throw new DecoderException("Badly compressed packet - size of " + i + " is below server threshold of " + this.threshold);
if (claimedUncompressedSize < this.threshold) {
throw new DecoderException("Badly compressed packet - size of " + claimedUncompressedSize + " is below server threshold of " + this.threshold);
}
if (i > MAX_SIZE) {
throw new DecoderException("Badly compressed packet - size of " + i + " is larger than protocol maximum of " + MAX_SIZE);
if (claimedUncompressedSize > MAX_SIZE) {
throw new DecoderException("Badly compressed packet - size of " + claimedUncompressedSize + " is larger than protocol maximum of " + MAX_SIZE);
}
// TODO optimize to do not initialize arrays each time
byte[] input = new byte[buf.readableBytes()];
buf.readBytes(input);
inflater.setInput(input);
byte[] output = new byte[i];
inflater.inflate(output);
inflater.reset();
out.add(Unpooled.wrappedBuffer(output));
ByteBuf compatibleIn = MoreByteBufUtils.ensureCompatible(ctx.alloc(), compressor, in);
ByteBuf uncompressed = MoreByteBufUtils.preferredBuffer(ctx.alloc(), compressor, claimedUncompressedSize);
try {
compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
out.add(uncompressed);
in.clear();
} catch (Exception e) {
uncompressed.release();
throw e;
} finally {
compatibleIn.release();
}
}
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.utils;
import com.velocitypowered.natives.compression.VelocityCompressor;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minestom.server.MinecraftServer;
@ -16,9 +18,8 @@ import net.minestom.server.utils.callback.validator.PlayerValidator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.zip.Deflater;
import java.util.zip.DataFormatException;
/**
* Utils class for packets. Including writing a {@link ServerPacket} into a {@link ByteBuf}
@ -27,7 +28,7 @@ import java.util.zip.Deflater;
public final class PacketUtils {
private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager();
private static final ThreadLocal<Deflater> DEFLATER = ThreadLocal.withInitial(Deflater::new);
private static final ThreadLocal<VelocityCompressor> COMPRESSOR = ThreadLocal.withInitial(() -> Natives.compress.get().create(4));
private PacketUtils() {
@ -162,35 +163,27 @@ public final class PacketUtils {
* <p>
* {@code packetBuffer} needs to be the packet content without any header (if you want to use it to write a Minecraft packet).
*
* @param deflater the deflater for zlib compression
* @param compressor the deflater for zlib compression
* @param packetBuffer the buffer containing all the packet fields
* @param compressionTarget the buffer which will receive the compressed version of {@code packetBuffer}
*/
public static void compressBuffer(@NotNull Deflater deflater, @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) {
public static void compressBuffer(@NotNull VelocityCompressor compressor, @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) {
final int packetLength = packetBuffer.readableBytes();
final boolean compression = packetLength > MinecraftServer.getCompressionThreshold();
Utils.writeVarIntBuf(compressionTarget, compression ? packetLength : 0);
if (compression) {
compress(deflater, packetBuffer, compressionTarget);
compress(compressor, packetBuffer, compressionTarget);
} else {
compressionTarget.writeBytes(packetBuffer);
}
}
private static void compress(@NotNull Deflater deflater, @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) {
deflater.setInput(uncompressed.nioBuffer());
deflater.finish();
while (!deflater.finished()) {
ByteBuffer nioBuffer = compressed.nioBuffer(compressed.writerIndex(), compressed.writableBytes());
compressed.writerIndex(deflater.deflate(nioBuffer) + compressed.writerIndex());
if (compressed.writableBytes() == 0) {
compressed.ensureWritable(8192);
}
private static void compress(@NotNull VelocityCompressor compressor, @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) {
try {
compressor.deflate(uncompressed, compressed);
} catch (DataFormatException e) {
e.printStackTrace();
}
deflater.reset();
}
public static void writeFramedPacket(@NotNull ByteBuf buffer,
@ -212,11 +205,11 @@ public final class PacketUtils {
if (packetSize >= compressionThreshold) {
// Packet large enough
final Deflater deflater = DEFLATER.get();
final VelocityCompressor compressor = COMPRESSOR.get();
// Compress id + payload
ByteBuf uncompressedCopy = buffer.copy(contentIndex, packetSize);
buffer.writerIndex(contentIndex);
compress(deflater, uncompressedCopy, buffer);
compress(compressor, uncompressedCopy, buffer);
uncompressedCopy.release();
final int totalPacketLength = buffer.writerIndex() - contentIndex + Utils.VARINT_HEADER_SIZE;

View File

@ -92,10 +92,8 @@ public class BinaryReader extends InputStream {
public String readSizedString(int maxLength) {
final int length = readVarInt();
Check.stateCondition(length > maxLength,
"String length (" + length + ") was higher than the max length of " + maxLength);
final byte[] bytes = readBytes(length);
return new String(bytes, StandardCharsets.UTF_8);
"String length ({0}) was higher than the max length of {1}", length, maxLength);
return buffer.readCharSequence(length, StandardCharsets.UTF_8).toString();
}
public byte[] readBytes(int length) {

View File

@ -44,4 +44,11 @@ public final class Check {
}
}
@Contract("true, _, _ -> fail")
public static void stateCondition(boolean condition, @NotNull String reason, Object... arguments) {
if (condition) {
throw new IllegalStateException(MessageFormat.format(reason, arguments));
}
}
}