Open-source multiplayer game server compatible with the RuneScape client https://www.openrs2.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
openrs2/protocol/src/main/kotlin/org/openrs2/protocol/js5/Js5ResponseDecoder.kt

109 lines
3.0 KiB

package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import io.netty.handler.codec.DecoderException
import kotlin.math.min
public class Js5ResponseDecoder : ByteToMessageDecoder() {
private enum class State {
READ_HEADER,
READ_DATA
}
private var state = State.READ_HEADER
private var prefetch: Boolean = false
private var archive: Int = 0
private var group: Int = 0
private var type: Int = 0
private var data: ByteBuf = Unpooled.EMPTY_BUFFER
override fun decode(ctx: ChannelHandlerContext, input: ByteBuf, out: MutableList<Any>) {
if (state == State.READ_HEADER) {
if (input.readableBytes() < 8) {
return
}
archive = input.readUnsignedByte().toInt()
group = input.readUnsignedShort()
type = input.readUnsignedByte().toInt()
if (type and 0x80 != 0) {
prefetch = true
type = type and 0x80.inv()
} else {
prefetch = false
}
val len = input.readInt()
if (len < 0) {
throw DecoderException("Length is negative: $len")
}
val totalLen = if (type == 0) {
len + 5
} else {
len + 9
}
if (totalLen < 0) {
throw DecoderException("Total length exceeds maximum ByteBuf size")
}
// TODO(gpe): release data here?
data = ctx.alloc().buffer(totalLen, totalLen)
data.writeByte(type)
data.writeInt(len)
state = State.READ_DATA
}
if (state == State.READ_DATA) {
while (data.isWritable) {
val blockLen = min(511 - ((data.readableBytes() + 3) % 511), data.writableBytes())
val last = data.writableBytes() <= blockLen
val blockLenIncludingTrailer = if (last) {
blockLen
} else {
blockLen + 1
}
if (input.readableBytes() < blockLenIncludingTrailer) {
return
}
data.writeBytes(input, blockLen)
if (!last && input.readUnsignedByte().toInt() != 0xFF) {
reset()
throw DecoderException("Invalid block trailer")
}
}
out += Js5Response(prefetch, archive, group, data)
data = Unpooled.EMPTY_BUFFER
state = State.READ_HEADER
}
}
override fun channelInactive(ctx: ChannelHandlerContext) {
super.channelInactive(ctx)
reset()
}
override fun handlerRemoved0(ctx: ChannelHandlerContext?) {
reset()
}
private fun reset() {
data.release()
data = Unpooled.EMPTY_BUFFER
state = State.READ_HEADER
}
}