mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 09:26:11 -04:00
abstract server connection more
Server connection is now stateless and is atm always a network connection. It might something else in the future
This commit is contained in:
parent
57a8985a94
commit
d42f9eb214
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<PlaySession> {
|
||||
|
||||
override fun passes(value: PlaySession): Boolean {
|
||||
val connected = value.network.connected
|
||||
return connected == this.connected
|
||||
}
|
||||
|
||||
companion object : TargetPropertyFactory<PlaySession> {
|
||||
override val name: String = "connected"
|
||||
|
||||
override fun read(reader: CommandReader): ConnectedProperty {
|
||||
return ConnectedProperty(reader.readBoolean() ?: throw ExpectedArgumentError(reader))
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,5 @@ object SessionTargetProperties : TargetProperties<PlaySession>() {
|
||||
|
||||
init {
|
||||
register(StateProperty)
|
||||
register(ConnectedProperty)
|
||||
}
|
||||
}
|
||||
|
@ -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<NetworkConnection>()?.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) {
|
||||
|
@ -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<NetworkConnection>()?.address ?: return
|
||||
if (address.hostname != "localhost" && address.hostname != "127.0.0.1") {
|
||||
return
|
||||
}
|
||||
events.listen<ChatMessageEvent> {
|
||||
|
@ -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<Pane>(), 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<Pane>(), 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<Pane>(), 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<Pane>(), Refreshable {
|
||||
private val SERVER_INFO_PROPERTIES: List<Pair<ResourceLocation, (ServerCard) -> 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 { " " },
|
||||
|
@ -62,7 +62,7 @@ object CustomServerType : ServerType {
|
||||
continue
|
||||
}
|
||||
DefaultThreadPool += ForcePooledRunnable {
|
||||
ping.network.disconnect()
|
||||
ping.terminate()
|
||||
ping.ping()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)) } }
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<ServerAddress> = 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)
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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)
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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)
|
||||
}
|
@ -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()
|
||||
|
||||
|
@ -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<Any>(), 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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -74,8 +74,8 @@ class PacketEncoder(
|
||||
throw PacketNotAvailableException(type, state, version)
|
||||
}
|
||||
|
||||
override fun encode(context: ChannelHandlerContext, packet: C2SPacket, out: MutableList<Any>) {
|
||||
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<Any>) {
|
||||
out += encode(packet)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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<NetworkConnection>()::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<NetworkConnection>().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<NetworkConnection>()::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<ChatMessageEvent> {
|
||||
events.listen<ChatMessageEvent> {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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<ServerAddress>? = 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<ServerAddress> {
|
||||
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<ServerAddress>
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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<StatusSession>()?.connection ?: session.nullCast<PlaySession>()?.connection?.nullCast<NetworkConnection>()
|
||||
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)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<ServerAddress> {
|
||||
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<ServerAddress> = mutableListOf()
|
||||
for (record in records) {
|
||||
if (record !is SRVRecord) continue
|
||||
addresses += ServerAddress(record.target.toString(true), record.port)
|
||||
}
|
||||
addresses += originalAddress
|
||||
addresses += original
|
||||
return addresses
|
||||
}
|
||||
|
||||
|
@ -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<PlaySession>("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)
|
||||
|
@ -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 -> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user