network: correct exception for packet not implemented

This commit is contained in:
Bixilon 2022-01-09 21:13:53 +01:00
parent d6d1220778
commit dc9947850d
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
8 changed files with 86 additions and 34 deletions

View File

@ -21,7 +21,6 @@ import de.bixilon.kutil.primitive.IntUtil.toInt
import de.bixilon.minosoft.Minosoft import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.assets.util.FileUtil.readJson import de.bixilon.minosoft.assets.util.FileUtil.readJson
import de.bixilon.minosoft.protocol.packets.factory.C2SPacketType 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.PacketTypeRegistry
import de.bixilon.minosoft.protocol.packets.factory.S2CPacketType import de.bixilon.minosoft.protocol.packets.factory.S2CPacketType
import de.bixilon.minosoft.protocol.protocol.ProtocolStates import de.bixilon.minosoft.protocol.protocol.ProtocolStates
@ -64,14 +63,14 @@ object Versions : Iterable<Version> {
when (val s2c = mapping["s2c"]) { when (val s2c = mapping["s2c"]) {
is List<*> -> { is List<*> -> {
// just play // 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<*, *> -> { is Map<*, *> -> {
// map other states // map other states
val packets: MutableMap<ProtocolStates, AbstractBiMap<S2CPacketType, Int>> = mutableMapOf() val packets: MutableMap<ProtocolStates, AbstractBiMap<S2CPacketType, Int>> = mutableMapOf()
for ((stateName, packetMapping) in s2c) { for ((stateName, packetMapping) in s2c) {
val state = ProtocolStates[stateName.toString()] 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 s2cPackets = packets
} }
@ -79,14 +78,14 @@ object Versions : Iterable<Version> {
} }
when (val c2s = mapping["c2s"]) { when (val c2s = mapping["c2s"]) {
is List<*> -> { 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<*, *> -> { is Map<*, *> -> {
// map other states // map other states
val packets: MutableMap<ProtocolStates, AbstractBiMap<C2SPacketType, Int>> = mutableMapOf() val packets: MutableMap<ProtocolStates, AbstractBiMap<C2SPacketType, Int>> = mutableMapOf()
for ((stateName, packetMapping) in c2s) { for ((stateName, packetMapping) in c2s) {
val state = ProtocolStates[stateName.toString()] 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 c2sPackets = packets
} }
@ -115,21 +114,31 @@ object Versions : Iterable<Version> {
} }
} }
private fun <T> readPacketMapping(versionId: Int, direction: PacketDirection, list: List<String>, typeGetter: (name: String) -> T?): MutableBiMap<T, Int> { private fun <T> readPacketMapping(versionId: Int, list: List<String>, typeGetter: (name: String) -> T): MutableBiMap<T, Int> {
val map: MutableBiMap<T, Int> = mutableBiMapOf() val map: MutableBiMap<T, Int> = mutableBiMapOf()
var packetId = 0 // To not mess up ids when packet is not registered
for (name in list) { for (name in list) {
val packetType = typeGetter(name) val packetType = typeGetter(name)
if (packetType == null) { map.put(packetType, map.size)?.let { Log.log(LogMessageType.VERSION_LOADING, LogLevels.WARN) { "Packet $name registered twice (version=$versionId)" } }
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)" } }
} }
return map return map
} }
private fun readS2PPacketMapping(versionId: Int, state: ProtocolStates, list: List<String>): AbstractBiMap<S2CPacketType, Int> {
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<String>): AbstractBiMap<C2SPacketType, Int> {
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? { operator fun get(name: String?): Version? {
if (name == "automatic") { if (name == "automatic") {
return AUTOMATIC return AUTOMATIC

View File

@ -13,11 +13,15 @@
package de.bixilon.minosoft.protocol.network.network.client.exceptions.implementation 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.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( class S2CPacketNotImplementedException(
val packetType: S2CPacketType, val packetId: Int,
val state: ProtocolStates,
val version: Version?,
) : NetworkException() { ) : NetworkException() {
override val message: String = "$packetType" override val message: String = "packetId=0x${packetId.toHex()}, state=$state, version=$version"
} }

View File

@ -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.NetworkException
import de.bixilon.minosoft.protocol.network.network.client.exceptions.PacketHandleException 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.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.factory.S2CPacketType
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket
@ -38,7 +37,7 @@ import io.netty.channel.SimpleChannelInboundHandler
class ClientPacketHandler( class ClientPacketHandler(
private val client: NettyClient, private val client: NettyClient,
) : SimpleChannelInboundHandler<S2CPacket>() { ) : SimpleChannelInboundHandler<QueuedS2CP<*>>() {
private val connection: Connection = client.connection private val connection: Connection = client.connection
private val handlers: MutableSet<ThreadPoolRunnable> = synchronizedSetOf() private val handlers: MutableSet<ThreadPoolRunnable> = synchronizedSetOf()
@ -52,15 +51,14 @@ class ClientPacketHandler(
} }
} }
override fun channelRead0(context: ChannelHandlerContext, packet: S2CPacket) { override fun channelRead0(context: ChannelHandlerContext, queued: QueuedS2CP<*>) {
val type = PacketTypeRegistry.getS2C(packet::class.java) ?: throw IllegalStateException("Packet type is null?") if (queued.type.threadSafe) {
if (type.threadSafe) {
val runnable = ThreadPoolRunnable() val runnable = ThreadPoolRunnable()
runnable.runnable = Runnable { tryHandle(type, packet);handlers -= runnable } runnable.runnable = Runnable { tryHandle(queued.type, queued.packet);handlers -= runnable }
handlers += runnable handlers += runnable
DefaultThreadPool += runnable DefaultThreadPool += runnable
} else { } else {
tryHandle(type, packet) tryHandle(queued.type, queued.packet)
} }
} }

View File

@ -21,23 +21,28 @@ import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import io.netty.channel.ChannelDuplexHandler import io.netty.channel.ChannelDuplexHandler
import io.netty.channel.ChannelHandlerContext import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.DecoderException
import io.netty.handler.codec.EncoderException
class ExceptionHandler( class ExceptionHandler(
private val client: NettyClient, private val client: NettyClient,
) : ChannelDuplexHandler() { ) : ChannelDuplexHandler() {
override fun exceptionCaught(context: ChannelHandlerContext, cause: Throwable) { override fun exceptionCaught(context: ChannelHandlerContext, cause: Throwable) {
Log.log(LogMessageType.NETWORK_PACKETS_IN, LogLevels.WARN) { cause } var realCause = cause
if (cause !is NetworkException) { if (cause is DecoderException) {
client.disconnect() realCause = cause.cause ?: cause
return } 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() client.disconnect()
return return
} }
} }
companion object { companion object {
const val NAME = "exception_handler" const val NAME = "exception_handler"
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<T : S2CPacket>(
val type: S2CPacketType,
val packet: T,
)

View File

@ -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.PacketReadException
import de.bixilon.minosoft.protocol.network.network.client.exceptions.ciritical.UnknownPacketIdException 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.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.factory.S2CPacketType
import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket import de.bixilon.minosoft.protocol.packets.s2c.S2CPacket
import de.bixilon.minosoft.protocol.protocol.InByteBuffer 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) 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 { val packet = try {
readPacket(packetType, data) readPacket(packetType, data)
} catch (exception: NetworkException) { } catch (exception: NetworkException) {
@ -54,7 +59,7 @@ class PacketDecoder(
throw PacketReadException(error) throw PacketReadException(error)
} }
out += packet out += QueuedS2CP(packetType, packet)
} }
private fun readPacket(type: S2CPacketType, data: ByteArray): S2CPacket { private fun readPacket(type: S2CPacketType, data: ByteArray): S2CPacket {
@ -63,7 +68,7 @@ class PacketDecoder(
} else { } else {
InByteBuffer(data) 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) { if (buffer.pointer < buffer.size) {
throw PacketBufferUnderflowException(type, buffer.size, buffer.pointer) throw PacketBufferUnderflowException(type, buffer.size, buffer.pointer)
} }

View File

@ -19,12 +19,16 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolStates
class C2SPacketType( class C2SPacketType(
val state: ProtocolStates, val state: ProtocolStates,
val clazz: Class<out C2SPacket>, val clazz: Class<out C2SPacket>,
val annotation: LoadPacket = clazz.getAnnotation(LoadPacket::class.java), val annotation: LoadPacket?,
override val threadSafe: Boolean = annotation!!.threadSafe,
) : AbstractPacketType { ) : AbstractPacketType {
override val direction = PacketDirection.CLIENT_TO_SERVER override val direction = PacketDirection.CLIENT_TO_SERVER
override val threadSafe: Boolean get() = annotation.threadSafe
override fun toString(): String { override fun toString(): String {
return clazz.toString() return clazz.toString()
} }
companion object {
val EMPTY = { C2SPacketType(ProtocolStates.HANDSHAKING, C2SPacket::class.java, null, false) }
}
} }

View File

@ -23,11 +23,11 @@ class S2CPacketType(
val state: ProtocolStates, val state: ProtocolStates,
val clazz: Class<out S2CPacket>, val clazz: Class<out S2CPacket>,
private val packetErrorHandler: PacketErrorHandler?, private val packetErrorHandler: PacketErrorHandler?,
val annotation: LoadPacket = clazz.getAnnotation(LoadPacket::class.java), val annotation: LoadPacket?,
val factory: PacketFactory? = null, val factory: PacketFactory? = null,
override val threadSafe: Boolean = annotation!!.threadSafe,
) : AbstractPacketType, PacketErrorHandler { ) : AbstractPacketType, PacketErrorHandler {
override val direction = PacketDirection.SERVER_TO_CLIENT override val direction = PacketDirection.SERVER_TO_CLIENT
override val threadSafe: Boolean get() = annotation.threadSafe
override fun onError(error: Throwable, connection: Connection) { override fun onError(error: Throwable, connection: Connection) {
@ -37,4 +37,9 @@ class S2CPacketType(
override fun toString(): String { override fun toString(): String {
return clazz.toString() return clazz.toString()
} }
companion object {
val EMPTY = { S2CPacketType(ProtocolStates.HANDSHAKING, S2CPacket::class.java, null, null, null, false) }
}
} }