Start implementing legacy protocol

This commit is contained in:
DBotThePony 2024-02-10 22:55:24 +07:00
parent 075b8259be
commit c2dc7c2e11
Signed by: DBot
GPG Key ID: DCC23B5715498507
29 changed files with 684 additions and 234 deletions

View File

@ -2,7 +2,7 @@ kotlin.code.style=official
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
kotlinVersion=1.9.0 kotlinVersion=1.9.0
kommonsVersion=2.1.1 kommonsVersion=2.1.5
ffiVersion=2.2.13 ffiVersion=2.2.13
lwjglVersion=3.3.0 lwjglVersion=3.3.0

View File

@ -16,6 +16,8 @@ import java.io.BufferedInputStream
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.DataInputStream import java.io.DataInputStream
import java.io.File import java.io.File
import java.net.InetSocketAddress
import java.net.SocketAddress
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.zip.Inflater import java.util.zip.Inflater
@ -83,6 +85,7 @@ fun main() {
client.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID()) client.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID())
//client2.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID()) //client2.connectToLocalServer(server.channels.createLocalChannel(), UUID.randomUUID())
server.channels.createChannel(InetSocketAddress(21060))
} }
//ent.position += Vector2d(y = 14.0, x = -10.0) //ent.position += Vector2d(y = 14.0, x = -10.0)

View File

