From dc9947850d0d3e1a689ac0e418aab3f9ca93bc7c Mon Sep 17 00:00:00 2001 From: Bixilon Date: Sun, 9 Jan 2022 21:13:53 +0100 Subject: [PATCH] network: correct exception for packet not implemented --- .../data/registries/versions/Versions.kt | 35 ++++++++++++------- .../S2CPacketNotImplementedException.kt | 10 ++++-- .../client/pipeline/ClientPacketHandler.kt | 12 +++---- .../client/pipeline/ExceptionHandler.kt | 15 +++++--- .../network/client/pipeline/QueuedS2CP.kt | 22 ++++++++++++ .../client/pipeline/encoding/PacketDecoder.kt | 9 +++-- .../protocol/packets/factory/C2SPacketType.kt | 8 +++-- .../protocol/packets/factory/S2CPacketType.kt | 9 +++-- 8 files changed, 86 insertions(+), 34 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/QueuedS2CP.kt diff --git a/src/main/java/de/bixilon/minosoft/data/registries/versions/Versions.kt b/src/main/java/de/bixilon/minosoft/data/registries/versions/Versions.kt index 7f9763646..baa008c05 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/versions/Versions.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/versions/Versions.kt @@ -21,7 +21,6 @@ import de.bixilon.kutil.primitive.IntUtil.toInt import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.assets.util.FileUtil.readJson import de.bixilon.minosoft.protocol.packets.factory.C2SPacketType -import de.bixilon.minosoft.protocol.packets.factory.PacketDirection import de.bixilon.minosoft.protocol.packets.factory.PacketTypeRegistry import de.bixilon.minosoft.protocol.packets.factory.S2CPacketType import de.bixilon.minosoft.protocol.protocol.ProtocolStates @@ -64,14 +63,14 @@ object Versions : Iterable { when (val s2c = mapping["s2c"]) { is List<*> -> { // just play - s2cPackets = mapOf(ProtocolStates.PLAY to readPacketMapping(versionId, PacketDirection.SERVER_TO_CLIENT, s2c.unsafeCast()) { PacketTypeRegistry.getS2C(ProtocolStates.PLAY, it) }) + s2cPackets = mapOf(ProtocolStates.PLAY to readS2PPacketMapping(versionId, ProtocolStates.PLAY, s2c.unsafeCast())) } is Map<*, *> -> { // map other states val packets: MutableMap> = mutableMapOf() for ((stateName, packetMapping) in s2c) { val state = ProtocolStates[stateName.toString()] - packets[state] = readPacketMapping(versionId, PacketDirection.SERVER_TO_CLIENT, packetMapping.unsafeCast()) { PacketTypeRegistry.getS2C(state, it) } + packets[state] = readS2PPacketMapping(versionId, state, packetMapping.unsafeCast()) } s2cPackets = packets } @@ -79,14 +78,14 @@ object Versions : Iterable { } when (val c2s = mapping["c2s"]) { is List<*> -> { - c2sPackets = mapOf(ProtocolStates.PLAY to readPacketMapping(versionId, PacketDirection.CLIENT_TO_SERVER, c2s.unsafeCast()) { PacketTypeRegistry.getC2S(ProtocolStates.PLAY, it) }) + c2sPackets = mapOf(ProtocolStates.PLAY to readC2SPacketMapping(versionId, ProtocolStates.PLAY, c2s.unsafeCast())) } is Map<*, *> -> { // map other states val packets: MutableMap> = mutableMapOf() for ((stateName, packetMapping) in c2s) { val state = ProtocolStates[stateName.toString()] - packets[state] = readPacketMapping(versionId, PacketDirection.CLIENT_TO_SERVER, packetMapping.unsafeCast()) { PacketTypeRegistry.getC2S(state, it) } + packets[state] = readC2SPacketMapping(versionId, state, packetMapping.unsafeCast()) } c2sPackets = packets } @@ -115,21 +114,31 @@ object Versions : Iterable { } } - private fun readPacketMapping(versionId: Int, direction: PacketDirection, list: List, typeGetter: (name: String) -> T?): MutableBiMap { + private fun readPacketMapping(versionId: Int, list: List, typeGetter: (name: String) -> T): MutableBiMap { val map: MutableBiMap = mutableBiMapOf() - var packetId = 0 // To not mess up ids when packet is not registered for (name in list) { val packetType = typeGetter(name) - if (packetType == null) { - Log.log(LogMessageType.VERSION_LOADING, LogLevels.WARN) { "Packet $name is not registered (versionId=$versionId, direction=$direction)!" } - packetId++ - continue - } - map.put(packetType, packetId++)?.let { Log.log(LogMessageType.VERSION_LOADING, LogLevels.WARN) { "Packet $name registered twice (version=$versionId)" } } + map.put(packetType, map.size)?.let { Log.log(LogMessageType.VERSION_LOADING, LogLevels.WARN) { "Packet $name registered twice (version=$versionId)" } } } return map } + private fun readS2PPacketMapping(versionId: Int, state: ProtocolStates, list: List): AbstractBiMap { + return readPacketMapping(versionId, list) { + PacketTypeRegistry.getS2C(state, it)?.let { type -> return@readPacketMapping type } + Log.log(LogMessageType.VERSION_LOADING, LogLevels.WARN) { "Packet $it is not registered (versionId=$versionId, state=$state, direction=SERVER_TO_CLIENT)!" } + return@readPacketMapping S2CPacketType.EMPTY() + } + } + + private fun readC2SPacketMapping(versionId: Int, state: ProtocolStates, list: List): AbstractBiMap { + return readPacketMapping(versionId, list) { + PacketTypeRegistry.getC2S(state, it)?.let { type -> return@readPacketMapping type } + Log.log(LogMessageType.VERSION_LOADING, LogLevels.WARN) { "Packet $it is not registered (versionId=$versionId, state=$state, direction=CLIENT_TO_SERVER)!" } + return@readPacketMapping C2SPacketType.EMPTY() + } + } + operator fun get(name: String?): Version? { if (name == "automatic") { return AUTOMATIC diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/exceptions/implementation/S2CPacketNotImplementedException.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/exceptions/implementation/S2CPacketNotImplementedException.kt index df761ce5d..e18b08912 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/exceptions/implementation/S2CPacketNotImplementedException.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/exceptions/implementation/S2CPacketNotImplementedException.kt @@ -13,11 +13,15 @@ package de.bixilon.minosoft.protocol.network.network.client.exceptions.implementation +import de.bixilon.minosoft.data.registries.versions.Version import de.bixilon.minosoft.protocol.network.network.client.exceptions.NetworkException -import de.bixilon.minosoft.protocol.packets.factory.S2CPacketType +import de.bixilon.minosoft.protocol.protocol.ProtocolStates +import de.bixilon.minosoft.util.KUtil.toHex class S2CPacketNotImplementedException( - val packetType: S2CPacketType, + val packetId: Int, + val state: ProtocolStates, + val version: Version?, ) : NetworkException() { - override val message: String = "$packetType" + override val message: String = "packetId=0x${packetId.toHex()}, state=$state, version=$version" } diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ClientPacketHandler.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ClientPacketHandler.kt index 9a64e1ab7..14781aa12 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ClientPacketHandler.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ClientPacketHandler.kt @@ -28,7 +28,6 @@ import de.bixilon.minosoft.protocol.network.network.client.NettyClient import de.bixilon.minosoft.protocol.network.network.client.exceptions.NetworkException import de.bixilon.minosoft.protocol.network.network.client.exceptions.PacketHandleException import de.bixilon.minosoft.protocol.network.network.client.exceptions.WrongConnectionException -import de.bixilon.minosoft.protocol.packets.factory.PacketTypeRegistry import de.bixilon.minosoft.protocol.packets.factory.S2CPacketType import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket @@ -38,7 +37,7 @@ import io.netty.channel.SimpleChannelInboundHandler class ClientPacketHandler( private val client: NettyClient, -) : SimpleChannelInboundHandler() { +) : SimpleChannelInboundHandler>() { private val connection: Connection = client.connection private val handlers: MutableSet = synchronizedSetOf() @@ -52,15 +51,14 @@ class ClientPacketHandler( } } - override fun channelRead0(context: ChannelHandlerContext, packet: S2CPacket) { - val type = PacketTypeRegistry.getS2C(packet::class.java) ?: throw IllegalStateException("Packet type is null?") - if (type.threadSafe) { + override fun channelRead0(context: ChannelHandlerContext, queued: QueuedS2CP<*>) { + if (queued.type.threadSafe) { val runnable = ThreadPoolRunnable() - runnable.runnable = Runnable { tryHandle(type, packet);handlers -= runnable } + runnable.runnable = Runnable { tryHandle(queued.type, queued.packet);handlers -= runnable } handlers += runnable DefaultThreadPool += runnable } else { - tryHandle(type, packet) + tryHandle(queued.type, queued.packet) } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ExceptionHandler.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ExceptionHandler.kt index dab179dd8..00e746c93 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ExceptionHandler.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/ExceptionHandler.kt @@ -21,23 +21,28 @@ import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogMessageType import io.netty.channel.ChannelDuplexHandler import io.netty.channel.ChannelHandlerContext +import io.netty.handler.codec.DecoderException +import io.netty.handler.codec.EncoderException class ExceptionHandler( private val client: NettyClient, ) : ChannelDuplexHandler() { override fun exceptionCaught(context: ChannelHandlerContext, cause: Throwable) { - Log.log(LogMessageType.NETWORK_PACKETS_IN, LogLevels.WARN) { cause } - if (cause !is NetworkException) { - client.disconnect() - return + var realCause = cause + if (cause is DecoderException) { + realCause = cause.cause ?: cause + } else if (cause is EncoderException) { + realCause = cause.cause ?: cause } - if (cause is CriticalNetworkException) { + Log.log(LogMessageType.NETWORK_PACKETS_IN, LogLevels.WARN) { realCause } + if (realCause !is NetworkException || realCause is CriticalNetworkException) { client.disconnect() return } } + companion object { const val NAME = "exception_handler" } diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/QueuedS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/QueuedS2CP.kt new file mode 100644 index 000000000..991df9cc8 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/QueuedS2CP.kt @@ -0,0 +1,22 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.protocol.network.network.client.pipeline + +import de.bixilon.minosoft.protocol.packets.factory.S2CPacketType +import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket + +data class QueuedS2CP( + val type: S2CPacketType, + val packet: T, +) diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/encoding/PacketDecoder.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/encoding/PacketDecoder.kt index 1070e5cf1..2bc51c9e7 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/encoding/PacketDecoder.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/pipeline/encoding/PacketDecoder.kt @@ -22,6 +22,7 @@ import de.bixilon.minosoft.protocol.network.network.client.exceptions.PacketBuff import de.bixilon.minosoft.protocol.network.network.client.exceptions.PacketReadException import de.bixilon.minosoft.protocol.network.network.client.exceptions.ciritical.UnknownPacketIdException import de.bixilon.minosoft.protocol.network.network.client.exceptions.implementation.S2CPacketNotImplementedException +import de.bixilon.minosoft.protocol.network.network.client.pipeline.QueuedS2CP import de.bixilon.minosoft.protocol.packets.factory.S2CPacketType import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket import de.bixilon.minosoft.protocol.protocol.InByteBuffer @@ -44,6 +45,10 @@ class PacketDecoder( val packetType = version?.s2cPackets?.get(state)?.getKey(packetId) ?: Protocol.S2C_PACKET_MAPPING[state]?.getKey(packetId) ?: throw UnknownPacketIdException(packetId, state, version) + if (packetType.clazz == S2CPacket::class.java) { + throw S2CPacketNotImplementedException(packetId, state, version) + } + val packet = try { readPacket(packetType, data) } catch (exception: NetworkException) { @@ -54,7 +59,7 @@ class PacketDecoder( throw PacketReadException(error) } - out += packet + out += QueuedS2CP(packetType, packet) } private fun readPacket(type: S2CPacketType, data: ByteArray): S2CPacket { @@ -63,7 +68,7 @@ class PacketDecoder( } else { InByteBuffer(data) } - val packet = type.factory?.createPacket(buffer) ?: throw S2CPacketNotImplementedException(type) + val packet = type.factory?.createPacket(buffer) ?: throw IllegalStateException("Packet factory is null?") if (buffer.pointer < buffer.size) { throw PacketBufferUnderflowException(type, buffer.size, buffer.pointer) } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/factory/C2SPacketType.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/factory/C2SPacketType.kt index a40b41073..ee2f2f49b 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/factory/C2SPacketType.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/factory/C2SPacketType.kt @@ -19,12 +19,16 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolStates class C2SPacketType( val state: ProtocolStates, val clazz: Class, - val annotation: LoadPacket = clazz.getAnnotation(LoadPacket::class.java), + val annotation: LoadPacket?, + override val threadSafe: Boolean = annotation!!.threadSafe, ) : AbstractPacketType { override val direction = PacketDirection.CLIENT_TO_SERVER - override val threadSafe: Boolean get() = annotation.threadSafe override fun toString(): String { return clazz.toString() } + + companion object { + val EMPTY = { C2SPacketType(ProtocolStates.HANDSHAKING, C2SPacket::class.java, null, false) } + } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/factory/S2CPacketType.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/factory/S2CPacketType.kt index fe7d8db74..cadc02cd8 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/factory/S2CPacketType.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/factory/S2CPacketType.kt @@ -23,11 +23,11 @@ class S2CPacketType( val state: ProtocolStates, val clazz: Class, private val packetErrorHandler: PacketErrorHandler?, - val annotation: LoadPacket = clazz.getAnnotation(LoadPacket::class.java), + val annotation: LoadPacket?, val factory: PacketFactory? = null, + override val threadSafe: Boolean = annotation!!.threadSafe, ) : AbstractPacketType, PacketErrorHandler { override val direction = PacketDirection.SERVER_TO_CLIENT - override val threadSafe: Boolean get() = annotation.threadSafe override fun onError(error: Throwable, connection: Connection) { @@ -37,4 +37,9 @@ class S2CPacketType( override fun toString(): String { return clazz.toString() } + + + companion object { + val EMPTY = { S2CPacketType(ProtocolStates.HANDSHAKING, S2CPacket::class.java, null, null, null, false) } + } }