package org.openrs2.protocol import com.github.michaelbull.logging.InlineLogger import io.netty.buffer.ByteBuf import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.ByteToMessageDecoder import io.netty.handler.codec.DecoderException import org.openrs2.crypto.NopStreamCipher import org.openrs2.crypto.StreamCipher public class Rs2Decoder(public var protocol: Protocol) : ByteToMessageDecoder() { private enum class State { READ_OPCODE, READ_LENGTH, READ_PAYLOAD } public var cipher: StreamCipher = NopStreamCipher private var state = State.READ_OPCODE private lateinit var decoder: PacketCodec<*> private var length = 0 init { isSingleDecode = true } override fun decode(ctx: ChannelHandlerContext, input: ByteBuf, out: MutableList) { if (state == State.READ_OPCODE) { if (!input.isReadable) { return } val opcode = (input.readUnsignedByte().toInt() - cipher.nextInt()) and 0xFF decoder = protocol.getDecoder(opcode) ?: throw DecoderException("Unsupported opcode: $opcode") state = State.READ_LENGTH } if (state == State.READ_LENGTH) { if (!decoder.isLengthReadable(input)) { return } length = decoder.readLength(input) state = State.READ_PAYLOAD } if (state == State.READ_PAYLOAD) { if (input.readableBytes() < length) { return } val payload = input.readSlice(length) out += try { decoder.decode(payload, cipher) } catch (ex: NotImplementedError) { // TODO(gpe): remove this catch block when every packet is implemented logger.warn { "Skipping unimplemented packet: ${decoder.javaClass}" } } if (payload.isReadable) { throw DecoderException("Decoder didn't read entire payload: ${decoder.javaClass}") } state = State.READ_OPCODE } } private companion object { private val logger = InlineLogger() } }