@ -8,22 +8,20 @@ import io.netty.channel.local.LocalAddress
import io.netty.channel.local.LocalChannel import io.netty.channel.local.LocalChannel
import io.netty.channel.socket.nio.NioSocketChannel import io.netty.channel.socket.nio.NioSocketChannel
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.network.Connection import ru.dbotthepony.kstarbound.network.Connection
import ru.dbotthepony.kstarbound.network.ConnectionSide import ru.dbotthepony.kstarbound.network.ConnectionSide
import ru.dbotthepony.kstarbound.network.ConnectionType import ru.dbotthepony.kstarbound.network.ConnectionType
import ru.dbotthepony.kstarbound.network.IClientPacket import ru.dbotthepony.kstarbound.network.IClientPacket
import ru.dbotthepony.kstarbound.network.packets.HelloListener import ru.dbotthepony.kstarbound.network.packets.ProtocolRequestPacket
import ru.dbotthepony.kstarbound.network.packets.HelloPacket
import java.net.SocketAddress import java.net.SocketAddress
import java.util.* import java.util.*
// client -> server // client -> server
class ClientConnection(val client: StarboundClient, type: ConnectionType, uuid: UUID) : Connection(ConnectionSide.CLIENT, type, uuid) { class ClientConnection(val client: StarboundClient, type: ConnectionType, uuid: UUID) : Connection(ConnectionSide.CLIENT, type, uuid) {
private fun sendHello() { private fun sendHello() {
helloListener = HelloListener(this, channel!!).sendHello(localUUID) isLegacy = true
} sendAndFlush(ProtocolRequestPacket(Starbound.LEGACY_PROTOCOL_VERSION))
override fun onHelloReceived(helloPacket: HelloPacket) {
} }
override fun inGame() { override fun inGame() {

View File

@ -945,7 +945,7 @@ class StarboundClient private constructor(val clientID: Int) : Closeable {
val activeConnection = activeConnection val activeConnection = activeConnection
activeConnection?.send(TrackedPositionPacket(camera.pos)) //activeConnection?.send(TrackedPositionPacket(camera.pos))
uberShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen } uberShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen }
fontShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen } fontShaderPrograms.forValidRefs { it.viewMatrix = viewportMatrixScreen }

View File

@ -15,7 +15,7 @@ import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
class ChunkCellsPacket(val pos: ChunkPos, val data: List<ImmutableCell>) : IClientPacket { class ChunkCellsPacket(val pos: ChunkPos, val data: List<ImmutableCell>) : IClientPacket {
constructor(stream: DataInputStream) : this(stream.readChunkPos(), stream.readCollection { MutableCell().read(stream).immutable() }) constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readChunkPos(), stream.readCollection { MutableCell().read(stream).immutable() })
constructor(chunk: Chunk<*, *>) : this(chunk.pos, ArrayList<ImmutableCell>(CHUNK_SIZE * CHUNK_SIZE).also { constructor(chunk: Chunk<*, *>) : this(chunk.pos, ArrayList<ImmutableCell>(CHUNK_SIZE * CHUNK_SIZE).also {
for (x in 0 until CHUNK_SIZE) { for (x in 0 until CHUNK_SIZE) {
for (y in 0 until CHUNK_SIZE) { for (y in 0 until CHUNK_SIZE) {
@ -24,7 +24,7 @@ class ChunkCellsPacket(val pos: ChunkPos, val data: List<ImmutableCell>) : IClie
} }
}) })
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeVec2i(pos) stream.writeVec2i(pos)
stream.writeCollection(data) { it.write(stream) } stream.writeCollection(data) { it.write(stream) }
} }

View File

@ -9,9 +9,9 @@ import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
class ForgetChunkPacket(val pos: ChunkPos) : IClientPacket { class ForgetChunkPacket(val pos: ChunkPos) : IClientPacket {
constructor(stream: DataInputStream) : this(stream.readChunkPos()) constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readChunkPos())
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeVec2i(pos) stream.writeVec2i(pos)
} }

View File

@ -9,9 +9,9 @@ import java.io.DataOutputStream
import java.util.UUID import java.util.UUID
class ForgetEntityPacket(val uuid: UUID) : IClientPacket { class ForgetEntityPacket(val uuid: UUID) : IClientPacket {
constructor(buff: DataInputStream) : this(buff.readUUID()) constructor(buff: DataInputStream, isLegacy: Boolean) : this(buff.readUUID())
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeUUID(uuid) stream.writeUUID(uuid)
} }

View File

@ -12,10 +12,10 @@ import java.io.DataOutputStream
import java.util.* import java.util.*
data class JoinWorldPacket(val uuid: UUID, val seed: Long, val geometry: WorldGeometry) : IClientPacket { data class JoinWorldPacket(val uuid: UUID, val seed: Long, val geometry: WorldGeometry) : IClientPacket {
constructor(buff: DataInputStream) : this(buff.readUUID(), buff.readLong(), WorldGeometry(buff)) constructor(buff: DataInputStream, isLegacy: Boolean) : this(buff.readUUID(), buff.readLong(), WorldGeometry(buff))
constructor(world: World<*, *>) : this(UUID(0L, 0L), world.seed, world.geometry) constructor(world: World<*, *>) : this(UUID(0L, 0L), world.seed, world.geometry)
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeUUID(uuid) stream.writeUUID(uuid)
stream.writeLong(seed) stream.writeLong(seed)
geometry.write(stream) geometry.write(stream)

View File

@ -5,7 +5,7 @@ import ru.dbotthepony.kstarbound.network.IClientPacket
import java.io.DataOutputStream import java.io.DataOutputStream
object LeaveWorldPacket : IClientPacket { object LeaveWorldPacket : IClientPacket {
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
} }

View File

@ -13,9 +13,9 @@ import java.io.DataOutputStream
import java.util.UUID import java.util.UUID
class SpawnWorldObjectPacket(val uuid: UUID, val data: JsonObject) : IClientPacket { class SpawnWorldObjectPacket(val uuid: UUID, val data: JsonObject) : IClientPacket {
constructor(stream: DataInputStream) : this(stream.readUUID(), stream.readJsonObject()) constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readUUID(), stream.readJsonObject())
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeUUID(uuid) stream.writeUUID(uuid)
stream.writeJsonObject(data) stream.writeJsonObject(data)
} }

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.kstarbound.defs
import ru.dbotthepony.kommons.io.writeStruct2i
import ru.dbotthepony.kommons.vector.Vector2i
import ru.dbotthepony.kstarbound.io.readVec2i
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import java.io.DataInputStream
import java.io.DataOutputStream
@JsonFactory
data class CelestialBaseInformation(
val planetOrbitalLevels: Int = 1,
val satelliteOrbitalLevels: Int = 1,
val chunkSize: Int = 1,
val xyCoordRange: Vector2i = Vector2i.ZERO,
val zCoordRange: Vector2i = Vector2i.ZERO,
) {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
stream.readInt(),
stream.readInt(),
stream.readInt(),
stream.readVec2i(),
stream.readVec2i(),
)
fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeInt(planetOrbitalLevels)
stream.writeInt(satelliteOrbitalLevels)
stream.writeInt(chunkSize)
stream.writeStruct2i(xyCoordRange)
stream.writeStruct2i(zCoordRange)
}
}

View File

@ -0,0 +1,59 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableSet
import ru.dbotthepony.kommons.collect.immutableSet
import ru.dbotthepony.kommons.io.readBinaryString
import ru.dbotthepony.kommons.io.readCollection
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeCollection
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import java.io.DataInputStream
import java.io.DataOutputStream
@JsonFactory
data class ShipUpgrades(
val shipLevel: Int = 0,
val maxFuel: Int = 0,
val crewSize: Int = 0,
val fuelEfficiency: Double = 1.0,
val shipSpeed: Int = 0,
val capabilities: ImmutableSet<String> = ImmutableSet.of()
) {
fun apply(upgrades: ShipUpgrades): ShipUpgrades {
return ShipUpgrades(
shipLevel = shipLevel.coerceAtLeast(upgrades.shipLevel),
maxFuel = maxFuel.coerceAtLeast(upgrades.maxFuel),
crewSize = crewSize.coerceAtLeast(upgrades.crewSize),
fuelEfficiency = fuelEfficiency.coerceAtLeast(upgrades.fuelEfficiency),
shipSpeed = shipSpeed.coerceAtLeast(upgrades.shipSpeed),
capabilities = immutableSet { capabilities.forEach(::accept); upgrades.capabilities.forEach(::accept) }
)
}
fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeInt(shipLevel)
stream.writeInt(maxFuel)
stream.writeInt(crewSize)
if (isLegacy)
stream.writeFloat(fuelEfficiency.toFloat())
else
stream.writeDouble(fuelEfficiency)
stream.writeInt(shipSpeed)
stream.writeCollection(capabilities) { writeBinaryString(it) }
}
companion object {
fun read(stream: DataInputStream, isLegacy: Boolean): ShipUpgrades {
return ShipUpgrades(
stream.readInt(),
stream.readInt(),
stream.readInt(),
if (isLegacy) stream.readFloat().toDouble() else stream.readDouble(),
stream.readInt(),
ImmutableSet.copyOf(stream.readCollection { readBinaryString() })
)
}
}
}

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.kstarbound.io
import ru.dbotthepony.kommons.io.readVarInt
import ru.dbotthepony.kommons.io.writeVarInt
import java.io.InputStream
import java.io.OutputStream
class ByteKey(private vararg val bytes: Byte) {
override fun equals(other: Any?): Boolean {
return this === other || other is ByteKey && other.bytes.contentEquals(bytes)
}
fun write(stream: OutputStream) {
stream.writeVarInt(bytes.size)
stream.write(bytes)
}
override fun hashCode(): Int {
return bytes.contentHashCode()
}
override fun toString(): String {
return "ByteKey[${bytes.joinToString(", ")}]"
}
}
fun InputStream.readByteKey(): ByteKey {
return ByteKey(*ByteArray(readVarInt()).also { read(it) })
}
fun OutputStream.writeByteKey(key: ByteKey) {
key.write(this)
}

