diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/session/selector/properties/ConnectedProperty.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/session/selector/properties/ConnectedProperty.kt deleted file mode 100644 index d61013eb5..000000000 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/session/selector/properties/ConnectedProperty.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Minosoft - * Copyright (C) 2020-2024 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.commands.parser.minosoft.session.selector.properties - -import de.bixilon.minosoft.commands.errors.ExpectedArgumentError -import de.bixilon.minosoft.commands.parser.brigadier.bool.BooleanParser.readBoolean -import de.bixilon.minosoft.commands.parser.selector.TargetProperty -import de.bixilon.minosoft.commands.parser.selector.TargetPropertyFactory -import de.bixilon.minosoft.commands.util.CommandReader -import de.bixilon.minosoft.protocol.network.session.play.PlaySession - -class ConnectedProperty( - val connected: Boolean, -) : TargetProperty { - - override fun passes(value: PlaySession): Boolean { - val connected = value.network.connected - return connected == this.connected - } - - companion object : TargetPropertyFactory { - override val name: String = "connected" - - override fun read(reader: CommandReader): ConnectedProperty { - return ConnectedProperty(reader.readBoolean() ?: throw ExpectedArgumentError(reader)) - } - } -} diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/session/selector/properties/SessionTargetProperties.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/session/selector/properties/SessionTargetProperties.kt index 1894a6483..5b4d3c06b 100644 --- a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/session/selector/properties/SessionTargetProperties.kt +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/session/selector/properties/SessionTargetProperties.kt @@ -20,6 +20,5 @@ object SessionTargetProperties : TargetProperties() { init { register(StateProperty) - register(ConnectedProperty) } } diff --git a/src/main/java/de/bixilon/minosoft/data/entities/entities/player/local/SignatureKeyManagement.kt b/src/main/java/de/bixilon/minosoft/data/entities/entities/player/local/SignatureKeyManagement.kt index 428092b30..747ae5796 100644 --- a/src/main/java/de/bixilon/minosoft/data/entities/entities/player/local/SignatureKeyManagement.kt +++ b/src/main/java/de/bixilon/minosoft/data/entities/entities/player/local/SignatureKeyManagement.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.data.entities.entities.player.local +import de.bixilon.kutil.cast.CastUtil.nullCast import de.bixilon.kutil.concurrent.lock.simple.SimpleLock import de.bixilon.kutil.concurrent.schedule.TaskScheduler.runLater import de.bixilon.kutil.latch.AbstractLatch @@ -23,6 +24,7 @@ import de.bixilon.minosoft.data.chat.signature.ChatSignatureProperties import de.bixilon.minosoft.data.chat.signature.errors.KeyExpiredError import de.bixilon.minosoft.data.text.TextComponent import de.bixilon.minosoft.modding.event.events.chat.ChatMessageEvent +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.protocol.packets.c2s.play.SessionDataC2SP import de.bixilon.minosoft.protocol.protocol.ProtocolStates @@ -46,7 +48,8 @@ class SignatureKeyManagement( private fun registerRefresh(millis: Int) { runLater(millis) { - if (session.error != null || (session.established && !session.network.connected) || (session.network.connected && !session.network.encrypted)) { + val connected = session.connection.nullCast()?.state != null + if (session.error != null || (session.established && !connected) || (connected && !session.network.encrypted)) { // session is dead return@runLater } @@ -89,7 +92,7 @@ class SignatureKeyManagement( if (session.version.versionId < ProtocolVersions.V_22W43A) { return } - if (session.network.state != ProtocolStates.PLAY || !session.network.connected) { + if (session.connection !is NetworkConnection || session.connection.state != ProtocolStates.PLAY) { return } if (!session.network.encrypted) { diff --git a/src/main/java/de/bixilon/minosoft/example/ExampleMod.kt b/src/main/java/de/bixilon/minosoft/example/ExampleMod.kt index d7f56f14f..2c20a3a8e 100644 --- a/src/main/java/de/bixilon/minosoft/example/ExampleMod.kt +++ b/src/main/java/de/bixilon/minosoft/example/ExampleMod.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.example +import de.bixilon.kutil.cast.CastUtil.nullCast import de.bixilon.kutil.observer.DataObserver.Companion.observe import de.bixilon.kutil.stream.InputStreamUtil.readAsString import de.bixilon.minosoft.modding.event.events.chat.ChatMessageEvent @@ -20,6 +21,7 @@ import de.bixilon.minosoft.modding.event.events.session.play.PlaySessionCreateEv import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen import de.bixilon.minosoft.modding.event.master.GlobalEventMaster import de.bixilon.minosoft.modding.loader.mod.ModMain +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.util.KUtil.toResourceLocation @@ -37,7 +39,8 @@ object ExampleMod : ModMain() { } private fun PlaySession.startListening() { - if (this.address.hostname != "localhost" && this.address.hostname != "127.0.0.1") { + val address = this.connection.nullCast()?.address ?: return + if (address.hostname != "localhost" && address.hostname != "127.0.0.1") { return } events.listen { diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/ServerListController.kt b/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/ServerListController.kt index aef8cc81b..c1232898b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/ServerListController.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/ServerListController.kt @@ -45,6 +45,7 @@ import de.bixilon.minosoft.gui.eros.modding.invoker.JavaFXEventListener import de.bixilon.minosoft.gui.eros.util.JavaFXUtil import de.bixilon.minosoft.gui.eros.util.JavaFXUtil.ctext import de.bixilon.minosoft.modding.event.events.KickEvent +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.protocol.network.session.play.PlaySessionStates import de.bixilon.minosoft.protocol.network.session.play.PlaySessionStates.Companion.disconnected @@ -136,11 +137,12 @@ class ServerListController : EmbeddedJavaFXController(), Refreshable { for ((type, name) in server.profiles) { override[ProfileManagers[type]?.type ?: continue] = name } + val profiles = SelectedProfiles(override) val session = PlaySession( - address = ping.realAddress ?: DNSUtil.getServerAddress(server.address), + connection = NetworkConnection(ping.connection?.address ?: DNSUtil.getServerAddress(server.address), native = profiles.other.nativeNetwork), account = account, version = version, - profiles = SelectedProfiles(override) + profiles = profiles ) account.sessions[server] = session serverCard.sessions += session @@ -151,14 +153,14 @@ class ServerListController : EmbeddedJavaFXController(), Refreshable { serverCard.sessions -= session } if (ErosProfileManager.selected.general.hideErosOnceConnected) { - if (session.network.connected) { + if (session.connection.active) { if (session.state == PlaySessionStates.PLAYING) { Eros.setVisibility(false) } } else { var connected = false for (entry in PlaySession.ACTIVE_CONNECTIONS.toSynchronizedSet()) { - if (entry.network.connected) { + if (entry.connection.active) { connected = true break } @@ -313,7 +315,8 @@ class ServerListController : EmbeddedJavaFXController(), Refreshable { // ToDo: server.connections.clear() ping.terminate() - ping.address = server.address + ping.reset() + ping.hostname = server.address ping.ping() } JavaFXUtil.runLater { refreshList() } @@ -425,7 +428,7 @@ class ServerListController : EmbeddedJavaFXController(), Refreshable { private val SERVER_INFO_PROPERTIES: List Any?>> = listOf( "minosoft:server_info.server_name".toResourceLocation() to { it.server.name }, "minosoft:server_info.server_address".toResourceLocation() to { it.server.address }, - "minosoft:server_info.real_server_address".toResourceLocation() to { it.ping.realAddress }, + "minosoft:server_info.real_server_address".toResourceLocation() to { it.ping.connection?.address }, "minosoft:server_info.forced_version".toResourceLocation() to { it.server.forcedVersion }, TranslatableComponents.GENERAL_EMPTY to { " " }, diff --git a/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/type/types/CustomServerType.kt b/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/type/types/CustomServerType.kt index 17ccc8aa4..14fc0661f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/type/types/CustomServerType.kt +++ b/src/main/java/de/bixilon/minosoft/gui/eros/main/play/server/type/types/CustomServerType.kt @@ -62,7 +62,7 @@ object CustomServerType : ServerType { continue } DefaultThreadPool += ForcePooledRunnable { - ping.network.disconnect() + ping.terminate() ping.ping() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/fade/FadingTextElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/fade/FadingTextElement.kt index bc4f45e55..be752eadc 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/fade/FadingTextElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/fade/FadingTextElement.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2023 Moritz Zwerger + * Copyright (C) 2020-2024 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. * @@ -65,6 +65,7 @@ class FadingTextElement( fun show() { val time = millis() + // TODO: extend time on call if already showing text val phase = times.createPhase(time) this.phase = phase updateSize(phase) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt index 2297030bc..c88b876cf 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt @@ -129,7 +129,7 @@ class DebugHUDElement(guiRenderer: GUIRenderer) : Element(guiRenderer), Layouted layout += LineSpacerElement(guiRenderer) layout += TextElement(guiRenderer, BaseComponent("Account ", session.account.username)) - layout += TextElement(guiRenderer, BaseComponent("Address ", session.address)) + layout += TextElement(guiRenderer, BaseComponent("Address ", session.connection.identifier)) layout += TextElement(guiRenderer, BaseComponent("Network version ", session.version)) layout += TextElement(guiRenderer, BaseComponent("Server brand ", session.serverInfo.brand)).apply { session.serverInfo::brand.observe(this@DebugHUDElement) { this.text = BaseComponent("Server brand ", it.truncate(50)) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt index be537acb7..fe655b6cc 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt @@ -107,7 +107,7 @@ class ScreenshotTaker( val size = Vec2i(context.window.size) val buffer = context.system.readPixels(Vec2i.EMPTY_INSTANCE, size) - val path = RunConfiguration.HOME_DIRECTORY.resolve("screenshots").resolve(context.session.address.hostname) + val path = RunConfiguration.HOME_DIRECTORY.resolve("screenshots").resolve(context.session.connection.identifier) val time = millis() DefaultThreadPool += ForcePooledRunnable(priority = ThreadPool.HIGHER) { store(buffer, path, time) } } catch (exception: Exception) { diff --git a/src/main/java/de/bixilon/minosoft/protocol/AddressResolver.kt b/src/main/java/de/bixilon/minosoft/protocol/AddressResolver.kt new file mode 100644 index 000000000..c0835e7d9 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/AddressResolver.kt @@ -0,0 +1,35 @@ +/* + * Minosoft + * Copyright (C) 2020-2024 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 + +import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.connection.NetworkConnection +import de.bixilon.minosoft.util.DNSUtil +import de.bixilon.minosoft.util.logging.Log +import de.bixilon.minosoft.util.logging.LogMessageType + +class AddressResolver( + val address: String, +) { + private var candidates: MutableList = DNSUtil.resolveServerAddress(address).toMutableList() + + + fun tryNext(): NetworkConnection? { + if (candidates.isEmpty()) return null + val address = candidates.removeAt(0) + + Log.log(LogMessageType.NETWORK) { "Trying address: $address" } + return NetworkConnection(address, false) + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/connection/NetworkConnection.kt b/src/main/java/de/bixilon/minosoft/protocol/connection/NetworkConnection.kt new file mode 100644 index 000000000..602708acf --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/connection/NetworkConnection.kt @@ -0,0 +1,60 @@ +/* + * Minosoft + * Copyright (C) 2020-2024 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.connection + +import de.bixilon.kutil.concurrent.lock.thread.ThreadLock +import de.bixilon.kutil.observer.DataObserver +import de.bixilon.kutil.observer.DataObserver.Companion.observe +import de.bixilon.kutil.observer.DataObserver.Companion.observed +import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.network.network.client.ClientNetwork +import de.bixilon.minosoft.protocol.network.network.client.netty.NettyClient +import de.bixilon.minosoft.protocol.network.session.Session +import de.bixilon.minosoft.protocol.packets.c2s.C2SPacket +import de.bixilon.minosoft.protocol.protocol.ProtocolStates +import de.bixilon.minosoft.util.logging.Log +import de.bixilon.minosoft.util.logging.LogLevels +import de.bixilon.minosoft.util.logging.LogMessageType + +class NetworkConnection( + val address: ServerAddress, + val native: Boolean, +) : ServerConnection { + var client: ClientNetwork? = null + private set + override val identifier = address.toString() + override var active by observed(false) + private set + var state: ProtocolStates? by DataObserver(null, ThreadLock()) + + init { + this::state.observe(this) { active = it != null } + } + + override fun connect(session: Session) { + Log.log(LogMessageType.NETWORK, level = LogLevels.INFO) { "Connecting to server: $address" } + if (client != null) throw IllegalStateException("Already connected???") + val netty = NettyClient(this, session) + this.client = netty + netty.connect() + } + + override fun disconnect() { + client?.disconnect() + } + + override fun send(packet: C2SPacket) { + client?.send(packet) + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/connection/ServerConnection.kt b/src/main/java/de/bixilon/minosoft/protocol/connection/ServerConnection.kt new file mode 100644 index 000000000..3416f4311 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/connection/ServerConnection.kt @@ -0,0 +1,28 @@ +/* + * Minosoft + * Copyright (C) 2020-2024 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.connection + +import de.bixilon.minosoft.protocol.network.session.Session +import de.bixilon.minosoft.protocol.packets.c2s.C2SPacket + +interface ServerConnection { + val identifier: String + + val active: Boolean + + fun connect(session: Session) + fun disconnect() + + fun send(packet: C2SPacket) +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/ClientNetwork.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/ClientNetwork.kt index 9ee6ef5c4..08f204a6f 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/ClientNetwork.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/ClientNetwork.kt @@ -13,25 +13,23 @@ package de.bixilon.minosoft.protocol.network.network.client -import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.network.client.netty.packet.receiver.PacketReceiver import de.bixilon.minosoft.protocol.network.network.client.netty.packet.sender.PacketSender import de.bixilon.minosoft.protocol.packets.c2s.C2SPacket -import de.bixilon.minosoft.protocol.protocol.ProtocolStates import javax.crypto.Cipher interface ClientNetwork { - val connected: Boolean + val connection: NetworkConnection val encrypted: Boolean val detached: Boolean - var state: ProtocolStates val compressionThreshold: Int val receiver: PacketReceiver val sender: PacketSender - fun connect(address: ServerAddress, native: Boolean) + fun connect() fun disconnect() fun detach() diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/NettyClient.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/NettyClient.kt index a7545837e..5e06bd56a 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/NettyClient.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/NettyClient.kt @@ -15,9 +15,9 @@ package de.bixilon.minosoft.protocol.network.network.client.netty import de.bixilon.kutil.cast.CastUtil.nullCast import de.bixilon.kutil.exception.ExceptionUtil.catchAll -import de.bixilon.kutil.observer.DataObserver.Companion.observed +import de.bixilon.kutil.observer.DataObserver.Companion.observe import de.bixilon.minosoft.config.profile.profiles.other.OtherProfileManager -import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.network.client.ClientNetwork import de.bixilon.minosoft.protocol.network.network.client.netty.exceptions.NetworkException import de.bixilon.minosoft.protocol.network.network.client.netty.exceptions.PacketHandleException @@ -51,14 +51,13 @@ import javax.crypto.Cipher @ChannelHandler.Sharable class NettyClient( + override val connection: NetworkConnection, val session: Session, ) : SimpleChannelInboundHandler(), ClientNetwork { + @Deprecated("unused") + var state = ProtocolStates.HANDSHAKE // TODO override val sender = PacketSender(this) override val receiver = PacketReceiver(this, session) - private var address: ServerAddress? = null - override var connected by observed(false) - private set - override var state by observed(ProtocolStates.HANDSHAKE) override var compressionThreshold = -1 override var encrypted: Boolean = false private set @@ -66,20 +65,22 @@ class NettyClient( override var detached = false private set - override fun connect(address: ServerAddress, native: Boolean) { - this.address = address - state = ProtocolStates.HANDSHAKE - val natives = if (native) TransportNatives.get() else NioNatives + init { + connection::state.observe(this) { this.state = it ?: ProtocolStates.HANDSHAKE } + } + + override fun connect() { + val natives = if (connection.native) TransportNatives.get() else NioNatives val bootstrap = Bootstrap() .group(natives.pool) .channel(natives.channel) .handler(NetworkPipeline(this)) + val address = connection.address val future = bootstrap.connect(address.hostname, address.port) future.addListener { - if (!it.isSuccess) { - handleError(it.cause()) - } + if (it.isSuccess) return@addListener + handleError(it.cause()) } } @@ -118,7 +119,7 @@ class NettyClient( encrypted = false channel = null compressionThreshold = -1 - connected = false + connection.state = null } override fun detach() { @@ -140,15 +141,17 @@ class NettyClient( override fun channelActive(context: ChannelHandlerContext) { catchAll { context.channel().config().setOption(ChannelOption.TCP_NODELAY, true) } - context.channel().config().isAutoRead = true - this.channel = context.channel() - connected = true + val channel = context.channel() + this.channel = channel + connection.state = ProtocolStates.HANDSHAKE + channel.config().isAutoRead = true } override fun channelInactive(context: ChannelHandlerContext) { - Log.log(LogMessageType.NETWORK, LogLevels.VERBOSE) { "Session closed ($address)" } + Log.log(LogMessageType.NETWORK, LogLevels.VERBOSE) { "Session closed (${connection.address})" } if (detached) return - connected = false + this.channel = null + connection.state = null } override fun handleError(error: Throwable) { @@ -158,11 +161,11 @@ class NettyClient( } else if (cause is EncoderException) { cause = error.cause ?: cause } - if (RunConfiguration.DISABLE_EROS || session !is StatusSession) { + if (RunConfiguration.DISABLE_EROS || session is StatusSession) { val log = if (cause is PacketHandleException || cause is PacketReadException) cause.cause else cause Log.log(LogMessageType.NETWORK_IN, LogLevels.WARN) { log } } - if (cause !is NetworkException || cause is CriticalNetworkException || state == ProtocolStates.LOGIN) { + if (cause !is NetworkException || cause is CriticalNetworkException || connection.state == ProtocolStates.LOGIN) { session.error = cause disconnect() return @@ -175,7 +178,7 @@ class NettyClient( private fun getChannel(): Channel? { val channel = this.channel - if (!connected || channel == null) { + if (channel == null || connection.state == null) { return null } return channel diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/receiver/PacketReceiver.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/receiver/PacketReceiver.kt index d8f71ee93..5b8a1d3b8 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/receiver/PacketReceiver.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/receiver/PacketReceiver.kt @@ -56,7 +56,7 @@ class PacketReceiver( } private fun tryHandle(type: PacketType, packet: S2CPacket) { - if (!network.connected) return + if (!network.connection.active) return try { handle(packet) @@ -93,7 +93,7 @@ class PacketReceiver( fun onReceive(type: PacketType, packet: S2CPacket) { if (network.detached) return - if (!network.connected) return + if (!network.connection.active) return val discard = notify(packet) if (discard) return diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/sender/PacketSender.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/sender/PacketSender.kt index a7f168969..1e00caccb 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/sender/PacketSender.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/packet/sender/PacketSender.kt @@ -41,7 +41,7 @@ class PacketSender( fun send(packet: C2SPacket) { if (network.detached) return - if (!network.connected) return + if (!network.connection.active) return val discard = notify(packet) if (discard) return diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketDecoder.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketDecoder.kt index 86c5f6409..f947f75a5 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketDecoder.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketDecoder.kt @@ -38,7 +38,7 @@ class PacketDecoder( val packetId = buffer.readVarInt() val data = buffer.readRest() - val state = client.state + val state = client.connection.state ?: throw IllegalStateException("Not connected!") val type = version?.s2c?.get(state, packetId) ?: DefaultPacketMapping.S2C_PACKET_MAPPING[state, packetId] ?: throw UnknownPacketIdException(packetId, state, version) diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketEncoder.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketEncoder.kt index bcff56344..ae8f0e443 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketEncoder.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/encoding/PacketEncoder.kt @@ -74,8 +74,8 @@ class PacketEncoder( throw PacketNotAvailableException(type, state, version) } - override fun encode(context: ChannelHandlerContext, packet: C2SPacket, out: MutableList) { - val state = client.state + private fun encode(packet: C2SPacket): ByteArray { + val state = client.connection.state!! val type = DefaultPackets.C2S[state]?.get(packet::class) ?: throw UnknownPacketException(packet::class.java) val id = getPacketId(version, state, type) @@ -86,7 +86,11 @@ class PacketEncoder( data.writeVarInt(id) data.writeBareByteArray(packetData.toArray()) - out += data.toArray() + return data.toArray() + } + + override fun encode(context: ChannelHandlerContext, packet: C2SPacket, out: MutableList) { + out += encode(packet) } companion object { diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/length/LengthDecoder.kt b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/length/LengthDecoder.kt index 06d0159b0..f013e2e21 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/length/LengthDecoder.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/network/client/netty/pipeline/length/LengthDecoder.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2023 Moritz Zwerger + * Copyright (C) 2020-2024 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. * @@ -13,6 +13,7 @@ package de.bixilon.minosoft.protocol.network.network.client.netty.pipeline.length +import de.bixilon.kutil.exception.FastException import de.bixilon.minosoft.protocol.network.network.client.netty.exceptions.ciritical.PacketTooLongException import io.netty.buffer.ByteBuf import io.netty.channel.ChannelHandlerContext @@ -76,6 +77,6 @@ class LengthDecoder( return varInt } - private class BufferTooShortException : Exception() + private class BufferTooShortException : FastException() } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/session/Session.kt b/src/main/java/de/bixilon/minosoft/protocol/network/session/Session.kt index 23b792360..6acb68f81 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/session/Session.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/session/Session.kt @@ -16,13 +16,10 @@ package de.bixilon.minosoft.protocol.network.session import de.bixilon.kutil.observer.DataObserver.Companion.observed import de.bixilon.minosoft.modding.event.master.EventMaster import de.bixilon.minosoft.modding.event.master.GlobalEventMaster -import de.bixilon.minosoft.protocol.network.network.client.ClientNetwork -import de.bixilon.minosoft.protocol.network.network.client.netty.NettyClient import de.bixilon.minosoft.protocol.versions.Version import java.util.concurrent.atomic.AtomicInteger abstract class Session { - val network: ClientNetwork = NettyClient(this) val events = EventMaster(GlobalEventMaster) val id = Session.id.getAndIncrement() var established = false @@ -31,9 +28,7 @@ abstract class Session { var error: Throwable? by observed(null) - open fun terminate() { - network.disconnect() - } + abstract fun terminate() companion object { private val id = AtomicInteger() diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/session/play/PlaySession.kt b/src/main/java/de/bixilon/minosoft/protocol/network/session/play/PlaySession.kt index 4a329e2a7..d6f746375 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/session/play/PlaySession.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/session/play/PlaySession.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.protocol.network.session.play +import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.kutil.cast.CastUtil.unsafeNull import de.bixilon.kutil.collections.CollectionUtil.synchronizedSetOf import de.bixilon.kutil.collections.CollectionUtil.toSynchronizedSet @@ -49,10 +50,12 @@ import de.bixilon.minosoft.gui.rendering.Rendering import de.bixilon.minosoft.modding.event.events.chat.ChatMessageEvent import de.bixilon.minosoft.modding.event.events.loading.RegistriesLoadEvent import de.bixilon.minosoft.modding.event.events.session.play.PlaySessionCreateEvent -import de.bixilon.minosoft.modding.event.listener.CallbackEventListener +import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen import de.bixilon.minosoft.modding.event.master.GlobalEventMaster import de.bixilon.minosoft.modding.loader.phase.DefaultModPhases -import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.connection.NetworkConnection +import de.bixilon.minosoft.protocol.connection.ServerConnection +import de.bixilon.minosoft.protocol.network.network.client.netty.NettyClient import de.bixilon.minosoft.protocol.network.session.Session import de.bixilon.minosoft.protocol.network.session.play.channel.DefaultChannelHandlers import de.bixilon.minosoft.protocol.network.session.play.channel.SessionChannelHandler @@ -75,11 +78,13 @@ import java.util.concurrent.atomic.AtomicInteger class PlaySession( - val address: ServerAddress, + val connection: ServerConnection, val account: Account, override val version: Version, val profiles: SelectedProfiles = SelectedProfiles(), ) : Session() { + @Deprecated("connection") + val network = NettyClient(connection.unsafeCast(), this) val sessionId = KUtil.secureRandomUUID() val settingsManager = ClientSettingsManager(this) val registries = Registries().apply { updateFlattened(version.flattened) } @@ -127,15 +132,16 @@ class PlaySession( RegistriesFixer.register(this) DefaultChannelHandlers.register(this) - network::connected.observe(this) { - if (it) { + connection.unsafeCast()::state.observe(this) { + if (it != null) { ACTIVE_CONNECTIONS += this ERRORED_CONNECTIONS -= this state = PlaySessionStates.HANDSHAKING - network.send(HandshakeC2SP(address, HandshakeC2SP.Actions.PLAY, version.protocolId)) + val address = connection.unsafeCast().address + network.send(HandshakeC2SP(address.hostname, address.port, HandshakeC2SP.Actions.PLAY, version.protocolId)) // after sending it, switch to next state - network.state = ProtocolStates.LOGIN + network.connection.state = ProtocolStates.LOGIN } else { established = true assetsManager.unload() @@ -146,7 +152,7 @@ class PlaySession( } } } - network::state.observe(this) { state -> + connection.unsafeCast()::state.observe(this) { state -> when (state) { ProtocolStates.HANDSHAKE, ProtocolStates.STATUS -> Broken("Invalid state!") ProtocolStates.LOGIN -> { @@ -162,17 +168,17 @@ class PlaySession( CLI.session = this } - events.register(CallbackEventListener.of { + events.listen { val additionalPrefix = when (it.message.type.position) { ChatTextPositions.SYSTEM -> "[SYSTEM] " ChatTextPositions.HOTBAR -> "[HOTBAR] " else -> "" } Log.log(LogMessageType.CHAT_IN, level = if (it.message.type.position == ChatTextPositions.HOTBAR) LogLevels.VERBOSE else LogLevels.INFO, prefix = ChatComponent.of(additionalPrefix)) { it.message.text } - }) + } } - ProtocolStates.CONFIGURATION -> Unit + else -> Unit } } ticker.init() @@ -206,7 +212,7 @@ class PlaySession( } val keyManagement = SignatureKeyManagement(this, account) - if (version.requiresSignedChat && !profiles.session.signature.disableKeys) { + if (version.requiresSignedChat && !profiles.session.signature.disableKeys && connection is NetworkConnection) { taskWorker += WorkerTask(optional = true) { keyManagement.init(latch) } } @@ -242,8 +248,7 @@ class PlaySession( private fun establish(latch: AbstractLatch?) { latch?.dec() // remove initial value - Log.log(LogMessageType.NETWORK, level = LogLevels.INFO) { "Connecting to server: $address" } - network.connect(address, profiles.other.nativeNetwork) + network.connect() state = PlaySessionStates.ESTABLISHING } @@ -260,7 +265,7 @@ class PlaySession( } override fun terminate() { - super.terminate() + network.disconnect() state = PlaySessionStates.DISCONNECTED } diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/session/status/StatusSession.kt b/src/main/java/de/bixilon/minosoft/protocol/network/session/status/StatusSession.kt index 1d81620e3..1bbb2c79c 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/session/status/StatusSession.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/session/status/StatusSession.kt @@ -17,7 +17,8 @@ import de.bixilon.kutil.observer.DataObserver.Companion.observe import de.bixilon.kutil.observer.DataObserver.Companion.observed import de.bixilon.minosoft.modding.event.events.session.status.StatusSessionCreateEvent import de.bixilon.minosoft.modding.event.master.GlobalEventMaster -import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.AddressResolver +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.session.Session import de.bixilon.minosoft.protocol.packets.c2s.handshake.HandshakeC2SP import de.bixilon.minosoft.protocol.packets.c2s.status.StatusRequestC2SP @@ -27,132 +28,111 @@ import de.bixilon.minosoft.protocol.status.StatusPing import de.bixilon.minosoft.protocol.status.StatusPong import de.bixilon.minosoft.protocol.versions.Version import de.bixilon.minosoft.protocol.versions.Versions -import de.bixilon.minosoft.util.DNSUtil import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.LogMessageType class StatusSession( - var address: String, + var hostname: String, var forcedVersion: Version? = null, ) : Session() { + var connection: NetworkConnection? = null + private var resolver: AddressResolver? = null + var status: ServerStatus? by observed(null) var ping: StatusPing? by observed(null) var pong: StatusPong? by observed(null) - var realAddress: ServerAddress? = null - private set - private var addresses: List? = null - private var addressIndex = 0 - var serverVersion: Version? = null var state by observed(StatusSessionStates.WAITING) val timeout = TimeoutHandler(this) - init { this::error.observe(this) { if (it == null) return@observe - ping = null - status = null + terminate() + reset() state = StatusSessionStates.ERROR - timeout.cancel() - network.disconnect() - } - network::connected.observe(this) { - if (it) { - state = StatusSessionStates.HANDSHAKING - network.send(HandshakeC2SP(realAddress!!, HandshakeC2SP.Actions.STATUS, forcedVersion?.protocolId ?: Versions.AUTOMATIC.protocolId)) - network.state = ProtocolStates.STATUS - return@observe - } - if (status != null) { - return@observe - } - tryNextAddress() - } - - network::state.observe(this) { - when (it) { - ProtocolStates.HANDSHAKE -> {} - ProtocolStates.PLAY, ProtocolStates.LOGIN, ProtocolStates.CONFIGURATION -> throw IllegalStateException("Invalid state!") - ProtocolStates.STATUS -> { - state = StatusSessionStates.QUERYING_STATUS - network.send(StatusRequestC2SP()) - } - } } GlobalEventMaster.fire(StatusSessionCreateEvent(this)) - } - private fun tryNextAddress() { - val addresses = this.addresses ?: return - val nextIndex = ++addressIndex - if (addresses.size > nextIndex) { - val nextAddress = addresses[nextIndex] - Log.log(LogMessageType.NETWORK) { "Could not connect to $address, trying next hostname: $nextAddress" } - realAddress = nextAddress - ping(nextAddress) + this::error.observe(this) { + if (it == null) return@observe + tryNext() } } - private fun resolve(): List { - state = StatusSessionStates.RESOLVING + private fun NetworkConnection.register() { + this::state.observe(this) { + if (this@StatusSession.connection != this) return@observe + when (it) { + ProtocolStates.HANDSHAKE -> { + // send handshake + send(HandshakeC2SP(hostname, this.address.port, HandshakeC2SP.Actions.STATUS, forcedVersion?.protocolId ?: Versions.AUTOMATIC.protocolId)) + state = ProtocolStates.STATUS + } - var addresses = this.addresses - if (addresses == null) { - addresses = DNSUtil.resolveServerAddress(address) - realAddress = addresses.first() - this.addresses = addresses - this.addressIndex = 0 + ProtocolStates.STATUS -> { + this@StatusSession.state = StatusSessionStates.QUERYING_STATUS + send(StatusRequestC2SP()) + } + + null -> Unit // done + + else -> throw IllegalStateException("Illegal status state: $it") + } } - return addresses + } + + override fun terminate() { + timeout.cancel() + this.connection?.disconnect() + this.connection = null + state = StatusSessionStates.WAITING } fun reset() { - timeout.cancel() - realAddress = null - this.addresses = null - this.addressIndex = 0 status = null ping = null pong = null serverVersion = null error = null - state = StatusSessionStates.WAITING } - fun ping(address: ServerAddress) { - if (state == StatusSessionStates.ESTABLISHING || network.connected) { + private fun tryNext(): Boolean { + val network = resolver!!.tryNext() ?: return false + this.connection = network + + if (state == StatusSessionStates.ESTABLISHING) { error("Already connecting!") } + timeout.register() - Log.log(LogMessageType.NETWORK) { "Pinging $address (from ${this.address})" } + Log.log(LogMessageType.NETWORK) { "Pinging ${network.address} (from ${this.hostname})" } state = StatusSessionStates.ESTABLISHING - network.connect(address, false) + network.register() + network.connect(this) + + return true } fun ping() { - if (state == StatusSessionStates.RESOLVING || state == StatusSessionStates.ESTABLISHING || network.connected) { + if (state == StatusSessionStates.RESOLVING || state == StatusSessionStates.ESTABLISHING) { error("Already connecting!") } + terminate() reset() + state = StatusSessionStates.RESOLVING - val addresses: List try { - // TODO: Don't resolve if address is ip - addresses = resolve() - } catch (exception: Exception) { - Log.log(LogMessageType.NETWORK) { "Can not resolve ${this.address}" } + val resolver = AddressResolver(hostname) + this.resolver = resolver + } catch (error: Exception) { + Log.log(LogMessageType.NETWORK) { "Can not resolve ${this.hostname}" } return } - ping(addresses.first()) - } - - override fun terminate() { - super.terminate() - state = StatusSessionStates.WAITING + tryNext() } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/handshake/HandshakeC2SP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/handshake/HandshakeC2SP.kt index 6afcbec1b..9d264e78e 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/handshake/HandshakeC2SP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/c2s/handshake/HandshakeC2SP.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2023 Moritz Zwerger + * Copyright (C) 2020-2024 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. * @@ -12,7 +12,6 @@ */ package de.bixilon.minosoft.protocol.packets.c2s.handshake -import de.bixilon.minosoft.protocol.address.ServerAddress import de.bixilon.minosoft.protocol.packets.c2s.C2SPacket import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition import de.bixilon.minosoft.protocol.protocol.buffers.OutByteBuffer @@ -21,20 +20,21 @@ import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogMessageType class HandshakeC2SP( - val address: ServerAddress, + val hostname: String, + val port: Int, val action: Actions = Actions.STATUS, val protocolId: Int = ProtocolDefinition.QUERY_PROTOCOL_VERSION_ID, ) : C2SPacket { override fun write(buffer: OutByteBuffer) { buffer.writeVarInt(protocolId) - buffer.writeString(address.hostname) - buffer.writeShort(address.port) + buffer.writeString(hostname) + buffer.writeShort(port) buffer.writeVarInt(action.protocolId) } override fun log(reducedLog: Boolean) { - Log.log(LogMessageType.NETWORK_OUT, LogLevels.VERBOSE) { "Handshake (protocolId=$protocolId, hostname=${address.hostname}, port=${address.port}, action=$action)" } + Log.log(LogMessageType.NETWORK_OUT, LogLevels.VERBOSE) { "Handshake (protocolId=$protocolId, hostname=${hostname}, port=${port}, action=$action)" } } enum class Actions(val protocolId: Int) { diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/registry/PacketType.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/registry/PacketType.kt index e10fe0612..663a90150 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/registry/PacketType.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/registry/PacketType.kt @@ -13,10 +13,13 @@ package de.bixilon.minosoft.protocol.packets.registry +import de.bixilon.kutil.cast.CastUtil.nullCast +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.network.client.netty.exceptions.PacketBufferUnderflowException import de.bixilon.minosoft.protocol.network.network.client.netty.exceptions.implementation.PacketNotImplementedException import de.bixilon.minosoft.protocol.network.session.Session import de.bixilon.minosoft.protocol.network.session.play.PlaySession +import de.bixilon.minosoft.protocol.network.session.status.StatusSession import de.bixilon.minosoft.protocol.packets.registry.factory.PacketFactory import de.bixilon.minosoft.protocol.packets.types.Packet import de.bixilon.minosoft.protocol.protocol.buffers.InByteBuffer @@ -31,7 +34,8 @@ class PacketType( ) { fun create(data: ByteArray, session: Session): Packet { - val factory = this.factory ?: throw PacketNotImplementedException(name, session.network.state, session.version) + val connection = session.nullCast()?.connection ?: session.nullCast()?.connection?.nullCast() + val factory = this.factory ?: throw PacketNotImplementedException(name, connection!!.state!!, session.version) val buffer = if (session is PlaySession) PlayInByteBuffer(data, session) else InByteBuffer(data) val packet = factory.create(buffer) diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/common/KickS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/common/KickS2CP.kt index b16fb58c2..5ea8ceca8 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/common/KickS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/common/KickS2CP.kt @@ -28,18 +28,18 @@ class KickS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket { val reason: ChatComponent = if (buffer.session.network.state == ProtocolStates.LOGIN && buffer.versionId >= V_23W42A) buffer.readChatComponent() else buffer.readNbtChatComponent() override fun handle(session: PlaySession) { - if (!session.network.connected) { + if (!session.connection.active) { return // already disconnected, maybe timed out? } session.events.fire(KickEvent(session, reason)) // got kicked - session.network.disconnect() + session.terminate() if (session.network.state == ProtocolStates.LOGIN) { session.state = PlaySessionStates.ERROR } else { session.state = PlaySessionStates.KICKED } - Log.log(LogMessageType.NETWORK, LogLevels.WARN) { "Kicked from ${session.address}: $reason" } + Log.log(LogMessageType.NETWORK, LogLevels.WARN) { "Kicked from ${session.connection.identifier}: $reason" } } override fun log(reducedLog: Boolean) { diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/PongS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/PongS2CP.kt index 3be32c3a4..b8c6c1569 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/PongS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/PongS2CP.kt @@ -28,7 +28,7 @@ class PongS2CP(buffer: InByteBuffer) : StatusS2CPacket { override fun handle(session: StatusSession) { val ping = session.ping ?: return val latency = nanos() - ping.nanos - session.network.disconnect() + session.terminate() session.pong = StatusPong(latency) session.state = StatusSessionStates.PING_DONE } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/StatusS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/StatusS2CP.kt index 15b9eb462..91dd91ee5 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/StatusS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/status/StatusS2CP.kt @@ -41,7 +41,7 @@ class StatusS2CP(buffer: InByteBuffer) : StatusS2CPacket { val ping = StatusPing() session.ping = ping session.state = StatusSessionStates.QUERYING_PING - session.network.send(PingC2SP(ThreadLocalRandom.current().nextLong())) + session.connection?.send(PingC2SP(ThreadLocalRandom.current().nextLong())) } override fun log(reducedLog: Boolean) { diff --git a/src/main/java/de/bixilon/minosoft/terminal/AutoConnect.kt b/src/main/java/de/bixilon/minosoft/terminal/AutoConnect.kt index a617e5cbd..ef4b98acd 100644 --- a/src/main/java/de/bixilon/minosoft/terminal/AutoConnect.kt +++ b/src/main/java/de/bixilon/minosoft/terminal/AutoConnect.kt @@ -19,6 +19,7 @@ import de.bixilon.kutil.shutdown.ShutdownManager import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.protocol.network.session.play.PlaySessionStates.Companion.disconnected import de.bixilon.minosoft.protocol.network.session.status.StatusSession @@ -35,7 +36,7 @@ object AutoConnect { private fun autoConnect(address: ServerAddress, version: Version, account: Account) { val session = PlaySession( - address = address, + connection = NetworkConnection(address, true), // TODO: native network account = account, version = version, ) @@ -67,7 +68,7 @@ object AutoConnect { if (version == Versions.AUTOMATIC) { Log.log(LogMessageType.AUTO_CONNECT, LogLevels.INFO) { "Pinging server to get version..." } val ping = StatusSession(address) - ping::status.observe(this) { autoConnect(ping.realAddress!!, ping.serverVersion ?: throw IllegalArgumentException("Could not determinate server's version!"), account) } + ping::status.observe(this) { autoConnect(ping.connection!!.address, ping.serverVersion ?: throw IllegalArgumentException("Could not determinate server's version!"), account) } ping::error.observe(this) { exitProcess(1) } ping.ping() return diff --git a/src/main/java/de/bixilon/minosoft/terminal/commands/ConnectCommand.kt b/src/main/java/de/bixilon/minosoft/terminal/commands/ConnectCommand.kt index 110721ac9..612c94758 100644 --- a/src/main/java/de/bixilon/minosoft/terminal/commands/ConnectCommand.kt +++ b/src/main/java/de/bixilon/minosoft/terminal/commands/ConnectCommand.kt @@ -22,6 +22,7 @@ import de.bixilon.minosoft.commands.stack.print.PrintTarget import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager import de.bixilon.minosoft.data.accounts.Account import de.bixilon.minosoft.protocol.address.ServerAddress +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.protocol.network.session.status.StatusSession import de.bixilon.minosoft.protocol.versions.Version @@ -38,7 +39,7 @@ object ConnectCommand : Command { if (version == null) { stack.print.print("Pinging server to get version...") val ping = StatusSession(address) - ping::status.observe(this) { connect(stack.print, ping.realAddress!!, ping.serverVersion ?: throw IllegalArgumentException("Could not determinate server's version!"), account) } + ping::status.observe(this) { connect(stack.print, ping.connection!!.address, ping.serverVersion ?: throw IllegalArgumentException("Could not determinate server's version!"), account) } ping::error.observe(this) { stack.print.print("Could not ping $address: $it") } ping.ping() return@add @@ -52,7 +53,7 @@ object ConnectCommand : Command { private fun connect(print: PrintTarget, address: ServerAddress, version: Version, account: Account) { print.print("Connecting to $address") - val session = PlaySession(address, account, version) + val session = PlaySession(NetworkConnection(address, true), account, version) // TODO: native network session.connect() } } diff --git a/src/main/java/de/bixilon/minosoft/terminal/commands/SessionManageCommand.kt b/src/main/java/de/bixilon/minosoft/terminal/commands/SessionManageCommand.kt index a975d1b4e..867ae0d74 100644 --- a/src/main/java/de/bixilon/minosoft/terminal/commands/SessionManageCommand.kt +++ b/src/main/java/de/bixilon/minosoft/terminal/commands/SessionManageCommand.kt @@ -31,20 +31,20 @@ object SessionManageCommand : Command { val filtered = it.collect() if (filtered.isEmpty()) throw CommandException("No session matched your filter!") - it.print.print(table(filtered, "Id", "State", "Address") { c -> arrayOf(c.id, c.state, c.address) }) + it.print.print(table(filtered, "Id", "State", "Address") { c -> arrayOf(c.id, c.state, c.connection.identifier) }) }) .addChild(ArgumentNode("filter", SessionParser, executable = true)), LiteralNode("terminate", aliases = setOf("disconnect")).apply { addFilter { stack, sessions -> var count = 0 - sessions.filter { it.network.connected }.forEach { it.terminate(); count++ } + sessions.forEach { it.terminate(); count++ } stack.print.print("Terminated $count sessions.") } }, LiteralNode("select").apply { addFilter(false) { stack, sessions -> val session = sessions.first() - if (!session.network.connected) { + if (session.network.connection.state == null) { throw CommandException("Session $session not established anymore!") } CLI.session = session diff --git a/src/main/java/de/bixilon/minosoft/util/DNSUtil.kt b/src/main/java/de/bixilon/minosoft/util/DNSUtil.kt index f50d6def7..b72d20cc1 100644 --- a/src/main/java/de/bixilon/minosoft/util/DNSUtil.kt +++ b/src/main/java/de/bixilon/minosoft/util/DNSUtil.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2023 Moritz Zwerger + * Copyright (C) 2020-2024 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. * @@ -22,21 +22,22 @@ import org.xbill.DNS.Type object DNSUtil { fun resolveServerAddress(hostname: String): List { - val originalAddress = getServerAddress(hostname) + val original = getServerAddress(hostname) + // TODO: Don't resolve if address is ip address if (":" in hostname) { // port provided, skip srv check - return listOf(originalAddress) + return listOf(original) } val query = "_minecraft._tcp.$hostname" - val records = catchAll { Lookup(query, Type.SRV).run() } ?: return listOf(originalAddress) + val records = catchAll { Lookup(query, Type.SRV).run() } ?: return listOf(original) val addresses: MutableList = mutableListOf() for (record in records) { if (record !is SRVRecord) continue addresses += ServerAddress(record.target.toString(true), record.port) } - addresses += originalAddress + addresses += original return addresses } diff --git a/src/main/java/de/bixilon/minosoft/util/crash/section/SessionCrashSection.kt b/src/main/java/de/bixilon/minosoft/util/crash/section/SessionCrashSection.kt index 521950372..311f2ff86 100644 --- a/src/main/java/de/bixilon/minosoft/util/crash/section/SessionCrashSection.kt +++ b/src/main/java/de/bixilon/minosoft/util/crash/section/SessionCrashSection.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.util.crash.section +import de.bixilon.minosoft.protocol.connection.NetworkConnection import de.bixilon.minosoft.protocol.network.session.play.PlaySession class SessionCrashSection( @@ -20,16 +21,19 @@ class SessionCrashSection( ) : ArrayCrashSection("Sessions", sessions) { override fun format(entry: PlaySession, builder: StringBuilder, intent: String) { + val connection = entry.connection builder.appendProperty(intent, "Id", entry.id) builder.appendProperty(intent, "Version", entry.version) builder.appendProperty(intent, "Account", entry.account.username) - builder.appendProperty(intent, "Address", entry.address) + builder.appendProperty(intent, "Address", entry.connection.identifier) builder.appendProperty(intent, "Brand", entry.serverInfo.brand) builder.appendProperty(intent, "Events", entry.events.size) builder.appendProperty(intent, "State", entry.state) - builder.appendProperty(intent, "DefaultPacketMapping state", entry.network.state) - builder.appendProperty(intent, "Compression threshold", entry.network.compressionThreshold) - builder.appendProperty(intent, "Encrypted", entry.network.encrypted) + if (connection is NetworkConnection) { + builder.appendProperty(intent, "Network state", connection.state) + builder.appendProperty(intent, "Compression threshold", connection.client?.compressionThreshold) + builder.appendProperty(intent, "Encrypted", connection.client?.encrypted) + } builder.appendProperty(intent, "Was connected", entry.established) builder.appendProperty(intent, "Rendering", entry.rendering != null) builder.appendProperty(intent, "Error", entry.error) diff --git a/src/test/java/de/bixilon/minosoft/data/text/ChatComponentTest.kt b/src/test/java/de/bixilon/minosoft/data/text/ChatComponentTest.kt index 6d87cca8d..b86e7050e 100644 --- a/src/test/java/de/bixilon/minosoft/data/text/ChatComponentTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/text/ChatComponentTest.kt @@ -318,6 +318,15 @@ internal class ChatComponentTest { assertEquals(text, expected) } + @Test + fun something() { // tree.ac + val string = """"§2Join the Other Server? Find it at §6Port 25566§2!"""" + val chat = ChatComponent.of(string) + + TODO() + + } + private fun assertEquals(expected: ChatComponent, actual: ChatComponent) { when (expected) { is BaseComponent -> {