package com.captainbern.minecraft.net.pipeline;
import com.captainbern.minecraft.net.util.ByteBufUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import io.netty.handler.codec.MessageToMessageCodec;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public class CompressionHandler extends MessageToMessageCodec<ByteBuf, ByteBuf> {
private static final int COMPRESSION_LEVEL = Deflater.DEFAULT_COMPRESSION;
private final int threshold;
private final Inflater inflater;
private final Deflater deflater;
public CompressionHandler(int threshold) {
this.threshold = threshold;
this.inflater = new Inflater();
this.deflater = new Deflater(COMPRESSION_LEVEL);
}
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
ByteBuf header = channelHandlerContext.alloc().buffer(5);
ByteBuf content;
if (byteBuf.readableBytes() >= this.threshold) {
int readerIndex = byteBuf.readerIndex();
int length = byteBuf.readableBytes();
byte[] decompressed = new byte[length];
byteBuf.readBytes(decompressed);
this.deflater.setInput(decompressed);
this.deflater.finish();
byte[] compressed = new byte[length];
int compressedLength = this.deflater.deflate(compressed);
this.deflater.reset();
if (compressedLength == 0) {
throw new EncoderException("Failed to compress packet of size: " + length);
} else if (compressedLength >= length) {
ByteBufUtils.writeVarInt(header, 0);
byteBuf.readerIndex(readerIndex);
byteBuf.retain();
content = byteBuf;
} else {
ByteBufUtils.writeVarInt(header, 0);
content = Unpooled.wrappedBuffer(compressed, 0, compressedLength);
}
} else {
ByteBufUtils.writeVarInt(header, 0);
byteBuf.retain();
content = byteBuf;
}
list.add(Unpooled.wrappedBuffer(header, content));
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
int readerIndex = byteBuf.readerIndex();
int decompressedSize = ByteBufUtils.readVarInt(byteBuf);
int length = byteBuf.readableBytes();
if (decompressedSize == 0) {
if (length >= this.threshold) {
throw new DecoderException("Received uncompressed packet of size: " + length + ", greater than threshold of: " + this.threshold);
}
ByteBuf buf = channelHandlerContext.alloc().buffer();
byteBuf.readBytes(buf, length);
list.add(buf);
} else {
byte[] compressed = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(compressed);
this.inflater.setInput(compressed);
byte[] decompressed = new byte[decompressedSize];
int decompressedLength = this.inflater.inflate(decompressed);
this.inflater.reset();
if (decompressedLength == 0) {
byteBuf.readerIndex(readerIndex);
byteBuf.retain();
list.add(byteBuf);
} else if (decompressedLength != decompressedSize) {
throw new DecoderException("Decompressed packet should have a size of: " + decompressedSize + " but actually has a size of: " + decompressedLength);
} else {
list.add(Unpooled.wrappedBuffer(decompressed));
}
}
}
}