From 21f3a66283a6aeeabe177c895c3d1c05e78e8485 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 19 Mar 2024 16:59:01 +0700 Subject: [PATCH] Now original game client can properly connect and be on server, and also can request a disconnect --- .../kstarbound/client/ClientConnection.kt | 39 +++++++++++++++++-- .../kstarbound/network/Connection.kt | 7 +--- .../kstarbound/network/PacketRegistry.kt | 9 +++-- .../clientbound/ServerDisconnectPacket.kt | 2 +- .../ClientDisconnectRequestPacket.kt | 21 ++++++++++ .../kstarbound/server/ServerConnection.kt | 28 +++++++++++++ .../kstarbound/server/world/ServerWorld.kt | 8 ++++ 7 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/serverbound/ClientDisconnectRequestPacket.kt diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientConnection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientConnection.kt index 0d963564..5467af1b 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientConnection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/client/ClientConnection.kt @@ -16,6 +16,7 @@ import ru.dbotthepony.kstarbound.network.ConnectionType import ru.dbotthepony.kstarbound.network.IClientPacket import ru.dbotthepony.kstarbound.network.packets.ClientContextUpdatePacket import ru.dbotthepony.kstarbound.network.packets.ProtocolRequestPacket +import ru.dbotthepony.kstarbound.network.packets.serverbound.ClientDisconnectRequestPacket import java.net.SocketAddress import java.util.* @@ -50,15 +51,47 @@ class ClientConnection(val client: StarboundClient, type: ConnectionType) : Conn } override fun flush() { - val entries = rpc.write() + if (!pendingDisconnect) { + val entries = rpc.write() - if (entries != null) { - channel.write(ClientContextUpdatePacket(entries, KOptional(), KOptional())) + if (entries != null) { + channel.write(ClientContextUpdatePacket(entries, KOptional(), KOptional())) + } } super.flush() } + private var pendingDisconnect = false + + fun disconnectNow() { + pendingDisconnect = false + + if (channel.isOpen) { + channel.close() + } + } + + override fun disconnect(reason: String) { + if (pendingDisconnect) + return + + if (channel.isOpen) { + pendingDisconnect = true + sendAndFlush(ClientDisconnectRequestPacket) + } else { + disconnectNow() + } + } + + override fun onChannelClosed() { + super.onChannelClosed() + + if (pendingDisconnect) { + disconnectNow() + } + } + companion object { private val LOGGER = LogManager.getLogger() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/Connection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/Connection.kt index 97bef3d5..749ed270 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/Connection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/Connection.kt @@ -26,7 +26,7 @@ abstract class Connection(val side: ConnectionSide, val type: ConnectionType) : val hasChannel get() = ::channel.isInitialized lateinit var channel: Channel - protected set + private set var isLegacy: Boolean = true protected set @@ -113,10 +113,7 @@ abstract class Connection(val side: ConnectionSide, val type: ConnectionType) : channel.flush() } - fun disconnect(reason: String) { - channel.flush() - channel.close() - } + abstract fun disconnect(reason: String = "") override fun close() { channel.close() diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt index ca71d81d..39e85694 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/PacketRegistry.kt @@ -33,6 +33,7 @@ import ru.dbotthepony.kstarbound.network.packets.clientbound.LegacyTileUpdatePac import ru.dbotthepony.kstarbound.network.packets.clientbound.ServerDisconnectPacket import ru.dbotthepony.kstarbound.network.packets.clientbound.WorldStartPacket import ru.dbotthepony.kstarbound.network.packets.clientbound.WorldStopPacket +import ru.dbotthepony.kstarbound.network.packets.serverbound.ClientDisconnectRequestPacket import ru.dbotthepony.kstarbound.server.network.packets.TrackedPositionPacket import ru.dbotthepony.kstarbound.server.network.packets.TrackedSizePacket import java.io.BufferedInputStream @@ -202,7 +203,7 @@ class PacketRegistry(val isLegacy: Boolean) { LOGGER.error("Packet ($packetType/${type.type}) of ${dataLength.absoluteValue} bytes is bigger than maximum allowed $MAX_PACKET_SIZE bytes") discardBytes = dataLength.absoluteValue } else { - LOGGER.debug("Packet type {} ({}) received on {} (size {} bytes)", packetType, type.type, side, dataLength.absoluteValue) + // LOGGER.debug("Packet type {} ({}) received on {} (size {} bytes)", packetType, type.type, side, dataLength.absoluteValue) readingType = type readableBytes = dataLength.absoluteValue isCompressed = dataLength < 0 @@ -252,7 +253,7 @@ class PacketRegistry(val isLegacy: Boolean) { stream2.writeByte(type.id) stream2.writeSignedVarInt(-buffers.size) stream2.write(buffers.elements(), 0, buffers.size) - LOGGER.debug("Packet type {} ({}) sent from {} (size {} bytes / COMPRESSED size {} bytes)", type.id, type.type, side, stream.length, buffers.size) + // LOGGER.debug("Packet type {} ({}) sent from {} (size {} bytes / COMPRESSED size {} bytes)", type.id, type.type, side, stream.length, buffers.size) ctx.write(buff, promise) } else { // send as-is @@ -261,7 +262,7 @@ class PacketRegistry(val isLegacy: Boolean) { stream2.writeByte(type.id) stream2.writeSignedVarInt(stream.length) stream2.write(stream.array, 0, stream.length) - LOGGER.debug("Packet type {} ({}) sent from {} (size {} bytes)", type.id, type.type, side, stream.length) + // LOGGER.debug("Packet type {} ({}) sent from {} (size {} bytes)", type.id, type.type, side, stream.length) ctx.write(buff, promise) } } @@ -349,7 +350,7 @@ class PacketRegistry(val isLegacy: Boolean) { // Packets sent universe client -> universe server LEGACY.add(::ClientConnectPacket) // ClientConnect - LEGACY.skip("ClientDisconnectRequest") + LEGACY.add(ClientDisconnectRequestPacket::read) LEGACY.add(::HandshakeResponsePacket) // HandshakeResponse LEGACY.skip("PlayerWarp") LEGACY.skip("FlyShip") diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/clientbound/ServerDisconnectPacket.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/clientbound/ServerDisconnectPacket.kt index 611e32b2..4b3e4162 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/clientbound/ServerDisconnectPacket.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/clientbound/ServerDisconnectPacket.kt @@ -15,6 +15,6 @@ class ServerDisconnectPacket(val reason: String = "") : IClientPacket { } override fun play(connection: ClientConnection) { - TODO("Not yet implemented") + connection.disconnectNow() } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/serverbound/ClientDisconnectRequestPacket.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/serverbound/ClientDisconnectRequestPacket.kt new file mode 100644 index 00000000..5cde89b0 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/network/packets/serverbound/ClientDisconnectRequestPacket.kt @@ -0,0 +1,21 @@ +package ru.dbotthepony.kstarbound.network.packets.serverbound + +import ru.dbotthepony.kstarbound.network.IServerPacket +import ru.dbotthepony.kstarbound.server.ServerConnection +import java.io.DataInputStream +import java.io.DataOutputStream + +object ClientDisconnectRequestPacket : IServerPacket { + override fun write(stream: DataOutputStream, isLegacy: Boolean) { + if (isLegacy) stream.writeBoolean(false) + } + + override fun play(connection: ServerConnection) { + connection.disconnect("Disconnect by user.") + } + + fun read(stream: DataInputStream, isLegacy: Boolean): ClientDisconnectRequestPacket { + if (isLegacy) stream.readBoolean() + return ClientDisconnectRequestPacket + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt index 62f52c20..3e46f517 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/ServerConnection.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.kstarbound.server +import com.google.gson.JsonObject import io.netty.channel.ChannelHandlerContext import it.unimi.dsi.fastutil.bytes.ByteArrayList import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap @@ -20,6 +21,7 @@ import ru.dbotthepony.kstarbound.network.ConnectionType import ru.dbotthepony.kstarbound.network.IServerPacket import ru.dbotthepony.kstarbound.network.packets.ClientContextUpdatePacket import ru.dbotthepony.kstarbound.network.packets.clientbound.LegacyTileArrayUpdatePacket +import ru.dbotthepony.kstarbound.network.packets.clientbound.ServerDisconnectPacket import ru.dbotthepony.kstarbound.server.world.IChunkSource import ru.dbotthepony.kstarbound.server.world.LegacyChunkSource import ru.dbotthepony.kstarbound.server.world.ServerWorld @@ -41,6 +43,10 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn init { connectionID = server.nextConnectionID.incrementAndGet() + + rpc.add("team.fetchTeamStatus") { + JsonObject() + } } override fun toString(): String { @@ -145,6 +151,28 @@ class ServerConnection(val server: StarboundServer, type: ConnectionType) : Conn } } + override fun disconnect(reason: String) { + if (channel.isOpen) { + // send pending updates + flush() + } + + tickets.values.forEach { it.cancel() } + tickets.clear() + pendingSend.clear() + + if (::shipWorld.isInitialized) { + shipWorld.close() + } + + if (channel.isOpen) { + // say goodbye + channel.write(ServerDisconnectPacket(reason)) + channel.flush() + channel.close() + } + } + private fun recomputeTrackedChunks() { val world = world ?: return val trackedPositionChunk = world.geometry.chunkFromCell(trackedPosition) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt index d1fef7ad..33b8fa34 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/server/world/ServerWorld.kt @@ -4,6 +4,7 @@ import com.google.gson.JsonObject import it.unimi.dsi.fastutil.longs.Long2ObjectFunction import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet +import org.apache.logging.log4j.LogManager import ru.dbotthepony.kommons.collect.chainOptionalFutures import ru.dbotthepony.kommons.util.KOptional import ru.dbotthepony.kstarbound.Starbound @@ -126,6 +127,8 @@ class ServerWorld( override fun close() { if (isClosed.compareAndSet(false, true)) { + LOGGER.info("Shutting down $this") + super.close() spinner.unpause() @@ -135,6 +138,7 @@ class ServerWorld( } } + server.worlds.remove(this) LockSupport.unpark(thread) } } @@ -394,4 +398,8 @@ class ServerWorld( } } } + + companion object { + private val LOGGER = LogManager.getLogger() + } }