View File

@ -76,11 +76,11 @@ enum class ConnectionType {
} }
fun interface IPacketReader<T : IPacket> { fun interface IPacketReader<T : IPacket> {
fun read(stream: DataInputStream): T fun read(stream: DataInputStream, isLegacy: Boolean): T
} }
interface IPacket { interface IPacket {
fun write(stream: DataOutputStream) fun write(stream: DataOutputStream, isLegacy: Boolean)
} }
interface IServerPacket : IPacket { interface IServerPacket : IPacket {

View File

@ -7,111 +7,108 @@ import io.netty.channel.ChannelInboundHandlerAdapter
import io.netty.channel.ChannelOption import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup import io.netty.channel.nio.NioEventLoopGroup
import org.apache.logging.log4j.LogManager 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.player.Avatar
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
import java.io.Closeable
import java.util.* import java.util.*
import kotlin.properties.Delegates
abstract class Connection(val side: ConnectionSide, val type: ConnectionType, val localUUID: UUID) : ChannelInboundHandlerAdapter(), IConnectionDetails { abstract class Connection(val side: ConnectionSide, val type: ConnectionType, val localUUID: UUID) : ChannelInboundHandlerAdapter(), Closeable {
abstract override fun channelRead(ctx: ChannelHandlerContext, msg: Any) abstract override fun channelRead(ctx: ChannelHandlerContext, msg: Any)
var avatar: Avatar? = null var avatar: Avatar? = null
var character: PlayerEntity? = null var character: PlayerEntity? = null
protected var channel: Channel? = null var channel: Channel by Delegates.notNull()
protected var otherSide: HelloPacket? = null protected set
protected var helloListener: HelloListener? = null
override val protocolVersion: Int var isLegacy: Boolean = true
get() = otherSide?.protocolVersion ?: 0 protected set
override val engineVersion: String
get() = otherSide?.engineVersion ?: "0.0.0" private val handshakeValidator = PacketRegistry.HANDSHAKE.Validator(side)
override val username: String private val handshakeSerializer = PacketRegistry.HANDSHAKE.Serializer(side)
get() = otherSide?.username ?: ""
override val password: String private val nativeValidator = PacketRegistry.NATIVE.Validator(side)
get() = otherSide?.password ?: "" private val nativeSerializer = PacketRegistry.NATIVE.Serializer(side)
override val uuid: UUID
get() = otherSide?.uuid ?: EMPTY_UUID private val legacyValidator = PacketRegistry.LEGACY.Validator(side)
private val legacySerializer = PacketRegistry.LEGACY.Serializer(side)
fun setupLegacy() {
LOGGER.info("Handshake successful from ${channel.remoteAddress()}, channel is using legacy protocol")
if (type == ConnectionType.MEMORY) {
channel.pipeline().remove(handshakeValidator)
channel.pipeline().addFirst(legacyValidator)
} else {
channel.pipeline().remove(handshakeSerializer)
channel.pipeline().addFirst(legacySerializer)
}
isLegacy = true
}
fun setupNative() {
LOGGER.info("Handshake successful from ${channel.remoteAddress()}, channel is using native protocol")
if (type == ConnectionType.MEMORY) {
channel.pipeline().remove(handshakeValidator)
channel.pipeline().addFirst(nativeValidator)
} else {
channel.pipeline().remove(handshakeSerializer)
channel.pipeline().addFirst(nativeSerializer)
}
isLegacy = false
}
var disconnectionReason: String? = null
private set
fun bind(channel: Channel) { fun bind(channel: Channel) {
check(this.channel == null) { "Already having channel bound" }
channel.config().setOption(ChannelOption.TCP_NODELAY, true) channel.config().setOption(ChannelOption.TCP_NODELAY, true)
this.channel = channel this.channel = channel
if (side == ConnectionSide.SERVER) if (type == ConnectionType.MEMORY)
helloListener = HelloListener(this, channel) channel.pipeline().addFirst(handshakeValidator)
} else
channel.pipeline().addFirst(handshakeSerializer)
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) channel.pipeline().addLast(this)
if (type == ConnectionType.NETWORK) { channel.closeFuture().addListener {
channel.pipeline().addLast(PacketRegistry.NATIVE.Outbound(side)) LOGGER.info("Connection to ${channel.remoteAddress()} is closed")
channel.pipeline().addFirst(DatagramEncoder)
channel.pipeline().addFirst(DatagramDecoder())
} else {
channel.pipeline().addLast(PacketRegistry.NATIVE.OutboundValidator(side))
} }
inGame()
} }
protected abstract fun inGame()
fun send(packet: IPacket) { fun send(packet: IPacket) {
val channel = channel ?: throw IllegalStateException("No network channel is bound")
channel.write(packet) channel.write(packet)
} }
fun sendAndFlush(packet: IPacket) { fun sendAndFlush(packet: IPacket) {
val channel = channel ?: throw IllegalStateException("No network channel is bound")
channel.write(packet) channel.write(packet)
channel.flush() channel.flush()
} }
fun flush() { fun flush() {
val channel = channel ?: throw IllegalStateException("No network channel is bound")
channel.flush() channel.flush()
} }
fun disconnect(reason: String) { fun disconnect(reason: String) {
if (side == ConnectionSide.CLIENT) { if (side == ConnectionSide.CLIENT) {
channel!!.close() channel.close()
} else { } else {
channel!!.write(DisconnectPacket(reason)) channel.flush()
channel!!.flush() channel.close()
channel!!.close()
} }
} }
override fun close() {
channel.close()
}
companion object { companion object {
private val EMPTY_UUID = UUID(0L, 0L) private val EMPTY_UUID = UUID(0L, 0L)
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()

View File

@ -3,26 +3,40 @@ package ru.dbotthepony.kstarbound.network
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufInputStream import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream import io.netty.buffer.ByteBufOutputStream
import io.netty.channel.ChannelDuplexHandler
import io.netty.channel.ChannelHandlerContext import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import io.netty.channel.ChannelOutboundHandlerAdapter
import io.netty.channel.ChannelPromise import io.netty.channel.ChannelPromise
import it.unimi.dsi.fastutil.bytes.ByteArrayList
import it.unimi.dsi.fastutil.io.FastByteArrayInputStream
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.io.readSignedVarInt
import ru.dbotthepony.kommons.io.writeSignedVarInt
import ru.dbotthepony.kstarbound.client.network.packets.ForgetChunkPacket import ru.dbotthepony.kstarbound.client.network.packets.ForgetChunkPacket
import ru.dbotthepony.kstarbound.client.network.packets.ChunkCellsPacket import ru.dbotthepony.kstarbound.client.network.packets.ChunkCellsPacket
import ru.dbotthepony.kstarbound.client.network.packets.ForgetEntityPacket import ru.dbotthepony.kstarbound.client.network.packets.ForgetEntityPacket
import ru.dbotthepony.kstarbound.client.network.packets.JoinWorldPacket import ru.dbotthepony.kstarbound.client.network.packets.JoinWorldPacket
import ru.dbotthepony.kstarbound.client.network.packets.SpawnWorldObjectPacket import ru.dbotthepony.kstarbound.client.network.packets.SpawnWorldObjectPacket
import ru.dbotthepony.kstarbound.network.packets.DisconnectPacket import ru.dbotthepony.kstarbound.network.packets.serverbound.ClientConnectPacket
import ru.dbotthepony.kstarbound.network.packets.clientbound.ConnectSuccessPacket
import ru.dbotthepony.kstarbound.network.packets.clientbound.HandshakeChallengePacket
import ru.dbotthepony.kstarbound.network.packets.serverbound.HandshakeResponsePacket
import ru.dbotthepony.kstarbound.network.packets.ProtocolRequestPacket
import ru.dbotthepony.kstarbound.network.packets.ProtocolResponsePacket
import ru.dbotthepony.kstarbound.network.packets.clientbound.ServerDisconnectPacket
import ru.dbotthepony.kstarbound.server.network.packets.TrackedPositionPacket import ru.dbotthepony.kstarbound.server.network.packets.TrackedPositionPacket
import ru.dbotthepony.kstarbound.server.network.packets.TrackedSizePacket import ru.dbotthepony.kstarbound.server.network.packets.TrackedSizePacket
import java.io.BufferedInputStream
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.InputStream
import java.util.zip.InflaterInputStream
import kotlin.math.absoluteValue
import kotlin.reflect.KClass import kotlin.reflect.KClass
class PacketRegistry { class PacketRegistry(val isLegacy: Boolean) {
private val packets = ArrayList<Type<*>>() private val packets = ArrayList<Type<*>?>()
private val clazz2Type = Reference2ObjectOpenHashMap<KClass<*>, Type<*>>() private val clazz2Type = Reference2ObjectOpenHashMap<KClass<*>, Type<*>>()
private data class Type<T : IPacket>(val id: Int, val type: KClass<T>, val factory: IPacketReader<T>, val direction: PacketDirection) private data class Type<T : IPacket>(val id: Int, val type: KClass<T>, val factory: IPacketReader<T>, val direction: PacketDirection)
@ -47,34 +61,110 @@ class PacketRegistry {
return add(T::class, reader, direction) return add(T::class, reader, direction)
} }
inner class Inbound(val side: ConnectionSide) : ChannelInboundHandlerAdapter() { private fun skip(amount: Int = 1) {
for (i in 0 until amount) {
packets.add(null)
}
}
inner class Serializer(val side: ConnectionSide) : ChannelDuplexHandler() {
private val backlog = ByteArrayList()
private var discardBytes = 0
private var readableBytes = 0
private var isCompressed = false
private var readingType: Type<*>? = null
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) { override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
if (msg is ByteBuf) { if (msg is ByteBuf) {
val packetType = msg.readUnsignedByte().toInt() try {
val type = packets.getOrNull(packetType) while (msg.readableBytes() > 0) {
if (discardBytes > 0) {
val toSkip = discardBytes.coerceAtMost(msg.readableBytes())
discardBytes -= toSkip
msg.skipBytes(toSkip)
continue
}
if (type == null) { if (readingType != null) {
LOGGER.error("Unknown packet type $packetType!") while (readableBytes > 0 && msg.readableBytes() > 0) {
msg.release() backlog.add(msg.readByte())
} else if (!type.direction.acceptedOn(side)) { readableBytes--
LOGGER.error("Packet ${type.type} can not be accepted on side $side!") }
msg.release()
} else { if (readableBytes == 0) {
try { val stream: InputStream
ctx.fireChannelRead(type.factory.read(DataInputStream(ByteBufInputStream(msg))))
} catch (err: Throwable) { if (isCompressed) {
LOGGER.error("Error while reading incoming packet from network", err) stream = BufferedInputStream(InflaterInputStream(FastByteArrayInputStream(backlog.elements(), 0, backlog.size)))
} finally { } else {
msg.release() stream = FastByteArrayInputStream(backlog.elements(), 0, backlog.size)
}
try {
ctx.fireChannelRead(readingType!!.factory.read(DataInputStream(stream), isLegacy))
} catch (err: Throwable) {
LOGGER.error("Error while reading incoming packet from network (type ${readingType!!.id}; ${readingType!!.type})", err)
}
backlog.clear()
readingType = null
isCompressed = false
}
continue
}
val stream = DataInputStream(ByteBufInputStream(msg))
val packetType = stream.readUnsignedByte()
val dataLength = stream.readSignedVarInt()
val type = packets.getOrNull(packetType)
if (type == null) {
LOGGER.error("Unknown packet type $packetType! Discarding ${dataLength.absoluteValue} bytes")
discardBytes = dataLength.absoluteValue
} else if (!type.direction.acceptedOn(side)) {
LOGGER.error("Packet type $packetType (${type.type}) can not be accepted on side $side! Discarding ${dataLength.absoluteValue} bytes")
discardBytes = dataLength.absoluteValue
} else {
readingType = type
readableBytes = dataLength.absoluteValue
isCompressed = dataLength < 0
}
} }
} finally {
msg.release()
} }
} else { } else {
super.channelRead(ctx, msg) super.channelRead(ctx, msg)
} }
} }
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
val type = clazz2Type[msg::class]
if (type == null) {
LOGGER.error("Unknown outgoing message type ${msg::class}, it will not reach the other side.")
} else if (!type.direction.acceptedOn(side.opposite)) {
LOGGER.error("Packet ${type.type} can not be accepted on side ${side.opposite}, refusing to send it!")
} else {
val stream = FastByteArrayOutputStream()
(msg as IPacket).write(DataOutputStream(stream), isLegacy)
if (isLegacy)
check(stream.length > 0) { "Packet $msg didn't write any data to network, this is not allowed by legacy protocol" }
val buff = ctx.alloc().buffer(stream.length + 5)
val stream2 = ByteBufOutputStream(buff)
stream2.writeByte(type.id)
stream2.writeSignedVarInt(stream.length)
stream2.write(stream.array, 0, stream.length)
ctx.write(buff, promise)
}
}
} }
inner class InboundValidator(val side: ConnectionSide) : ChannelInboundHandlerAdapter() { inner class Validator(val side: ConnectionSide) : ChannelDuplexHandler() {
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) { override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
val type = clazz2Type[msg::class] val type = clazz2Type[msg::class]
@ -90,26 +180,7 @@ class PacketRegistry {
} }
} }
} }
}
inner class Outbound(val side: ConnectionSide) : ChannelOutboundHandlerAdapter() {
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
val type = clazz2Type[msg::class]
if (type == null) {
LOGGER.error("Unknown outgoing message type ${msg::class}, it will not reach the other side.")
} else if (!type.direction.acceptedOn(side.opposite)) {
LOGGER.error("Packet ${type.type} can not be accepted on side ${side.opposite}, refusing to send it!")
} else {
val buff = ctx.alloc().buffer(2048)
buff.writeByte(type.id)
(msg as IPacket).write(DataOutputStream(ByteBufOutputStream(buff)))
ctx.write(buff, promise)
}
}
}
inner class OutboundValidator(val side: ConnectionSide) : ChannelOutboundHandlerAdapter() {
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) { override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
val type = clazz2Type[msg::class] val type = clazz2Type[msg::class]
@ -126,12 +197,11 @@ class PacketRegistry {
companion object { companion object {
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
val NATIVE = PacketRegistry() val NATIVE = PacketRegistry(false)
val LEGACY = PacketRegistry() val LEGACY = PacketRegistry(true)
val HANDSHAKE = PacketRegistry() val HANDSHAKE = PacketRegistry(false)
init { init {
NATIVE.add(::DisconnectPacket)
NATIVE.add(::JoinWorldPacket) NATIVE.add(::JoinWorldPacket)
NATIVE.add(::ChunkCellsPacket) NATIVE.add(::ChunkCellsPacket)
NATIVE.add(::ForgetChunkPacket) NATIVE.add(::ForgetChunkPacket)
@ -139,6 +209,96 @@ class PacketRegistry {
NATIVE.add(::TrackedSizePacket) NATIVE.add(::TrackedSizePacket)
NATIVE.add(::SpawnWorldObjectPacket) NATIVE.add(::SpawnWorldObjectPacket)
NATIVE.add(::ForgetEntityPacket) NATIVE.add(::ForgetEntityPacket)
HANDSHAKE.add(::ProtocolRequestPacket)
HANDSHAKE.add(::ProtocolResponsePacket)
LEGACY.skip() // ProtocolRequest
LEGACY.skip() // ProtocolResponse
// Packets sent universe server -> universe client
LEGACY.add(::ServerDisconnectPacket) // ServerDisconnect
LEGACY.add(::ConnectSuccessPacket) // ConnectSuccess
LEGACY.skip() // ConnectFailure
LEGACY.add(::HandshakeChallengePacket) // HandshakeChallenge
LEGACY.skip() // ChatReceive
LEGACY.skip() // UniverseTimeUpdate
LEGACY.skip() // CelestialResponse
LEGACY.skip() // PlayerWarpResult
LEGACY.skip() // PlanetTypeUpdate
LEGACY.skip() // Pause
LEGACY.skip() // ServerInfo
// Packets sent universe client -> universe server
LEGACY.add(::ClientConnectPacket) // ClientConnect
LEGACY.skip() // ClientDisconnectRequest
LEGACY.add(::HandshakeResponsePacket) // HandshakeResponse
LEGACY.skip() // PlayerWarp
LEGACY.skip() // FlyShip
LEGACY.skip() // ChatSend
LEGACY.skip() // CelestialRequest
// Packets sent bidirectionally between the universe client and the universe
// server
LEGACY.skip() // ClientContextUpdate
// Packets sent world server -> world client
LEGACY.skip() // WorldStart
LEGACY.skip() // WorldStop
LEGACY.skip() // WorldLayoutUpdate
LEGACY.skip() // WorldParametersUpdate
LEGACY.skip() // CentralStructureUpdate
LEGACY.skip() // TileArrayUpdate
LEGACY.skip() // TileUpdate
LEGACY.skip() // TileLiquidUpdate
LEGACY.skip() // TileDamageUpdate
LEGACY.skip() // TileModificationFailure
LEGACY.skip() // GiveItem
LEGACY.skip() // EnvironmentUpdate
LEGACY.skip() // UpdateTileProtection
LEGACY.skip() // SetDungeonGravity
LEGACY.skip() // SetDungeonBreathable
LEGACY.skip() // SetPlayerStart
LEGACY.skip() // FindUniqueEntityResponse
LEGACY.skip() // Pong
// Packets sent world client -> world server
LEGACY.skip() // ModifyTileList
LEGACY.skip() // DamageTileGroup
LEGACY.skip() // CollectLiquid
LEGACY.skip() // RequestDrop
LEGACY.skip() // SpawnEntity
LEGACY.skip() // ConnectWire
LEGACY.skip() // DisconnectAllWires
LEGACY.skip() // WorldClientStateUpdate
LEGACY.skip() // FindUniqueEntity
LEGACY.skip() // WorldStartAcknowledge
LEGACY.skip() // Ping
// Packets sent bidirectionally between world client and world server
LEGACY.skip() // EntityCreate
LEGACY.skip() // EntityUpdateSet
LEGACY.skip() // EntityDestroy
LEGACY.skip() // EntityInteract
LEGACY.skip() // EntityInteractResult
LEGACY.skip() // HitRequest
LEGACY.skip() // DamageRequest
LEGACY.skip() // DamageNotification
LEGACY.skip() // EntityMessage
LEGACY.skip() // EntityMessageResponse
LEGACY.skip() // UpdateWorldProperties
LEGACY.skip() // StepUpdate
// Packets sent system server -> system client
LEGACY.skip() // SystemWorldStart
LEGACY.skip() // SystemWorldUpdate
LEGACY.skip() // SystemObjectCreate
LEGACY.skip() // SystemObjectDestroy
LEGACY.skip() // SystemShipCreate
LEGACY.skip() // SystemShipDestroy
// Packets sent system client -> system server
LEGACY.skip() // SystemObjectSpawn
} }
} }
} }

View File

@ -0,0 +1,30 @@
package ru.dbotthepony.kstarbound.network.packets
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.CelestialBaseInformation
import ru.dbotthepony.kstarbound.network.IServerPacket
import ru.dbotthepony.kstarbound.server.ServerConnection
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.UUID
data class ProtocolRequestPacket(val version: Int) : IServerPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readInt())
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeInt(version)
}
override fun play(connection: ServerConnection) {
if (version == Starbound.NATIVE_PROTOCOL_VERSION) {
connection.sendAndFlush(ProtocolResponsePacket(true))
connection.setupNative()
} else if (version == Starbound.LEGACY_PROTOCOL_VERSION) {
connection.sendAndFlush(ProtocolResponsePacket(true))
connection.setupLegacy()
} else {
connection.sendAndFlush(ProtocolResponsePacket(false))
connection.close()
}
}
}

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.kstarbound.network.packets
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.defs.player.ShipUpgrades
import ru.dbotthepony.kstarbound.network.IClientPacket
import ru.dbotthepony.kstarbound.network.packets.serverbound.ClientConnectPacket
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.*
import kotlin.collections.HashMap
data class ProtocolResponsePacket(val allowed: Boolean) : IClientPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readBoolean())
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeBoolean(allowed)
}
override fun play(connection: ClientConnection) {
if (allowed) {
if (connection.isLegacy) {
connection.setupLegacy()
connection.sendAndFlush(
ClientConnectPacket(
ByteArray(ClientConnectPacket.DIGEST_SIZE),
allowAssetMismatch = true,
playerUuid = UUID(0L, 0L),
playerName = "Test Name",
playerSpecies = "hylotl",
shipChunks = HashMap(),
shipUpgrades = ShipUpgrades(),
introComplete = false,
account = ""
)
)
} else {
connection.setupNative()
}
}
}
}

