001/*
002 * Trident - A Multithreaded Server Alternative
003 * Copyright 2014 The TridentSDK Team
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *    http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package net.tridentsdk.server.netty.packet;
019
020import io.netty.buffer.ByteBuf;
021import io.netty.buffer.Unpooled;
022import io.netty.channel.ChannelHandlerContext;
023import io.netty.handler.codec.ReplayingDecoder;
024import net.tridentsdk.server.TridentServer;
025import net.tridentsdk.server.netty.ClientConnection;
026import net.tridentsdk.server.netty.Codec;
027
028import java.util.List;
029import java.util.zip.Inflater;
030
031/**
032 * Decoder that decompresses (if needed) and reads the length of the packet data sent from the stream in the form
033 * of the byte buffer.
034 *
035 * <p>This is needed to interpret the data sent correctly, and make sure that the data maintains its transmission
036 * integrity.
037 *
038 * <p>Note this is not thread safe. It should only be used on one thread, or create a new instance for each
039 * channel.</p>
040 *
041 * @author The TridentSDK Team
042 */
043public class PacketDecoder extends ReplayingDecoder<Void> {
044
045    private final Inflater inflater = new Inflater();
046    private ClientConnection connection;
047    private int rawLength;
048
049    @Override
050    public void handlerAdded(ChannelHandlerContext context) {
051        this.connection = ClientConnection.connection(context);
052    }
053
054    @Override
055    protected void decode(ChannelHandlerContext context, ByteBuf buf, List<Object> objects) throws Exception {
056        boolean compressed = connection.isCompressionEnabled();
057        int fullLength = -1;
058
059        if (compressed) {
060            fullLength = Codec.readVarInt32(buf);
061        }
062
063        this.rawLength = Codec.readVarInt32(buf);
064
065        if (rawLength == 0)
066            compressed = false;
067
068        if (!(compressed) || rawLength < TridentServer.instance().compressionThreshold()) {
069            ByteBuf data = buf.readBytes((fullLength == -1) ? rawLength : (fullLength - Codec.sizeOf(0)));
070
071            objects.add(new PacketData(data));
072            return;
073        }
074
075        byte[] compressedData = new byte[buf.readableBytes()];
076        byte[] decompressed = new byte[rawLength];
077
078        buf.readBytes(compressedData);
079        inflater.setInput(compressedData);
080
081        inflater.inflate(decompressed);
082        objects.add(new PacketData(Unpooled.wrappedBuffer(decompressed)));
083
084        inflater.reset();
085    }
086}