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/game/src/main/kotlin/org/openrs2/game/net/login/LoginChannelHandler.kt

144 lines
5.2 KiB

package org.openrs2.game.net.login
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.SimpleChannelInboundHandler
import io.netty.handler.codec.DelimiterBasedFrameDecoder
import io.netty.handler.codec.http.HttpObjectAggregator
import io.netty.handler.codec.http.HttpRequestDecoder
import io.netty.handler.codec.http.HttpResponseEncoder
import io.netty.handler.codec.string.StringDecoder
import io.netty.handler.timeout.IdleStateEvent
import org.openrs2.buffer.copiedBuffer
import org.openrs2.game.cluster.Cluster
import org.openrs2.game.net.crossdomain.CrossDomainChannelHandler
import org.openrs2.game.net.http.Http
import org.openrs2.game.net.jaggrab.JaggrabChannelHandler
import org.openrs2.game.net.js5.Js5ChannelHandler
import org.openrs2.protocol.Protocol
import org.openrs2.protocol.Rs2Decoder
import org.openrs2.protocol.Rs2Encoder
import org.openrs2.protocol.jaggrab.JaggrabRequestDecoder
import org.openrs2.protocol.js5.Js5RequestDecoder
import org.openrs2.protocol.js5.Js5ResponseEncoder
import org.openrs2.protocol.js5.XorDecoder
import org.openrs2.protocol.login.LoginRequest
import org.openrs2.protocol.login.LoginResponse
import org.openrs2.protocol.world.WorldListResponse
import javax.inject.Inject
import javax.inject.Provider
public class LoginChannelHandler @Inject constructor(
private val cluster: Cluster,
private val js5HandlerProvider: Provider<Js5ChannelHandler>,
private val jaggrabHandler: JaggrabChannelHandler
) : SimpleChannelInboundHandler<LoginRequest>(LoginRequest::class.java) {
override fun channelActive(ctx: ChannelHandlerContext) {
ctx.read()
}
override fun channelRead0(ctx: ChannelHandlerContext, msg: LoginRequest) {
when (msg) {
is LoginRequest.InitJs5RemoteConnection -> handleInitJs5RemoteConnection(ctx, msg)
is LoginRequest.InitJaggrabConnection -> handleInitJaggrabConnection(ctx)
is LoginRequest.RequestWorldList -> handleRequestWorldList(ctx, msg)
is LoginRequest.InitCrossDomainConnection -> handleInitCrossDomainConnection(ctx)
else -> Unit
}
}
private fun handleInitJs5RemoteConnection(ctx: ChannelHandlerContext, msg: LoginRequest.InitJs5RemoteConnection) {
val encoder = ctx.pipeline().get(Rs2Encoder::class.java)
encoder.protocol = Protocol.JS5REMOTE_DOWNSTREAM
if (msg.build != BUILD) {
ctx.write(LoginResponse.ClientOutOfDate).addListener(ChannelFutureListener.CLOSE)
return
}
ctx.pipeline().addLast(
XorDecoder(),
Js5RequestDecoder(),
Js5ResponseEncoder,
js5HandlerProvider.get()
)
ctx.pipeline().remove(Rs2Decoder::class.java)
ctx.write(LoginResponse.Js5Ok).addListener { future ->
if (future.isSuccess) {
ctx.pipeline().remove(encoder)
ctx.pipeline().remove(this)
}
}
}
private fun handleInitJaggrabConnection(ctx: ChannelHandlerContext) {
ctx.pipeline().addLast(
DelimiterBasedFrameDecoder(JAGGRAB_MAX_FRAME_LENGTH, JAGGRAB_DELIMITER),
StringDecoder(Charsets.UTF_8),
JaggrabRequestDecoder,
jaggrabHandler
)
ctx.pipeline().remove(Rs2Decoder::class.java)
ctx.pipeline().remove(Rs2Encoder::class.java)
ctx.pipeline().remove(this)
}
private fun handleRequestWorldList(ctx: ChannelHandlerContext, msg: LoginRequest.RequestWorldList) {
val (worlds, players) = cluster.getWorldList()
var checksum = worlds.hashCode()
/*
* Fix a 1 in 2^32 chance that we'll fail to fetch the original world
* list on startup.
*/
if (checksum == 0) {
checksum = 1
}
val worldList = if (checksum != msg.checksum) {
WorldListResponse.WorldList(worlds, checksum)
} else {
null
}
val encoder = ctx.pipeline().get(Rs2Encoder::class.java)
encoder.protocol = Protocol.WORLD_LIST_DOWNSTREAM
ctx.write(WorldListResponse(worldList, players)).addListener(ChannelFutureListener.CLOSE)
}
private fun handleInitCrossDomainConnection(ctx: ChannelHandlerContext) {
ctx.pipeline().addLast(
HttpRequestDecoder(),
HttpResponseEncoder(),
HttpObjectAggregator(Http.MAX_CONTENT_LENGTH),
CrossDomainChannelHandler
)
ctx.fireChannelRead(Unpooled.wrappedBuffer(G))
ctx.pipeline().remove(Rs2Decoder::class.java)
ctx.pipeline().remove(Rs2Encoder::class.java)
ctx.pipeline().remove(this)
}
override fun channelReadComplete(ctx: ChannelHandlerContext) {
ctx.flush()
}
override fun userEventTriggered(ctx: ChannelHandlerContext, evt: Any) {
if (evt is IdleStateEvent) {
ctx.close()
}
}
private companion object {
private const val BUILD = 550
private const val JAGGRAB_MAX_FRAME_LENGTH = 4096
private val JAGGRAB_DELIMITER = Unpooled.unreleasableBuffer(copiedBuffer("\n\n"))
private val G = byteArrayOf('G'.code.toByte())
}
}