View File

@ -1,88 +0,0 @@
package ru.dbotthepony.kstarbound.network.packets
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufInputStream
import io.netty.buffer.ByteBufOutputStream
import io.netty.channel.Channel
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import ru.dbotthepony.kommons.io.readUUID
import ru.dbotthepony.kommons.io.writeUUID
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.network.Connection
import ru.dbotthepony.kstarbound.network.IClientPacket
import ru.dbotthepony.kstarbound.network.IConnectionDetails
import ru.dbotthepony.kstarbound.network.IPacket
import ru.dbotthepony.kstarbound.network.IServerPacket
import ru.dbotthepony.kstarbound.network.readUTF
import ru.dbotthepony.kstarbound.network.writeUTF
import ru.dbotthepony.kstarbound.server.ServerConnection
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.UUID
data class HelloPacket(
override val protocolVersion: Int,
override val engineVersion: String,
override val username: String,
override val password: String,
override val uuid: UUID
) : IPacket, IConnectionDetails {
constructor(buff: DataInputStream) : this(buff.readInt(), buff.readUTF(), buff.readUTF(), buff.readUTF(), buff.readUUID())
override fun write(stream: DataOutputStream) {
stream.writeInt(protocolVersion)
stream.writeUTF(engineVersion)
stream.writeUTF(username)
stream.writeUTF(password)
stream.writeUUID(uuid)
}
}
data class DisconnectPacket(val reason: String) : IServerPacket, IClientPacket {
constructor(buff: DataInputStream) : this(buff.readUTF())
override fun write(stream: DataOutputStream) {
stream.writeUTF(reason)
}
override fun play(connection: ServerConnection) {
}
override fun play(connection: ClientConnection) {
}
}
class HelloListener(val connection: Connection, private val channel: Channel) : ChannelInboundHandlerAdapter() {
init {
channel.pipeline().addFirst(this)
}
fun sendHello(uuid: UUID, username: String = "", password: String = ""): HelloListener {
val buf = channel.config().allocator.buffer()
buf.writeUTF("KSTARBOUND HELLO")
HelloPacket(Starbound.NATIVE_PROTOCOL_VERSION, Starbound.ENGINE_VERSION, username, password, uuid).write(DataOutputStream(ByteBufOutputStream(buf)))
channel.write(buf)
channel.flush()
return this
}
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
if (msg is ByteBuf) {
if (msg.readUTF() != "KSTARBOUND HELLO") {
connection.helloFailed()
} else {
connection.helloReceived(HelloPacket(DataInputStream(ByteBufInputStream(msg))))
}
msg.release()
} else {
connection.helloFailed()
}
channel.pipeline().remove(this)
}
}

