KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/network/Connection.kt
2024-02-10 16:49:58 +07:00

124 lines
3.7 KiB
Kotlin

package ru.dbotthepony.kstarbound.network
import com.google.common.util.concurrent.ThreadFactoryBuilder
import io.netty.channel.Channel
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.network.packets.DisconnectPacket
import ru.dbotthepony.kstarbound.network.packets.HelloListener
import ru.dbotthepony.kstarbound.network.packets.HelloPacket
import ru.dbotthepony.kstarbound.player.Avatar
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
import java.util.*
abstract class Connection(val side: ConnectionSide, val type: ConnectionType, val localUUID: UUID) : ChannelInboundHandlerAdapter(), IConnectionDetails {
abstract override fun channelRead(ctx: ChannelHandlerContext, msg: Any)
var avatar: Avatar? = null
var character: PlayerEntity? = null
protected var channel: Channel? = null
protected var otherSide: HelloPacket? = null
protected var helloListener: HelloListener? = null
override val protocolVersion: Int
get() = otherSide?.protocolVersion ?: 0
override val engineVersion: String
get() = otherSide?.engineVersion ?: "0.0.0"
override val username: String
get() = otherSide?.username ?: ""
override val password: String
get() = otherSide?.password ?: ""
override val uuid: UUID
get() = otherSide?.uuid ?: EMPTY_UUID
fun bind(channel: Channel) {
check(this.channel == null) { "Already having channel bound" }
channel.config().setOption(ChannelOption.TCP_NODELAY, true)
this.channel = channel
if (side == ConnectionSide.SERVER)
helloListener = HelloListener(this, channel)
}
protected abstract fun onHelloReceived(helloPacket: HelloPacket)
protected abstract fun inGame()
fun helloReceived(helloPacket: HelloPacket) {
LOGGER.info("Handshake received on $side from ${channel?.remoteAddress()}: $helloPacket")
otherSide = helloPacket
if (side == ConnectionSide.SERVER) {
helloListener!!.sendHello(localUUID)
}
onHelloReceived(helloPacket)
initializeHandlers()
}
fun helloFailed() {
channel!!.close()
}
fun initializeHandlers() {
val channel = channel ?: throw IllegalStateException("No network channel is bound")
if (type == ConnectionType.NETWORK) {
channel.pipeline().addLast(PacketRegistry.NATIVE.Inbound(side))
} else {
channel.pipeline().addLast(PacketRegistry.NATIVE.InboundValidator(side))
}
channel.pipeline().addLast(this)
if (type == ConnectionType.NETWORK) {
channel.pipeline().addLast(PacketRegistry.NATIVE.Outbound(side))
channel.pipeline().addFirst(DatagramEncoder)
channel.pipeline().addFirst(DatagramDecoder())
} else {
channel.pipeline().addLast(PacketRegistry.NATIVE.OutboundValidator(side))
}
inGame()
}
fun send(packet: IPacket) {
val channel = channel ?: throw IllegalStateException("No network channel is bound")
channel.write(packet)
}
fun sendAndFlush(packet: IPacket) {
val channel = channel ?: throw IllegalStateException("No network channel is bound")
channel.write(packet)
channel.flush()
}
fun flush() {
val channel = channel ?: throw IllegalStateException("No network channel is bound")
channel.flush()
}
fun disconnect(reason: String) {
if (side == ConnectionSide.CLIENT) {
channel!!.close()
} else {
channel!!.write(DisconnectPacket(reason))
channel!!.flush()
channel!!.close()
}
}
companion object {
private val EMPTY_UUID = UUID(0L, 0L)
private val LOGGER = LogManager.getLogger()
val NIO_POOL by lazy {
NioEventLoopGroup(ThreadFactoryBuilder().setDaemon(true).setNameFormat("Starbound Network IO %d").build())
}
}
}