View File

@ -0,0 +1,20 @@
package ru.dbotthepony.kstarbound.network.packets.clientbound
import ru.dbotthepony.kommons.io.readBinaryString
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.network.IClientPacket
import java.io.DataInputStream
import java.io.DataOutputStream
class ConnectFailurePacket(val reason: String = "") : IClientPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readBinaryString())
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeBinaryString(reason)
}
override fun play(connection: ClientConnection) {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,28 @@
package ru.dbotthepony.kstarbound.network.packets.clientbound
import ru.dbotthepony.kommons.io.readUUID
import ru.dbotthepony.kommons.io.writeUUID
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.defs.CelestialBaseInformation
import ru.dbotthepony.kstarbound.network.IClientPacket
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.UUID
class ConnectSuccessPacket(val connectionID: Int, val serverUUID: UUID, val celestialInformation: CelestialBaseInformation) : IClientPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
stream.readUnsignedShort(),
stream.readUUID(),
CelestialBaseInformation(stream, isLegacy)
)
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeShort(connectionID)
stream.writeUUID(serverUUID)
celestialInformation.write(stream, isLegacy)
}
override fun play(connection: ClientConnection) {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,20 @@
package ru.dbotthepony.kstarbound.network.packets.clientbound
import ru.dbotthepony.kommons.io.readByteArray
import ru.dbotthepony.kommons.io.writeByteArray
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.network.IClientPacket
import java.io.DataInputStream
import java.io.DataOutputStream
class HandshakeChallengePacket(val passwordSalt: ByteArray) : IClientPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readByteArray())
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByteArray(passwordSalt)
}
override fun play(connection: ClientConnection) {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,20 @@
package ru.dbotthepony.kstarbound.network.packets.clientbound
import ru.dbotthepony.kommons.io.readBinaryString
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kstarbound.client.ClientConnection
import ru.dbotthepony.kstarbound.network.IClientPacket
import java.io.DataInputStream
import java.io.DataOutputStream
class ServerDisconnectPacket(val reason: String = "") : IClientPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readBinaryString())
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeBinaryString(reason)
}
override fun play(connection: ClientConnection) {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,68 @@
package ru.dbotthepony.kstarbound.network.packets.serverbound
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kommons.io.readBinaryString
import ru.dbotthepony.kommons.io.readByteArray
import ru.dbotthepony.kommons.io.readKOptional
import ru.dbotthepony.kommons.io.readMap
import ru.dbotthepony.kommons.io.readUUID
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeByteArray
import ru.dbotthepony.kommons.io.writeKOptional
import ru.dbotthepony.kommons.io.writeMap
import ru.dbotthepony.kommons.io.writeUUID
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kstarbound.defs.CelestialBaseInformation
import ru.dbotthepony.kstarbound.defs.player.ShipUpgrades
import ru.dbotthepony.kstarbound.io.ByteKey
import ru.dbotthepony.kstarbound.io.readByteKey
import ru.dbotthepony.kstarbound.io.writeByteKey
import ru.dbotthepony.kstarbound.network.IServerPacket
import ru.dbotthepony.kstarbound.network.packets.clientbound.ConnectSuccessPacket
import ru.dbotthepony.kstarbound.server.ServerConnection
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.InputStream
import java.util.*
import kotlin.collections.HashMap
data class ClientConnectPacket(
val assetDigest: ByteArray, val allowAssetMismatch: Boolean,
val playerUuid: UUID, val playerName: String, val playerSpecies: String,
val shipChunks: Map<ByteKey, KOptional<ByteArray>>, val shipUpgrades: ShipUpgrades, val introComplete: Boolean,
val account: String
) : IServerPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(
stream.readByteArray(),
stream.readBoolean(),
stream.readUUID(),
stream.readBinaryString(),
stream.readBinaryString(),
stream.readMap(InputStream::readByteKey, { readKOptional { readByteArray() } }, { HashMap(it) }),
ShipUpgrades.read(stream, isLegacy),
stream.readBoolean(),
stream.readBinaryString()
)
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByteArray(assetDigest)
stream.writeBoolean(allowAssetMismatch)
stream.writeUUID(playerUuid)
stream.writeBinaryString(playerName)
stream.writeBinaryString(playerSpecies)
stream.writeMap(shipChunks, { writeByteKey(it) }, { writeKOptional(it) { writeByteArray(it) } })
shipUpgrades.write(stream, isLegacy)
stream.writeBoolean(introComplete)
stream.writeBinaryString(account)
}
override fun play(connection: ServerConnection) {
LOGGER.info("Client connection request received from ${connection.channel.remoteAddress()}, Player $playerName/$playerUuid (account '$account')")
connection.sendAndFlush(ConnectSuccessPacket(4, UUID(4L, 4L), CelestialBaseInformation()))
}
companion object {
const val DIGEST_SIZE = 32
private val LOGGER = LogManager.getLogger()
}
}

View File

@ -0,0 +1,20 @@
package ru.dbotthepony.kstarbound.network.packets.serverbound
import ru.dbotthepony.kommons.io.readByteArray
import ru.dbotthepony.kommons.io.writeByteArray
import ru.dbotthepony.kstarbound.network.IServerPacket
import ru.dbotthepony.kstarbound.server.ServerConnection
import java.io.DataInputStream
import java.io.DataOutputStream
class HandshakeResponsePacket(val passwordHash: ByteArray) : IServerPacket {
constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readByteArray())
override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByteArray(passwordHash)
}
override fun play(connection: ServerConnection) {
TODO("Not yet implemented")
}
}

View File

@ -41,9 +41,15 @@ class ServerChannels(val server: StarboundServer) : Closeable {
val channel = ServerBootstrap().channel(LocalServerChannel::class.java).group(Connection.NIO_POOL).childHandler(object : ChannelInitializer<Channel>() { val channel = ServerBootstrap().channel(LocalServerChannel::class.java).group(Connection.NIO_POOL).childHandler(object : ChannelInitializer<Channel>() {
override fun initChannel(ch: Channel) { override fun initChannel(ch: Channel) {
LOGGER.info("Incoming connection from ${ch.remoteAddress()}") LOGGER.info("Incoming connection from ${ch.remoteAddress()}")
val connection = ServerConnection(server, ConnectionType.MEMORY)
connections.add(connection) try {
connection.bind(ch) val connection = ServerConnection(server, ConnectionType.MEMORY)
connections.add(connection)
connection.bind(ch)
} catch (err: Throwable) {
LOGGER.error("Error while accepting new connection from ${ch.remoteAddress()}", err)
ch.close()
}
} }
}).bind(LocalAddress.ANY).syncUninterruptibly() }).bind(LocalAddress.ANY).syncUninterruptibly()
@ -64,9 +70,15 @@ class ServerChannels(val server: StarboundServer) : Closeable {
val channel = ServerBootstrap().channel(NioServerSocketChannel::class.java).group(Connection.NIO_POOL).childHandler(object : ChannelInitializer<Channel>() { val channel = ServerBootstrap().channel(NioServerSocketChannel::class.java).group(Connection.NIO_POOL).childHandler(object : ChannelInitializer<Channel>() {
override fun initChannel(ch: Channel) { override fun initChannel(ch: Channel) {
LOGGER.info("Incoming connection from ${ch.remoteAddress()}") LOGGER.info("Incoming connection from ${ch.remoteAddress()}")
val connection = ServerConnection(server, ConnectionType.NETWORK)
connections.add(connection) try {
connection.bind(ch) val connection = ServerConnection(server, ConnectionType.NETWORK)
connections.add(connection)
connection.bind(ch)
} catch (err: Throwable) {
LOGGER.error("Error while accepting new connection from ${ch.remoteAddress()}", err)
ch.close()
}
} }
}).bind(localAddress).syncUninterruptibly() }).bind(localAddress).syncUninterruptibly()

View File

@ -14,7 +14,6 @@ import ru.dbotthepony.kstarbound.network.Connection
import ru.dbotthepony.kstarbound.network.ConnectionSide import ru.dbotthepony.kstarbound.network.ConnectionSide
import ru.dbotthepony.kstarbound.network.ConnectionType import ru.dbotthepony.kstarbound.network.ConnectionType
import ru.dbotthepony.kstarbound.network.IServerPacket import ru.dbotthepony.kstarbound.network.IServerPacket
import ru.dbotthepony.kstarbound.network.packets.HelloPacket
import ru.dbotthepony.kstarbound.server.world.ServerWorld import ru.dbotthepony.kstarbound.server.world.ServerWorld
import ru.dbotthepony.kstarbound.world.ChunkPos import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.IChunkListener import ru.dbotthepony.kstarbound.world.IChunkListener
@ -115,7 +114,7 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
} }
fun tick() { fun tick() {
channel?.flush() channel.flush()
val world = world val world = world
if (world == null) { if (world == null) {
@ -154,9 +153,6 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn
server.playerInGame(this) server.playerInGame(this)
} }
override fun onHelloReceived(helloPacket: HelloPacket) {
}
companion object { companion object {
private val LOGGER = LogManager.getLogger() private val LOGGER = LogManager.getLogger()
} }

View File

@ -9,9 +9,9 @@ import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
data class TrackedPositionPacket(val pos: Vector2d) : IServerPacket { data class TrackedPositionPacket(val pos: Vector2d) : IServerPacket {
constructor(stream: DataInputStream) : this(stream.readVec2d()) constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readVec2d())
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeVec2d(pos) stream.writeVec2d(pos)
} }

View File

@ -6,14 +6,14 @@ import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
data class TrackedSizePacket(val width: Int, val height: Int) : IServerPacket { data class TrackedSizePacket(val width: Int, val height: Int) : IServerPacket {
constructor(stream: DataInputStream) : this(stream.readUnsignedByte(), stream.readUnsignedByte()) constructor(stream: DataInputStream, isLegacy: Boolean) : this(stream.readUnsignedByte(), stream.readUnsignedByte())
init { init {
require(width in 1 .. 12) { "Bad chunk width to track: $width" } require(width in 1 .. 12) { "Bad chunk width to track: $width" }
require(height in 1 .. 12) { "Bad chunk height to track: $height" } require(height in 1 .. 12) { "Bad chunk height to track: $height" }
} }
override fun write(stream: DataOutputStream) { override fun write(stream: DataOutputStream, isLegacy: Boolean) {
stream.writeByte(width) stream.writeByte(width)
stream.writeByte(height) stream.writeByte(height)
} }