From 799edce45f30c38aadb1e84d6d3a3b0dcb12c3bb Mon Sep 17 00:00:00 2001 From: RTAkland Date: Sat, 5 Sep 2026 13:22:00 +0800 Subject: [PATCH] Make packets public and formatted, fix state machine change state incorrectly --- .../kotlin/cn/rtast/libmc/chat/chat.kt | 146 ---------- .../libmc/chat/packet/PacketDirection.kt | 14 - .../AckFinishConfigurationPacket.kt | 23 -- ...lientboundDisconnectConfigurationPacket.kt | 28 -- .../configuration/ClientboundPingPacket.kt | 25 -- .../ClientboundSelectKnownPacksPacket.kt | 30 --- .../FinishConfigurationPacket.kt | 23 -- .../packet/configuration/KeepAlivePacket.kt | 28 -- .../configuration/ServerboundPongPacket.kt | 25 -- .../ServerboundSelectKnownPacksPacket.kt | 30 --- .../chat/packet/handshake/HandshakePacket.kt | 36 --- .../login/ClientboundDisconnectLoginPacket.kt | 28 -- .../packet/login/LoginAcknowledgedPacket.kt | 23 -- .../chat/packet/login/LoginStartPacket.kt | 32 --- .../chat/packet/login/LoginSuccessPacket.kt | 32 --- .../play/ClientboundDisconnectPlayPacket.kt | 28 -- .../play/ClientboundKeepAlivePlayPacket.kt | 26 -- .../play/ServerboundChatMessagePacket.kt | 33 --- .../play/ServerboundKeepAlivePlayPacket.kt | 26 -- .../libmc/chat/protocol/HandshakeIntent.kt | 14 - .../kotlin/cn/rtast/libmc/chat/util/uuid.kt | 14 - .../commonTest/kotlin/test/TestChatClient.kt | 22 -- .../kotlin/cn/rtast/libmc/common/buffer.kt | 24 +- .../kotlin/cn/rtast/libmc/common/channels.kt | 6 +- .../kotlin/cn/rtast/libmc/common/codec.kt | 12 +- .../cn/rtast/libmc/common/mc_primitives.kt | 8 +- .../kotlin/cn/rtast/libmc/common/packet.kt | 12 - .../libmc/common/packet/MinecraftPacket.kt | 10 + .../libmc/common/packet/PacketRegistry.kt | 48 ++++ .../libmc/common/packet/UnknownPacket.kt | 31 +++ .../cn/rtast/libmc/common/packet_writer.kt | 20 -- .../kotlin/cn/rtast/libmc/common/socket.kt | 6 +- .../cn/rtast/libmc/common/buffer.jvm.kt | 2 +- .../cn/rtast/libmc/common/channels.jvm.kt | 4 +- .../cn/rtast/libmc/common/socket.jvm.kt | 6 +- .../cn/rtast/libmc/common/buffer.native.kt | 2 +- .../cn/rtast/libmc/common/channels.native.kt | 4 +- .../cn/rtast/libmc/common/socket.native.kt | 8 +- .../cn/rtast/libmc/mcping/bedrock/packet.kt | 10 +- .../libmc/mcping/bedrock/packet_writer.kt | 4 +- .../cn/rtast/libmc/mcping/java/packet.kt | 34 ++- .../cn/rtast/libmc/mcping/java/ping_java.kt | 9 +- .../cn/rtast/libmc/mcping/packet_writer.kt | 23 ++ .../build.gradle.kts | 0 .../kotlin/cn/rtast/libmc/protocol/chat.kt | 146 ++++++++++ .../libmc/protocol}/chat/ChatFilterType.kt | 2 +- .../protocol}/chat/PreviousMessageEntry.kt | 8 +- .../protocol/client/ClientStateMachine.kt | 22 ++ .../libmc/protocol/client/MinecraftClient.kt | 133 +++++++++ .../rtast/libmc/protocol/codec/PacketCodec.kt | 9 + .../libmc/protocol/network/NetworkChannel.kt | 56 ++++ .../ClientboundCookieRequestPacket.kt | 27 ++ .../ClientboundCustomPayloadPacket.kt | 48 ++++ ...lientboundDisconnectConfigurationPacket.kt | 23 ++ .../ClientboundFinishConfigurationPacket.kt | 20 ++ ...ClientboundKeepAliveConfigurationPacket.kt | 24 ++ .../configuration/ClientboundPingPacket.kt | 22 ++ .../ClientboundSelectKnownPacksPacket.kt | 29 ++ .../packet/configuration/KnownPacks.kt | 14 +- ...ServerboundAckFinishConfigurationPacket.kt | 20 ++ .../ServerboundCookieResponsePacket.kt | 60 +++++ ...ServerboundKeepAliveConfigurationPacket.kt | 24 ++ .../configuration/ServerboundPongPacket.kt | 22 ++ .../ServerboundSelectKnownPacksPacket.kt | 29 ++ .../handshake/ServerboundHandshakePacket.kt | 33 +++ .../login/ClientboundDisconnectLoginPacket.kt | 23 ++ .../login/ClientboundLoginSuccessPacket.kt | 26 ++ .../ServerboundLoginAcknowledgedPacket.kt | 19 ++ .../login/ServerboundLoginStartPacket.kt | 26 ++ .../play/ClientboundDisconnectPlayPacket.kt | 23 ++ .../play/ClientboundKeepAlivePlayPacket.kt | 23 ++ .../packet/play/ClientboundLoginPlayPacket.kt | 25 +- .../packet/play/ClientboundPingPlayPacket.kt | 24 ++ .../ClientboundPlayerChatMessagePacket.kt | 25 +- .../ClientboundStartConfigurationPacket.kt | 20 ++ .../play/ServerboundChatMessagePacket.kt | 34 +++ ...verboundConfigurationAcknowledgedPacket.kt | 20 ++ .../play/ServerboundKeepAlivePlayPacket.kt | 23 ++ .../packet/play/ServerboundPongPlayPacket.kt | 23 ++ .../libmc/protocol}/profile/GameProfile.kt | 10 +- .../libmc/protocol/protocol/GameProtocols.kt | 64 +++++ .../protocol/protocol/PacketDirection.kt | 12 + .../libmc/protocol/protocol/game}/BlockPos.kt | 12 +- .../libmc/protocol/protocol/game}/GameMode.kt | 2 +- .../protocol/protocol/game}/Identifier.kt | 12 +- .../protocol/state/HandshakeIntent.kt | 24 ++ .../protocol/protocol/state}/ProtocolState.kt | 7 +- .../protocol/state/ProtocolStateRegistry.kt | 23 ++ .../cn/rtast/libmc/protocol/util/md5.kt | 254 ++++++++++++++++++ .../cn/rtast/libmc/protocol}/util/nbt.kt | 8 +- .../cn/rtast/libmc/protocol/util/uuid.kt | 30 +++ .../src/commonTest/kotlin/test/TestClient.kt | 21 ++ .../rtast/libmc/mcping/rconlib/buffer_ext.kt | 4 +- .../cn/rtast/libmc/mcping/rconlib/packet.kt | 18 +- .../cn/rtast/libmc/mcping/rconlib/rconlib.kt | 14 +- settings.gradle.kts | 2 +- 96 files changed, 1718 insertions(+), 873 deletions(-) delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/PacketDirection.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/AckFinishConfigurationPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundDisconnectConfigurationPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundPingPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundSelectKnownPacksPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/FinishConfigurationPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/KeepAlivePacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundPongPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundSelectKnownPacksPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/handshake/HandshakePacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/ClientboundDisconnectLoginPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginAcknowledgedPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginStartPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginSuccessPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundDisconnectPlayPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundKeepAlivePlayPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundChatMessagePacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundKeepAlivePlayPacket.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/HandshakeIntent.kt delete mode 100644 libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/util/uuid.kt delete mode 100644 libmc-chat/src/commonTest/kotlin/test/TestChatClient.kt delete mode 100644 libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet.kt create mode 100644 libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/MinecraftPacket.kt create mode 100644 libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/PacketRegistry.kt create mode 100644 libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/UnknownPacket.kt delete mode 100644 libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet_writer.kt create mode 100644 libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/packet_writer.kt rename {libmc-chat => libmc-protocol}/build.gradle.kts (100%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol}/chat/ChatFilterType.kt (90%) rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol}/chat/PreviousMessageEntry.kt (80%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/MinecraftClient.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/codec/PacketCodec.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/network/NetworkChannel.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCookieRequestPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCustomPayloadPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundDisconnectConfigurationPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundFinishConfigurationPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundKeepAliveConfigurationPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundPingPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundSelectKnownPacksPacket.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol}/packet/configuration/KnownPacks.kt (68%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundAckFinishConfigurationPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundCookieResponsePacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundKeepAliveConfigurationPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundPongPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundSelectKnownPacksPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/handshake/ServerboundHandshakePacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundDisconnectLoginPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundLoginSuccessPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginAcknowledgedPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginStartPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundDisconnectPlayPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundKeepAlivePlayPacket.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol}/packet/play/ClientboundLoginPlayPacket.kt (79%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundPingPlayPacket.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol}/packet/play/ClientboundPlayerChatMessagePacket.kt (83%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundStartConfigurationPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundChatMessagePacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundConfigurationAcknowledgedPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundKeepAlivePlayPacket.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundPongPlayPacket.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol}/profile/GameProfile.kt (84%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GameProtocols.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/PacketDirection.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game}/BlockPos.kt (76%) rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game}/GameMode.kt (91%) rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game}/Identifier.kt (66%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/HandshakeIntent.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state}/ProtocolState.kt (53%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/ProtocolStateRegistry.kt create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/md5.kt rename {libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat => libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol}/util/nbt.kt (82%) create mode 100644 libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/uuid.kt create mode 100644 libmc-protocol/src/commonTest/kotlin/test/TestClient.kt diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat.kt deleted file mode 100644 index a5b5266..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat.kt +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat - -import cn.rtast.libmc.chat.packet.configuration.AckFinishConfigurationPacket -import cn.rtast.libmc.chat.packet.configuration.ServerboundPongPacket -import cn.rtast.libmc.chat.packet.configuration.ServerboundSelectKnownPacksPacket -import cn.rtast.libmc.chat.packet.handshake.HandshakePacket -import cn.rtast.libmc.chat.packet.login.LoginAcknowledgedPacket -import cn.rtast.libmc.chat.packet.login.LoginStartPacket -import cn.rtast.libmc.chat.packet.play.ServerboundKeepAlivePlayPacket -import cn.rtast.libmc.chat.protocol.HandshakeIntent -import cn.rtast.libmc.chat.protocol.ProtocolState -import cn.rtast.libmc.chat.util.generateOfflineUuid -import cn.rtast.libmc.common.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.currentCoroutineContext -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlin.uuid.Uuid - - -public class MinecraftChatClient( - private val host: String, - private val port: Int, - private val username: String, - private val uuid: Uuid = generateOfflineUuid(username), - private val context: LibMCContext = LibMCContext(), -) { - private var state = ProtocolState.HANDSHAKE - - public suspend fun start(): Unit = coroutineScope { - val socket = _Socket(host, port, context) - val input = socket.openReadChannel() - val output = socket.openWriteChannel() - - executeInitHandshake(output) - - val readerJob = launch(Dispatchers.Default) { - handleIncomingPackets(input, output) - } - - readerJob.join() - } - - private fun executeInitHandshake(output: _WriteChannel) { - val handshakePacket = HandshakePacket(776, host, port.toUShort(), HandshakeIntent.LOGIN) - output.sendPacket(handshakePacket, HandshakePacket) - state = ProtocolState.LOGIN - - val loginStartPacket = LoginStartPacket(username, uuid) - output.sendPacket(loginStartPacket, LoginStartPacket) - } - - private suspend fun handleIncomingPackets(input: _ReadChannel, output: _WriteChannel) { - try { - while (currentCoroutineContext().isActive) { - val packetLength = input.readVarInt() - if (packetLength <= 0) continue - - val packetBytes = ByteArray(packetLength) - input.readFully(packetBytes, 0, packetLength) - - val buffer = _Buffer(packetBytes) - val packetId = buffer.readVarInt() - println("received -> State: $state | ID: 0x${packetId.toString(16).uppercase()} | Length: $packetLength") - try { - when (state) { - ProtocolState.LOGIN -> handleLoginPackets(packetId, output) - ProtocolState.CONFIGURATION -> handleConfigurationPackets(packetId, buffer, output) - ProtocolState.PLAY -> handlePlayPackets(packetId, buffer, output) - else -> {} - } - } catch (e: Exception) { - println("parsing 0x${packetId.toString(16).uppercase()} Payload failed: ${e.message}") - } - } - } catch (e: Exception) { - e.printStackTrace() - println("disconnecting: ${e.message}") - } - } - - private fun handleLoginPackets(packetId: Int, output: _WriteChannel) { - when (packetId) { - 0x02 -> { - output.sendPacket(LoginAcknowledgedPacket(), LoginAcknowledgedPacket) - state = ProtocolState.CONFIGURATION - println("[3/4] sent LoginAcknowledgedPacket -> switching to CONFIGURATION state") - - output.sendPacket( - ServerboundSelectKnownPacksPacket(knownPacks = emptyList()), - ServerboundSelectKnownPacksPacket - ) - } - - 0x00 -> { - println("login denied (ClientboundDisconnectLoginPacket)") - } - } - } - - private fun handleConfigurationPackets(packetId: Int, packetBuffer: _Buffer, output: _WriteChannel) { - when (packetId) { - 0x0E -> { - println("received ClientboundSelectKnownPacksPacket") - } - - 0x03 -> { - output.sendPacket(AckFinishConfigurationPacket, AckFinishConfigurationPacket) - state = ProtocolState.PLAY - } - - 0x05 -> { - output.sendPacket(ServerboundPongPacket(0), ServerboundPongPacket) - } - - 0x01 -> println("configuration state disconnected") - } - } - - private fun handlePlayPackets(packetId: Int, packetBuffer: _Buffer, output: _WriteChannel) { - try { - when (packetId) { - 0x2B -> println("[PLAY] Joined world") - - 0x2c -> { - val keepAliveId = packetBuffer.readLong() - output.sendPacket(ServerboundKeepAlivePlayPacket(id = keepAliveId), ServerboundKeepAlivePlayPacket) - println("[PLAY] reply keep alive packet $keepAliveId") - } - - 0x1D -> println("[PLAY] disconnected (ClientboundDisconnectPlayPacket)") - else -> {} - } - } catch (e: Exception) { - println("parsing 0x${packetId.toString(16).uppercase()} failed, skipped: ${e.message}") - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/PacketDirection.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/PacketDirection.kt deleted file mode 100644 index 9542be3..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/PacketDirection.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet - -internal sealed interface PacketDirection { - interface ServerboundPacket : PacketDirection - interface ClientboundPacket : PacketDirection - interface AcrossPacket : PacketDirection -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/AckFinishConfigurationPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/AckFinishConfigurationPacket.kt deleted file mode 100644 index 568095b..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/AckFinishConfigurationPacket.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data object AckFinishConfigurationPacket : MinecraftPacket, - PacketCodec, - PacketDirection.ServerboundPacket { - override val packetId: Int = 0x03 - - override fun encode(buffer: _Buffer, value: AckFinishConfigurationPacket) {} - - override fun decode(buffer: _Buffer): AckFinishConfigurationPacket = AckFinishConfigurationPacket -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundDisconnectConfigurationPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundDisconnectConfigurationPacket.kt deleted file mode 100644 index 001f781..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundDisconnectConfigurationPacket.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.chat.util.readMinimalTextNbt -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class ClientboundDisconnectConfigurationPacket( - val reason: String, -) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x02 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundDisconnectConfigurationPacket) {} - override fun decode(buffer: _Buffer): ClientboundDisconnectConfigurationPacket { - val reasonText = buffer.readMinimalTextNbt() - return ClientboundDisconnectConfigurationPacket(reason = reasonText) - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundPingPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundPingPacket.kt deleted file mode 100644 index 08d5390..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundPingPacket.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class ClientboundPingPacket(val id: Int) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x51 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundPingPacket) { - buffer.writeInt(value.id) - } - - override fun decode(buffer: _Buffer): ClientboundPingPacket = ClientboundPingPacket(buffer.readInt()) - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundSelectKnownPacksPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundSelectKnownPacksPacket.kt deleted file mode 100644 index 5bb7a3a..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ClientboundSelectKnownPacksPacket.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.* - -internal data class ClientboundSelectKnownPacksPacket( - val knownPacks: List, -) : MinecraftPacket, PacketDirection.AcrossPacket { - override val packetId: Int = 0x0e - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundSelectKnownPacksPacket) { - buffer.writeVarInt(value.knownPacks.size) - value.knownPacks.forEach { KnownPacks.encode(buffer, it) } - } - - override fun decode(buffer: _Buffer): ClientboundSelectKnownPacksPacket { - val packsCount = buffer.readVarInt() - val packs = List(packsCount) { KnownPacks.decode(buffer) } - return ClientboundSelectKnownPacksPacket(packs) - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/FinishConfigurationPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/FinishConfigurationPacket.kt deleted file mode 100644 index d32dff9..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/FinishConfigurationPacket.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data object FinishConfigurationPacket : MinecraftPacket, - PacketCodec, - PacketDirection.ClientboundPacket { - override val packetId: Int = 0x03 - - override fun encode(buffer: _Buffer, value: FinishConfigurationPacket) {} - - override fun decode(buffer: _Buffer): FinishConfigurationPacket = FinishConfigurationPacket -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/KeepAlivePacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/KeepAlivePacket.kt deleted file mode 100644 index f518755..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/KeepAlivePacket.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class KeepAlivePacket(val id: Long) : MinecraftPacket, PacketDirection.AcrossPacket { - override val packetId: Int = 0x04 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: KeepAlivePacket) { - buffer.writeLong(value.id) - } - - override fun decode(buffer: _Buffer): KeepAlivePacket { - val id = buffer.readLong() - return KeepAlivePacket(id) - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundPongPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundPongPacket.kt deleted file mode 100644 index 5085281..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundPongPacket.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class ServerboundPongPacket(val id: Int) : MinecraftPacket, PacketDirection.ServerboundPacket { - override val packetId: Int = 0x2D - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ServerboundPongPacket) { - buffer.writeInt(value.id) - } - - override fun decode(buffer: _Buffer): ServerboundPongPacket = ServerboundPongPacket(buffer.readInt()) - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundSelectKnownPacksPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundSelectKnownPacksPacket.kt deleted file mode 100644 index 73b6f7d..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/ServerboundSelectKnownPacksPacket.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.configuration - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.* - -internal data class ServerboundSelectKnownPacksPacket( - val knownPacks: List, -) : MinecraftPacket, PacketDirection.AcrossPacket { - override val packetId: Int = 0x07 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ServerboundSelectKnownPacksPacket) { - buffer.writeVarInt(value.knownPacks.size) - value.knownPacks.forEach { KnownPacks.encode(buffer, it) } - } - - override fun decode(buffer: _Buffer): ServerboundSelectKnownPacksPacket { - val packsCount = buffer.readVarInt() - val packs = List(packsCount) { KnownPacks.decode(buffer) } - return ServerboundSelectKnownPacksPacket(packs) - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/handshake/HandshakePacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/handshake/HandshakePacket.kt deleted file mode 100644 index 84e3339..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/handshake/HandshakePacket.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.handshake - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer -import cn.rtast.libmc.common.writeMcString -import cn.rtast.libmc.common.writeVarInt - -internal data class HandshakePacket( - val protocolVersion: Int, - val serverAddress: String, - val serverPort: UShort, - // 1 for Status, 2 for Login, 3 for Transfer - val intent: Int -) : MinecraftPacket, PacketDirection.ServerboundPacket { - override val packetId: Int = 0x00 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: HandshakePacket) { - buffer.writeVarInt(value.protocolVersion) - buffer.writeMcString(value.serverAddress) - buffer.writeShort(value.serverPort.toShort()) - buffer.writeVarInt(value.intent) - } - - override fun decode(buffer: _Buffer): HandshakePacket = throw UnsupportedOperationException() - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/ClientboundDisconnectLoginPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/ClientboundDisconnectLoginPacket.kt deleted file mode 100644 index 60b714b..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/ClientboundDisconnectLoginPacket.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.login - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer -import cn.rtast.libmc.common.readMcString - -internal data class ClientboundDisconnectLoginPacket( - val reason: String, -) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x00 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundDisconnectLoginPacket) {} - override fun decode(buffer: _Buffer): ClientboundDisconnectLoginPacket { - val reasonJson = buffer.readMcString() - return ClientboundDisconnectLoginPacket(reason = reasonJson) - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginAcknowledgedPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginAcknowledgedPacket.kt deleted file mode 100644 index 24f9402..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginAcknowledgedPacket.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.login - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class LoginAcknowledgedPacket( - override val packetId: Int = 0x03, -) : MinecraftPacket, PacketDirection.ServerboundPacket { - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: LoginAcknowledgedPacket) {} - override fun decode(buffer: _Buffer): LoginAcknowledgedPacket = throw UnsupportedOperationException() - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginStartPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginStartPacket.kt deleted file mode 100644 index f7dd3fe..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginStartPacket.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.login - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer -import cn.rtast.libmc.common.writeMcString -import cn.rtast.libmc.common.writeUuid -import kotlin.uuid.Uuid - -internal data class LoginStartPacket( - val username: String, - val playerUuid: Uuid, -) : MinecraftPacket, PacketDirection.ServerboundPacket { - override val packetId: Int = 0x00 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: LoginStartPacket) { - buffer.writeMcString(value.username) - buffer.writeUuid(value.playerUuid) - } - - override fun decode(buffer: _Buffer): LoginStartPacket = throw UnsupportedOperationException() - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginSuccessPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginSuccessPacket.kt deleted file mode 100644 index 0ff7ac1..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/login/LoginSuccessPacket.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.login - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.chat.profile.GameProfile -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer -import cn.rtast.libmc.common.readUuid -import kotlin.uuid.Uuid - -internal data class LoginSuccessPacket( - val gameProfile: GameProfile, - val sessionId: Uuid, -) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x02 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: LoginSuccessPacket) {} - override fun decode(buffer: _Buffer): LoginSuccessPacket { - val gameProfile = GameProfile.decode(buffer) - val sessionId = buffer.readUuid() - return LoginSuccessPacket(gameProfile, sessionId) - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundDisconnectPlayPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundDisconnectPlayPacket.kt deleted file mode 100644 index dea1032..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundDisconnectPlayPacket.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.play - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.chat.util.readMinimalTextNbt -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class ClientboundDisconnectPlayPacket( - val reason: String, -) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x28 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundDisconnectPlayPacket) {} - override fun decode(buffer: _Buffer): ClientboundDisconnectPlayPacket { - val reasonText = buffer.readMinimalTextNbt() - return ClientboundDisconnectPlayPacket(reason = reasonText) - } - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundKeepAlivePlayPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundKeepAlivePlayPacket.kt deleted file mode 100644 index 08b4072..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundKeepAlivePlayPacket.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.play - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class ClientboundKeepAlivePlayPacket(val id: Long) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x33 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundKeepAlivePlayPacket) { - buffer.writeLong(value.id) - } - - override fun decode(buffer: _Buffer): ClientboundKeepAlivePlayPacket = - ClientboundKeepAlivePlayPacket(buffer.readLong()) - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundChatMessagePacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundChatMessagePacket.kt deleted file mode 100644 index 195c886..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundChatMessagePacket.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.play - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.* -import kotlin.time.Clock - -internal data class ServerboundChatMessagePacket( - val message: String, - val timestamp: Long = Clock.System.now().toEpochMilliseconds(), - val salt: Long = 0L, -) : MinecraftPacket, PacketDirection.ServerboundPacket { - override val packetId: Int = 0x09 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ServerboundChatMessagePacket) { - buffer.writeMcString(value.message) - buffer.writeLong(value.timestamp) - buffer.writeLong(value.salt) - buffer.writeBoolean(false) // has signature - buffer.writeVarInt(0) // message count - buffer.writeBytes(byteArrayOf(0, 0, 0)) - } - - override fun decode(buffer: _Buffer): ServerboundChatMessagePacket = throw UnsupportedOperationException() - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundKeepAlivePlayPacket.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundKeepAlivePlayPacket.kt deleted file mode 100644 index 942b63d..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ServerboundKeepAlivePlayPacket.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.packet.play - -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.common.MinecraftPacket -import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer - -internal data class ServerboundKeepAlivePlayPacket(val id: Long) : MinecraftPacket, PacketDirection.ServerboundPacket { - override val packetId: Int = 0x1C - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ServerboundKeepAlivePlayPacket) { - buffer.writeLong(value.id) - } - - override fun decode(buffer: _Buffer): ServerboundKeepAlivePlayPacket = - ServerboundKeepAlivePlayPacket(buffer.readLong()) - } -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/HandshakeIntent.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/HandshakeIntent.kt deleted file mode 100644 index 4515b75..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/HandshakeIntent.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.protocol - -internal object HandshakeIntent { - const val STATUS = 1 - const val LOGIN = 2 - const val TRANSFER = 3 -} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/util/uuid.kt b/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/util/uuid.kt deleted file mode 100644 index 2d0a7f7..0000000 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/util/uuid.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.chat.util - -import kotlin.uuid.Uuid - - -public fun generateOfflineUuid(username: String): Uuid = - Uuid.fromByteArray("OfflinePlayer:$username".encodeToByteArray()) \ No newline at end of file diff --git a/libmc-chat/src/commonTest/kotlin/test/TestChatClient.kt b/libmc-chat/src/commonTest/kotlin/test/TestChatClient.kt deleted file mode 100644 index 8a4d3f9..0000000 --- a/libmc-chat/src/commonTest/kotlin/test/TestChatClient.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package test - -import cn.rtast.libmc.chat.MinecraftChatClient -import kotlinx.coroutines.test.runTest -import kotlin.test.Test -import kotlin.uuid.Uuid - -class TestChatClient { - - @Test - fun `test chat client`() = runTest { - val cli = MinecraftChatClient("127.0.0.1", 25565, "RTAkland", Uuid.parse("0dc6a9e9-a6df-3f3e-ae07-e6dbdf74b294")) - cli.start() - } -} \ No newline at end of file diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/buffer.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/buffer.kt index 3aad6ac..27079b7 100644 --- a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/buffer.kt +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/buffer.kt @@ -10,7 +10,7 @@ package cn.rtast.libmc.common import kotlin.uuid.Uuid @Suppress("CLASSNAME") -public expect class _Buffer { +public expect class BytesBuffer { public constructor() public constructor(bytes: ByteArray) @@ -35,29 +35,27 @@ public expect class _Buffer { public val remaining: Long } -public fun ByteArray.wrap(): _Buffer = _Buffer(this) +@Suppress("NOTHING_TO_INLINE") +public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this) -public fun _Buffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits -> +public fun BytesBuffer.writeUuid(uuid: Uuid): Unit = uuid.toLongs { mostSignificantBits, leastSignificantBits -> this.writeLong(mostSignificantBits) this.writeLong(leastSignificantBits) } -public fun _Buffer.readUuid(): Uuid { +public fun BytesBuffer.readUuid(): Uuid { val most = this.readLong() val least = this.readLong() return Uuid.fromLongs(most, least) } -public fun _Buffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value) -public fun _Buffer.readVarInt(): Int = VarIntCodec.decode(this) +public fun BytesBuffer.writeVarInt(value: Int): Unit = VarIntCodec.encode(this, value) +public fun BytesBuffer.readVarInt(): Int = VarIntCodec.decode(this) -public fun _Buffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value) -public fun _Buffer.readMcString(): String = McStringCodec.decode(this) +public fun BytesBuffer.writeMcString(value: String): Unit = McStringCodec.encode(this, value) +public fun BytesBuffer.readMcString(): String = McStringCodec.decode(this) -public fun _ReadChannel.readPacketFrame(): _Buffer { +public fun ReadChannel.readPacketFrame(): BytesBuffer { val length = this.readVarInt() - val frameBytes = this.readBytes(length) - return _Buffer().apply { - writeBytes(frameBytes) - } + return this.readBytes(length).wrap() } \ No newline at end of file diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/channels.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/channels.kt index ee057d9..b65b4c3 100644 --- a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/channels.kt +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/channels.kt @@ -8,7 +8,7 @@ package cn.rtast.libmc.common @Suppress("CLASSNAME") -public expect class _ReadChannel { +public expect class ReadChannel { public fun readByte(): Byte public fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short public fun readInt(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Int @@ -18,12 +18,12 @@ public expect class _ReadChannel { } @Suppress("CLASSNAME") -public expect class _WriteChannel { +public expect class WriteChannel { public fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size) public fun flush() } -public fun _ReadChannel.readVarInt(): Int { +public fun ReadChannel.readVarInt(): Int { var numRead = 0 var result = 0 var read: Byte diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/codec.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/codec.kt index 66e2546..b2cef5c 100644 --- a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/codec.kt +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/codec.kt @@ -8,17 +8,17 @@ package cn.rtast.libmc.common public interface Encoder { - public fun encode(buffer: _Buffer, value: T) + public fun encode(buffer: BytesBuffer, value: T) public fun encodeToByteArray(value: T): ByteArray { - val buf = _Buffer() + val buf = BytesBuffer() encode(buf, value) return buf.toByteArray() } } public interface Decoder { - public fun decode(buffer: _Buffer): T + public fun decode(buffer: BytesBuffer): T public fun decodeFromByteArray(bytes: ByteArray): T = decode(bytes.wrap()) } @@ -26,12 +26,12 @@ public interface Decoder { public interface PacketCodec : Encoder, Decoder @Suppress("NOTHING_TO_INLINE") -public inline fun _Buffer.write(value: T, encoder: Encoder): Unit = encoder.encode(this, value) +public inline fun BytesBuffer.write(value: T, encoder: Encoder): Unit = encoder.encode(this, value) @Suppress("NOTHING_TO_INLINE") -public inline fun _Buffer.read(decoder: Decoder): T = decoder.decode(this) +public inline fun BytesBuffer.read(decoder: Decoder): T = decoder.decode(this) -public fun _Buffer.writeBuffer(source: _Buffer, length: Long = source.remaining) { +public fun BytesBuffer.writeBuffer(source: BytesBuffer, length: Long = source.remaining) { if (length <= 0) return val bytes = source.readBytes(length.toInt()) this.writeBytes(bytes) diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/mc_primitives.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/mc_primitives.kt index ec3c1ec..9a27168 100644 --- a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/mc_primitives.kt +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/mc_primitives.kt @@ -7,7 +7,7 @@ package cn.rtast.libmc.common public object VarIntCodec : PacketCodec { - override fun encode(buffer: _Buffer, value: Int) { + override fun encode(buffer: BytesBuffer, value: Int) { var v = value while (true) { if ((v and 0x7F.inv()) == 0) { @@ -19,7 +19,7 @@ public object VarIntCodec : PacketCodec { } } - override fun decode(buffer: _Buffer): Int { + override fun decode(buffer: BytesBuffer): Int { var numRead = 0 var result = 0 var read: Byte @@ -35,13 +35,13 @@ public object VarIntCodec : PacketCodec { } public object McStringCodec : PacketCodec { - override fun encode(buffer: _Buffer, value: String) { + override fun encode(buffer: BytesBuffer, value: String) { val bytes = value.encodeToByteArray() VarIntCodec.encode(buffer, bytes.size) buffer.writeBytes(bytes) } - override fun decode(buffer: _Buffer): String { + override fun decode(buffer: BytesBuffer): String { val length = VarIntCodec.decode(buffer) val bytes = buffer.readBytes(length) return bytes.decodeToString() diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet.kt deleted file mode 100644 index b90d219..0000000 --- a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.common - -public interface MinecraftPacket { - public val packetId: Int -} \ No newline at end of file diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/MinecraftPacket.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/MinecraftPacket.kt new file mode 100644 index 0000000..6f3b56f --- /dev/null +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/MinecraftPacket.kt @@ -0,0 +1,10 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.common.packet + +public interface MinecraftPacket \ No newline at end of file diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/PacketRegistry.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/PacketRegistry.kt new file mode 100644 index 0000000..a6c3530 --- /dev/null +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/PacketRegistry.kt @@ -0,0 +1,48 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.common.packet + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.writeVarInt +import kotlin.reflect.KClass + +public class PacketRegistry { + private val idToCodec = mutableMapOf>() + private val classToInfo = mutableMapOf, RegisteredPacket<*>>() + + private data class RegisteredPacket

( + val id: Int, val codec: PacketCodec

, + ) + + public fun register(id: Int, kClass: KClass, codec: PacketCodec) { + idToCodec[id] = codec + classToInfo[kClass] = RegisteredPacket(id, codec) + } + + public inline fun register(id: Int, codec: PacketCodec) { + register(id, T::class, codec) + } + + public fun decodePacket(packetId: Int, buffer: BytesBuffer): MinecraftPacket { + val codec = idToCodec[packetId] + if (codec != null) return codec.decode(buffer) else { +// println("Ignored unknown packet 0x${packetId.toString(16).uppercase()}") + return UnknownPacket(packetId, buffer.readBytes(buffer.remaining.toInt())) + } + } + + public fun encodePacket(buffer: BytesBuffer, packet: T) { + @Suppress("UNCHECKED_CAST") + val info = requireNotNull(classToInfo[packet::class]) { + "Unregistered Packet ${packet::class.simpleName}" + } as RegisteredPacket + buffer.writeVarInt(info.id) + info.codec.encode(buffer, packet) + } +} \ No newline at end of file diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/UnknownPacket.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/UnknownPacket.kt new file mode 100644 index 0000000..7a026e2 --- /dev/null +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet/UnknownPacket.kt @@ -0,0 +1,31 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.common.packet + +/** + * reserved packet + */ +public data class UnknownPacket(val packetId: Int, val data: ByteArray): MinecraftPacket { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as UnknownPacket + + if (packetId != other.packetId) return false + if (!data.contentEquals(other.data)) return false + + return true + } + + override fun hashCode(): Int { + var result = packetId + result = 31 * result + data.contentHashCode() + return result + } +} \ No newline at end of file diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet_writer.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet_writer.kt deleted file mode 100644 index 4387fa4..0000000 --- a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/packet_writer.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright © 2026 RTAkland - * Author: RTAkland - * Date: 2026/9/4 - */ - - -package cn.rtast.libmc.common - -public fun _WriteChannel.sendPacket(packet: T, codec: PacketCodec) { - val bodyBuffer = _Buffer() - bodyBuffer.write(packet.packetId, VarIntCodec) - codec.encode(bodyBuffer, packet) - val frameBuffer = _Buffer() - frameBuffer.write(bodyBuffer.size, VarIntCodec) - frameBuffer.writeBuffer(bodyBuffer) - val bytes = frameBuffer.toByteArray() - this.writeFully(bytes, 0, bytes.size) - this.flush() -} \ No newline at end of file diff --git a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/socket.kt b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/socket.kt index eda5ecc..ae2cf12 100644 --- a/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/socket.kt +++ b/libmc-common/src/commonMain/kotlin/cn/rtast/libmc/common/socket.kt @@ -8,9 +8,9 @@ package cn.rtast.libmc.common @Suppress("CLASSNAME") -public expect class _Socket public constructor(host: String, port: Int, context: LibMCContext) { - public fun openReadChannel(): _ReadChannel - public fun openWriteChannel(): _WriteChannel +public expect class Socket public constructor(host: String, port: Int, context: LibMCContext) { + public fun openReadChannel(): ReadChannel + public fun openWriteChannel(): WriteChannel public fun close() } diff --git a/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/buffer.jvm.kt b/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/buffer.jvm.kt index 1677ac6..594a296 100644 --- a/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/buffer.jvm.kt +++ b/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/buffer.jvm.kt @@ -9,7 +9,7 @@ package cn.rtast.libmc.common import java.io.ByteArrayOutputStream @Suppress("CLASSNAME") -public actual class _Buffer { +public actual class BytesBuffer { private val outStream = ByteArrayOutputStream() private var readBuffer: ByteArray? = null private var readOffset = 0 diff --git a/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/channels.jvm.kt b/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/channels.jvm.kt index 8c932c4..cb16681 100644 --- a/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/channels.jvm.kt +++ b/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/channels.jvm.kt @@ -12,7 +12,7 @@ import java.io.InputStream import java.io.OutputStream @Suppress("CLASSNAME") -public actual class _ReadChannel(private val _inputStream: InputStream) { +public actual class ReadChannel(private val _inputStream: InputStream) { public actual fun readByte(): Byte = _inputStream.read().toByte() public actual fun readBytes(length: Int): ByteArray = _inputStream.readNBytes(length) @@ -33,7 +33,7 @@ public actual class _ReadChannel(private val _inputStream: InputStream) { } @Suppress("CLASSNAME") -public actual class _WriteChannel { +public actual class WriteChannel { private val _outputStream: OutputStream public constructor(outputStream: OutputStream) { diff --git a/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/socket.jvm.kt b/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/socket.jvm.kt index 286ff44..8172de1 100644 --- a/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/socket.jvm.kt +++ b/libmc-common/src/jvmMain/kotlin/cn/rtast/libmc/common/socket.jvm.kt @@ -12,11 +12,11 @@ import java.net.InetSocketAddress import java.net.Socket as JvmSocket @Suppress("CLASSNAME") -public actual class _Socket public actual constructor(host: String, port: Int, context: LibMCContext) { +public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) { private val socket = JvmSocket(host, port) - public actual fun openReadChannel(): _ReadChannel = _ReadChannel(socket.getInputStream()) - public actual fun openWriteChannel(): _WriteChannel = _WriteChannel(socket.getOutputStream()) + public actual fun openReadChannel(): ReadChannel = ReadChannel(socket.getInputStream()) + public actual fun openWriteChannel(): WriteChannel = WriteChannel(socket.getOutputStream()) public actual fun close(): Unit = socket.close() } diff --git a/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/buffer.native.kt b/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/buffer.native.kt index 695723a..fb0ae73 100644 --- a/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/buffer.native.kt +++ b/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/buffer.native.kt @@ -12,7 +12,7 @@ import kotlinx.io.Buffer import kotlinx.io.readByteArray @Suppress("CLASSNAME") -public actual class _Buffer { +public actual class BytesBuffer { private val _delegateBuf: Buffer public actual constructor() { diff --git a/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/channels.native.kt b/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/channels.native.kt index d5902f6..65b78f2 100644 --- a/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/channels.native.kt +++ b/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/channels.native.kt @@ -11,7 +11,7 @@ import io.ktor.utils.io.bits.reverseByteOrder import kotlinx.coroutines.runBlocking @Suppress("CLASSNAME") -public actual class _ReadChannel(private val _readChannel: ByteReadChannel) { +public actual class ReadChannel(private val _readChannel: ByteReadChannel) { public actual fun readByte(): Byte = runBlocking { _readChannel.readByte() } public actual fun readBytes(length: Int): ByteArray = runBlocking { _readChannel.readByteArray(length) } @@ -35,7 +35,7 @@ public actual class _ReadChannel(private val _readChannel: ByteReadChannel) { } @Suppress("CLASSNAME") -public actual class _WriteChannel { +public actual class WriteChannel { private val _writeChannel: ByteWriteChannel public constructor(writeChannel: ByteWriteChannel) { diff --git a/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/socket.native.kt b/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/socket.native.kt index ff52576..1375b72 100644 --- a/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/socket.native.kt +++ b/libmc-common/src/nativeMain/kotlin/cn/rtast/libmc/common/socket.native.kt @@ -12,13 +12,13 @@ import kotlinx.coroutines.runBlocking import kotlinx.io.readByteArray @Suppress("CLASSNAME") -public actual class _Socket public actual constructor(host: String, port: Int, context: LibMCContext) { +public actual class Socket public actual constructor(host: String, port: Int, context: LibMCContext) { private val ctx = context private val socket = runBlocking { aSocket(ctx._selectorManager).tcp().connect(host, port) } - public actual fun openReadChannel(): _ReadChannel = _ReadChannel(socket.openReadChannel()) - public actual fun openWriteChannel(): _WriteChannel = - _WriteChannel(socket.openWriteChannel(autoFlush = true)) + public actual fun openReadChannel(): ReadChannel = ReadChannel(socket.openReadChannel()) + public actual fun openWriteChannel(): WriteChannel = + WriteChannel(socket.openWriteChannel(autoFlush = true)) public actual fun close() { socket.close() diff --git a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet.kt b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet.kt index ccf186a..0bc3420 100644 --- a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet.kt +++ b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet.kt @@ -8,7 +8,7 @@ package cn.rtast.libmc.mcping.bedrock import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer import kotlin.random.Random private val RAKNET_MAGIC = byteArrayOf( @@ -33,14 +33,14 @@ internal data class BedrockRequestPacket( override val packetId: Byte = 0x01 companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: BedrockRequestPacket) { + override fun encode(buffer: BytesBuffer, value: BedrockRequestPacket) { buffer.writeByte(value.packetId) buffer.writeLong(value.time) buffer.writeBytes(value.magic) buffer.writeLong(value.guid) } - override fun decode(buffer: _Buffer): BedrockRequestPacket = throw UnsupportedOperationException() + override fun decode(buffer: BytesBuffer): BedrockRequestPacket = throw UnsupportedOperationException() } override fun equals(other: Any?): Boolean { @@ -72,8 +72,8 @@ internal data class BedrockResponsePacket( ) : MinecraftBedrockPacket { companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: BedrockResponsePacket) = throw UnsupportedOperationException() - override fun decode(buffer: _Buffer): BedrockResponsePacket { + override fun encode(buffer: BytesBuffer, value: BedrockResponsePacket) = throw UnsupportedOperationException() + override fun decode(buffer: BytesBuffer): BedrockResponsePacket { val packetId = buffer.readByte() if (packetId != 0x1C.toByte()) throw IllegalStateException("Expected pong id 0x1C, got $packetId") val time = buffer.readLong() diff --git a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet_writer.kt b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet_writer.kt index 224bde5..672fad7 100644 --- a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet_writer.kt +++ b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/bedrock/packet_writer.kt @@ -8,12 +8,12 @@ package cn.rtast.libmc.mcping.bedrock import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common._UdpSocket internal fun _UdpSocket.sendPacket(packet: T, codec: PacketCodec): ByteArray { - val buf = _Buffer() + val buf = BytesBuffer() codec.encode(buf, packet) return sendAndReceive(buf.toByteArray()) } \ No newline at end of file diff --git a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/packet.kt b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/packet.kt index 8d8c0c7..0dccbb4 100644 --- a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/packet.kt +++ b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/packet.kt @@ -7,47 +7,45 @@ package cn.rtast.libmc.mcping.java -import cn.rtast.libmc.common.* +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.McStringCodec +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.VarIntCodec +import cn.rtast.libmc.common.packet.MinecraftPacket // ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Handshake -public data class HandshakePacket( +internal data class HandshakePacket( val protocolVersion: Int, val serverAddress: String, val serverPort: UShort, // 1 -> Status, 2 -> Login val nextState: Int, ) : MinecraftPacket { - override val packetId: Int = 0x00 - - public companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: HandshakePacket) { + companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: HandshakePacket) { VarIntCodec.encode(buffer, value.protocolVersion) McStringCodec.encode(buffer, value.serverAddress) buffer.writeShort(value.serverPort.toShort()) VarIntCodec.encode(buffer, value.nextState) } - override fun decode(buffer: _Buffer): HandshakePacket = throw UnsupportedOperationException() + override fun decode(buffer: BytesBuffer): HandshakePacket = throw UnsupportedOperationException() } } // ref https://minecraft.wiki/w/Java_Edition_protocol/Packets#Status -public data object StatusRequestPacket : MinecraftPacket, PacketCodec { - override val packetId: Int = 0x00 - - override fun encode(buffer: _Buffer, value: StatusRequestPacket) {} - override fun decode(buffer: _Buffer): StatusRequestPacket = throw UnsupportedOperationException() +internal data object StatusRequestPacket : MinecraftPacket, PacketCodec { + override fun encode(buffer: BytesBuffer, value: StatusRequestPacket) {} + override fun decode(buffer: BytesBuffer): StatusRequestPacket = throw UnsupportedOperationException() } -public data class PingPacket(val currentTime: Long) : MinecraftPacket { - override val packetId: Int = 0x01 - - public companion object : PacketCodec { - override fun encode(buffer: _Buffer, value: PingPacket) { +internal data class PingPacket(val currentTime: Long) : MinecraftPacket { + companion object : PacketCodec { + override fun encode(buffer: BytesBuffer, value: PingPacket) { buffer.writeLong(value.currentTime) } - override fun decode(buffer: _Buffer): PingPacket { + override fun decode(buffer: BytesBuffer): PingPacket { return PingPacket(buffer.readLong()) } } diff --git a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/ping_java.kt b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/ping_java.kt index 3e11d1c..bb15d77 100644 --- a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/ping_java.kt +++ b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/java/ping_java.kt @@ -9,10 +9,11 @@ package cn.rtast.libmc.mcping.java import cn.rtast.libmc.common.* import cn.rtast.libmc.mcping.PingResponse +import cn.rtast.libmc.mcping.sendPacket import kotlin.time.Clock internal fun pingJavaServer(host: String, port: Int, context: LibMCContext): PingResponse { - val socket = _Socket(host, port, context) + val socket = Socket(host, port, context) val receiveChannel = socket.openReadChannel() val sendChannel = socket.openWriteChannel() @@ -23,8 +24,8 @@ internal fun pingJavaServer(host: String, port: Int, context: LibMCContext): Pin serverPort = port.toUShort(), nextState = 1 ) - sendChannel.sendPacket(handshakePacket, HandshakePacket) - sendChannel.sendPacket(StatusRequestPacket, StatusRequestPacket) + sendChannel.sendPacket(handshakePacket, 0x00, HandshakePacket) + sendChannel.sendPacket(StatusRequestPacket, 0x00, StatusRequestPacket) val statusFrameBuffer = receiveChannel.readPacketFrame() val statusPacketId = VarIntCodec.decode(statusFrameBuffer) @@ -35,7 +36,7 @@ internal fun pingJavaServer(host: String, port: Int, context: LibMCContext): Pin val sendTime = Clock.System.now().toEpochMilliseconds() val pingPacket = PingPacket(sendTime) - sendChannel.sendPacket(pingPacket, PingPacket) + sendChannel.sendPacket(pingPacket, 0x01, PingPacket) val pongFrameBuffer = receiveChannel.readPacketFrame() val pongPacketId = VarIntCodec.decode(pongFrameBuffer) diff --git a/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/packet_writer.kt b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/packet_writer.kt new file mode 100644 index 0000000..317baa5 --- /dev/null +++ b/libmc-mcping/src/commonMain/kotlin/cn/rtast/libmc/mcping/packet_writer.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.mcping + +import cn.rtast.libmc.common.* +import cn.rtast.libmc.common.packet.MinecraftPacket + +public fun WriteChannel.sendPacket(packet: T, packetId: Int, codec: PacketCodec) { + val bodyBuffer = BytesBuffer() + bodyBuffer.write(packetId, VarIntCodec) + codec.encode(bodyBuffer, packet) + val frameBuffer = BytesBuffer() + frameBuffer.write(bodyBuffer.size, VarIntCodec) + frameBuffer.writeBuffer(bodyBuffer) + val bytes = frameBuffer.toByteArray() + this.writeFully(bytes, 0, bytes.size) + this.flush() +} \ No newline at end of file diff --git a/libmc-chat/build.gradle.kts b/libmc-protocol/build.gradle.kts similarity index 100% rename from libmc-chat/build.gradle.kts rename to libmc-protocol/build.gradle.kts diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat.kt new file mode 100644 index 0000000..077df18 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat.kt @@ -0,0 +1,146 @@ +///* +// * Copyright © 2026 RTAkland +// * Author: RTAkland +// * Date: 2026/9/4 +// */ +// +// +//package cn.rtast.libmc.protocol +// +//import cn.rtast.libmc.protocol.packet.configuration.ServerboundAckFinishConfigurationPacket +//import cn.rtast.libmc.protocol.packet.configuration.ServerboundPongPacket +//import cn.rtast.libmc.protocol.packet.configuration.ServerboundSelectKnownPacksPacket +//import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket +//import cn.rtast.libmc.protocol.packet.login.ServerboundLoginAcknowledgedPacket +//import cn.rtast.libmc.protocol.packet.login.ServerboundLoginStartPacket +//import cn.rtast.libmc.protocol.packet.play.ServerboundKeepAlivePlayPacket +//import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent +//import cn.rtast.libmc.protocol.protocol.state.ProtocolState +//import cn.rtast.libmc.protocol.util.generateOfflineUuid +//import cn.rtast.libmc.common.* +//import kotlinx.coroutines.Dispatchers +//import kotlinx.coroutines.coroutineScope +//import kotlinx.coroutines.currentCoroutineContext +//import kotlinx.coroutines.isActive +//import kotlinx.coroutines.launch +//import kotlin.uuid.Uuid +// +// +//public class MinecraftChatClient( +// private val host: String, +// private val port: Int, +// private val username: String, +// private val uuid: Uuid = generateOfflineUuid(username), +// private val context: LibMCContext = LibMCContext(), +//) { +// private var state = ProtocolState.HANDSHAKE +// +// public suspend fun start(): Unit = coroutineScope { +// val socket = Socket(host, port, context) +// val input = socket.openReadChannel() +// val output = socket.openWriteChannel() +// +// executeInitHandshake(output) +// +// val readerJob = launch(Dispatchers.Default) { +// handleIncomingPackets(input, output) +// } +// +// readerJob.join() +// } +// +// private fun executeInitHandshake(output: WriteChannel) { +// val handshakePacket = ServerboundHandshakePacket(776, host, port.toUShort(), HandshakeIntent.LOGIN) +// output.sendPacket(handshakePacket, ServerboundHandshakePacket) +// state = ProtocolState.LOGIN +// +// val loginStartPacket = ServerboundLoginStartPacket(username, uuid) +// output.sendPacket(loginStartPacket, ServerboundLoginStartPacket) +// } +// +// private suspend fun handleIncomingPackets(input: ReadChannel, output: WriteChannel) { +// try { +// while (currentCoroutineContext().isActive) { +// val packetLength = input.readVarInt() +// if (packetLength <= 0) continue +// +// val packetBytes = ByteArray(packetLength) +// input.readFully(packetBytes, 0, packetLength) +// +// val buffer = BytesBuffer(packetBytes) +// val packetId = buffer.readVarInt() +// println("received -> State: $state | ID: 0x${packetId.toString(16).uppercase()} | Length: $packetLength") +// try { +// when (state) { +// ProtocolState.LOGIN -> handleLoginPackets(packetId, output) +// ProtocolState.CONFIGURATION -> handleConfigurationPackets(packetId, buffer, output) +// ProtocolState.PLAY -> handlePlayPackets(packetId, buffer, output) +// else -> {} +// } +// } catch (e: Exception) { +// println("parsing 0x${packetId.toString(16).uppercase()} Payload failed: ${e.message}") +// } +// } +// } catch (e: Exception) { +// e.printStackTrace() +// println("disconnecting: ${e.message}") +// } +// } +// +// private fun handleLoginPackets(packetId: Int, output: WriteChannel) { +// when (packetId) { +// 0x02 -> { +// output.sendPacket(ServerboundLoginAcknowledgedPacket(), ServerboundLoginAcknowledgedPacket) +// state = ProtocolState.CONFIGURATION +// println("[3/4] sent LoginAcknowledgedPacket -> switching to CONFIGURATION state") +// +// output.sendPacket( +// ServerboundSelectKnownPacksPacket(knownPacks = emptyList()), +// ServerboundSelectKnownPacksPacket +// ) +// } +// +// 0x00 -> { +// println("login denied (ClientboundDisconnectLoginPacket)") +// } +// } +// } +// +// private fun handleConfigurationPackets(packetId: Int, packetBuffer: BytesBuffer, output: WriteChannel) { +// when (packetId) { +// 0x0E -> { +// println("received ClientboundSelectKnownPacksPacket") +// } +// +// 0x03 -> { +// output.sendPacket(ServerboundAckFinishConfigurationPacket, ServerboundAckFinishConfigurationPacket) +// state = ProtocolState.PLAY +// } +// +// 0x05 -> { +// output.sendPacket(ServerboundPongPacket(0), ServerboundPongPacket) +// } +// +// 0x01 -> println("configuration state disconnected") +// } +// } +// +// private fun handlePlayPackets(packetId: Int, packetBuffer: BytesBuffer, output: WriteChannel) { +// try { +// when (packetId) { +// 0x2B -> println("[PLAY] Joined world") +// +// 0x2c -> { +// val keepAliveId = packetBuffer.readLong() +// output.sendPacket(ServerboundKeepAlivePlayPacket(id = keepAliveId), ServerboundKeepAlivePlayPacket) +// println("[PLAY] reply keep alive packet $keepAliveId") +// } +// +// 0x1D -> println("[PLAY] disconnected (ClientboundDisconnectPlayPacket)") +// else -> {} +// } +// } catch (e: Exception) { +// println("parsing 0x${packetId.toString(16).uppercase()} failed, skipped: ${e.message}") +// } +// } +//} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat/ChatFilterType.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat/ChatFilterType.kt similarity index 90% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat/ChatFilterType.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat/ChatFilterType.kt index f2c8e44..37dafc3 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat/ChatFilterType.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat/ChatFilterType.kt @@ -5,7 +5,7 @@ */ -package cn.rtast.libmc.chat.chat +package cn.rtast.libmc.protocol.chat public enum class ChatFilterType(public val id: Int) { PASS_THROUGH(0), diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat/PreviousMessageEntry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat/PreviousMessageEntry.kt similarity index 80% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat/PreviousMessageEntry.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat/PreviousMessageEntry.kt index 7314492..cb6b87d 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/chat/PreviousMessageEntry.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/chat/PreviousMessageEntry.kt @@ -5,10 +5,10 @@ */ -package cn.rtast.libmc.chat.chat +package cn.rtast.libmc.protocol.chat import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.writeVarInt public data class PreviousMessageEntry( @@ -16,7 +16,7 @@ public data class PreviousMessageEntry( val signature: ByteArray?, ) { public companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: PreviousMessageEntry) { + override fun encode(buffer: BytesBuffer, value: PreviousMessageEntry) { buffer.writeVarInt(value.messageId) if (value.messageId == 0) { val sig = requireNotNull(value.signature) { "signature must be present when messageId is 0" } @@ -25,7 +25,7 @@ public data class PreviousMessageEntry( } } - override fun decode(buffer: _Buffer): PreviousMessageEntry = throw UnsupportedOperationException() // TODO + override fun decode(buffer: BytesBuffer): PreviousMessageEntry = throw UnsupportedOperationException() // TODO } override fun equals(other: Any?): Boolean { diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt new file mode 100644 index 0000000..de9ab95 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/ClientStateMachine.kt @@ -0,0 +1,22 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.client + +import cn.rtast.libmc.protocol.protocol.state.ProtocolState +import kotlin.concurrent.Volatile + +internal class ClientStateMachine { + @Volatile + var currentState: ProtocolState = ProtocolState.HANDSHAKE + private set + + fun transitionTo(newState: ProtocolState) { + println("Changing State $currentState to $newState") + currentState = newState + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/MinecraftClient.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/MinecraftClient.kt new file mode 100644 index 0000000..5081fb0 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/client/MinecraftClient.kt @@ -0,0 +1,133 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + +package cn.rtast.libmc.protocol.client + +import cn.rtast.libmc.common.LibMCContext +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.packet.UnknownPacket +import cn.rtast.libmc.protocol.network.NetworkChannel +import cn.rtast.libmc.protocol.packet.configuration.* +import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket +import cn.rtast.libmc.protocol.packet.login.ClientboundDisconnectLoginPacket +import cn.rtast.libmc.protocol.packet.login.ClientboundLoginSuccessPacket +import cn.rtast.libmc.protocol.packet.login.ServerboundLoginAcknowledgedPacket +import cn.rtast.libmc.protocol.packet.login.ServerboundLoginStartPacket +import cn.rtast.libmc.protocol.packet.play.* +import cn.rtast.libmc.protocol.protocol.GameProtocols +import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent +import cn.rtast.libmc.protocol.protocol.state.ProtocolState +import cn.rtast.libmc.protocol.util.generateOfflineUuid +import kotlinx.coroutines.* +import kotlin.uuid.Uuid + +public class MinecraftClient( + private val host: String, + private val port: Int = 25565, + private val username: String, + private val uuid: Uuid = generateOfflineUuid(username), + private val context: LibMCContext = LibMCContext(), +) { + private val stateMachine = ClientStateMachine() + private val networkChannel = NetworkChannel(host, port, context, stateMachine) + private val listeners = mutableListOf<(MinecraftPacket) -> Unit>() + private var listenJob: Job? = null + + public suspend fun connect(protocolVersion: Int = 776) { + networkChannel.connect() + startListening() + networkChannel.sendPacket( + ServerboundHandshakePacket( + protocolVersion, host, + port.toUShort(), + HandshakeIntent.LOGIN + ) + ) + stateMachine.transitionTo(ProtocolState.LOGIN) + networkChannel.sendPacket(ServerboundLoginStartPacket(username, uuid)) + listenJob?.join() + } + + private fun startListening() { + listenJob = CoroutineScope(Dispatchers.IO).launch { + try { + while (isActive) { + val packet = networkChannel.readNextPacket() + handleIncomingPackets(packet) + listeners.forEach { it.invoke(packet) } + } + } catch (e: Exception) { + e.printStackTrace() + if (isActive) { + println("Network read loop exception: ${e.message}") + close() + } + } + } + } + + private fun handleIncomingPackets(packet: MinecraftPacket) { + when (packet) { + is ClientboundLoginSuccessPacket -> { + networkChannel.sendPacket(ServerboundLoginAcknowledgedPacket) + stateMachine.transitionTo(ProtocolState.CONFIGURATION) + } + + is ClientboundDisconnectLoginPacket -> { + println("Login denied: ${packet.reason}") + close() + } + + is ClientboundSelectKnownPacksPacket -> { + networkChannel.sendPacket(ServerboundSelectKnownPacksPacket(emptyList())) // TODO empty resource packs list + } + + is ClientboundPingPacket -> networkChannel.sendPacket(ServerboundPongPacket(packet.id)) + + is ClientboundKeepAliveConfigurationPacket -> { + networkChannel.sendPacket(ServerboundKeepAliveConfigurationPacket(packet.id)) + } + + is ClientboundFinishConfigurationPacket -> { + networkChannel.sendPacket(ServerboundAckFinishConfigurationPacket) + stateMachine.transitionTo(ProtocolState.PLAY) + } + + is ClientboundDisconnectConfigurationPacket -> { + println("Configuration disconnected: ${packet.reason}") + close() + } + + is ClientboundLoginPlayPacket -> { + println("Successfully joined world! Entity ID: ${packet.entityId}") + } + + is ClientboundKeepAlivePlayPacket -> { + networkChannel.sendPacket(ServerboundKeepAlivePlayPacket(id = packet.id)) + } + + is ClientboundStartConfigurationPacket -> { + networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket) + stateMachine.transitionTo(ProtocolState.CONFIGURATION) + } + + is ClientboundDisconnectPlayPacket -> { + println("Disconnected from play session: ${packet.reason}") + close() + } +// else -> println((packet as? UnknownPacket)?.data?.contentToString() ?: packet) + } + } + + public fun onPacket(listener: (MinecraftPacket) -> Unit) { + listeners.add(listener) + } + + public fun close() { + listenJob?.cancel() + networkChannel.close() + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/codec/PacketCodec.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/codec/PacketCodec.kt new file mode 100644 index 0000000..56ed718 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/codec/PacketCodec.kt @@ -0,0 +1,9 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.codec + diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/network/NetworkChannel.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/network/NetworkChannel.kt new file mode 100644 index 0000000..8fa9128 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/network/NetworkChannel.kt @@ -0,0 +1,56 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.network + +import cn.rtast.libmc.common.* +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.protocol.client.ClientStateMachine +import cn.rtast.libmc.protocol.protocol.GameProtocols + +internal class NetworkChannel( + private val host: String, + private val port: Int, + private val context: LibMCContext, + private val stateMachine: ClientStateMachine, +) { + private var socket: Socket? = null + private var readChannel: ReadChannel? = null + private var writeChannel: WriteChannel? = null + + fun connect() { + val sk = Socket(host, port, context) + this.socket = sk + this.readChannel = sk.openReadChannel() + this.writeChannel = sk.openWriteChannel() + } + + fun readNextPacket(): MinecraftPacket { + val channel = requireNotNull(readChannel) { "ReadChannel not connected" } + val length = channel.readVarInt() + val buf = channel.readBytes(length).wrap() + val currentState = stateMachine.currentState + val packetId = buf.readVarInt() + return GameProtocols.clientboundGameProtocols.getRegistry(currentState).decodePacket(packetId, buf) + } + + fun sendPacket(packet: MinecraftPacket) { + val channel = requireNotNull(writeChannel) { "WriteChannel not connected" } + val bodyBuffer = BytesBuffer() + GameProtocols.serverboundGameProtocols.getRegistry(stateMachine.currentState).encodePacket(bodyBuffer, packet) + val frameBuffer = BytesBuffer().apply { + writeVarInt(bodyBuffer.size) + writeBuffer(bodyBuffer) + } + channel.writeFully(frameBuffer.toByteArray()) + channel.flush() + } + + fun close() { + socket?.close() + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCookieRequestPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCookieRequestPacket.kt new file mode 100644 index 0000000..3fa8a52 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCookieRequestPacket.kt @@ -0,0 +1,27 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.readIdentifier +import cn.rtast.libmc.protocol.protocol.game.writeIdentifier + +public data class ClientboundCookieRequestPacket(val key: Identifier) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundCookieRequestPacket) { + buffer.writeIdentifier(value.key) + } + + override fun decode(buffer: BytesBuffer): ClientboundCookieRequestPacket { + return ClientboundCookieRequestPacket(key = buffer.readIdentifier()) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCustomPayloadPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCustomPayloadPacket.kt new file mode 100644 index 0000000..af87f6e --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundCustomPayloadPacket.kt @@ -0,0 +1,48 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.readIdentifier +import cn.rtast.libmc.protocol.protocol.game.writeIdentifier + +public data class ClientboundCustomPayloadPacket(val channel: Identifier, val data: ByteArray) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundCustomPayloadPacket) { + buffer.writeIdentifier(value.channel) + buffer.writeBytes(value.data) + } + + override fun decode(buffer: BytesBuffer): ClientboundCustomPayloadPacket { + val channel = buffer.readIdentifier() + val data = buffer.readBytes(buffer.remaining.toInt()) + return ClientboundCustomPayloadPacket(channel, data) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as ClientboundCustomPayloadPacket + + if (channel != other.channel) return false + if (!data.contentEquals(other.data)) return false + + return true + } + + override fun hashCode(): Int { + var result = channel.hashCode() + result = 31 * result + data.contentHashCode() + return result + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundDisconnectConfigurationPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundDisconnectConfigurationPacket.kt new file mode 100644 index 0000000..cc7cb14 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundDisconnectConfigurationPacket.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.protocol.util.readMinimalTextNbt +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.BytesBuffer + +public data class ClientboundDisconnectConfigurationPacket(val reason: String) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectConfigurationPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundDisconnectConfigurationPacket { + val reasonText = buffer.readMinimalTextNbt() + return ClientboundDisconnectConfigurationPacket(reason = reasonText) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundFinishConfigurationPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundFinishConfigurationPacket.kt new file mode 100644 index 0000000..d46e8a3 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundFinishConfigurationPacket.kt @@ -0,0 +1,20 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data object ClientboundFinishConfigurationPacket : MinecraftPacket, + PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundFinishConfigurationPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundFinishConfigurationPacket { + return ClientboundFinishConfigurationPacket + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundKeepAliveConfigurationPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundKeepAliveConfigurationPacket.kt new file mode 100644 index 0000000..a01f8bf --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundKeepAliveConfigurationPacket.kt @@ -0,0 +1,24 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ClientboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundKeepAliveConfigurationPacket) { + buffer.writeLong(value.id) + } + + override fun decode(buffer: BytesBuffer): ClientboundKeepAliveConfigurationPacket { + return ClientboundKeepAliveConfigurationPacket(buffer.readLong()) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundPingPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundPingPacket.kt new file mode 100644 index 0000000..985afd0 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundPingPacket.kt @@ -0,0 +1,22 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ClientboundPingPacket(val id: Int) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundPingPacket) { + buffer.writeInt(value.id) + } + + override fun decode(buffer: BytesBuffer): ClientboundPingPacket = ClientboundPingPacket(buffer.readInt()) + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundSelectKnownPacksPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundSelectKnownPacksPacket.kt new file mode 100644 index 0000000..8864527 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ClientboundSelectKnownPacksPacket.kt @@ -0,0 +1,29 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.readVarInt +import cn.rtast.libmc.common.writeVarInt + +public data class ClientboundSelectKnownPacksPacket(val knownPacks: List) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundSelectKnownPacksPacket) { + buffer.writeVarInt(value.knownPacks.size) + value.knownPacks.forEach { KnownPacks.encode(buffer, it) } + } + + override fun decode(buffer: BytesBuffer): ClientboundSelectKnownPacksPacket { + val packsCount = buffer.readVarInt() + val packs = List(packsCount) { KnownPacks.decode(buffer) } + return ClientboundSelectKnownPacksPacket(packs) + } + } +} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/KnownPacks.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/KnownPacks.kt similarity index 68% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/KnownPacks.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/KnownPacks.kt index 17f891b..38f2307 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/configuration/KnownPacks.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/KnownPacks.kt @@ -5,28 +5,24 @@ */ -package cn.rtast.libmc.chat.packet.configuration +package cn.rtast.libmc.protocol.packet.configuration import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.readMcString import cn.rtast.libmc.common.writeMcString import kotlinx.serialization.Serializable @Serializable -public data class KnownPacks( - val namespace: String, - val id: String, - val version: String, -) { +public data class KnownPacks(val namespace: String, val id: String, val version: String) { public companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: KnownPacks) { + override fun encode(buffer: BytesBuffer, value: KnownPacks) { buffer.writeMcString(value.namespace) buffer.writeMcString(value.id) buffer.writeMcString(value.version) } - override fun decode(buffer: _Buffer): KnownPacks { + override fun decode(buffer: BytesBuffer): KnownPacks { val namespace = buffer.readMcString() val id = buffer.readMcString() val version = buffer.readMcString() diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundAckFinishConfigurationPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundAckFinishConfigurationPacket.kt new file mode 100644 index 0000000..adf2e1b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundAckFinishConfigurationPacket.kt @@ -0,0 +1,20 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data object ServerboundAckFinishConfigurationPacket : MinecraftPacket, + PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundAckFinishConfigurationPacket) {} + + override fun decode(buffer: BytesBuffer): ServerboundAckFinishConfigurationPacket = + ServerboundAckFinishConfigurationPacket +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundCookieResponsePacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundCookieResponsePacket.kt new file mode 100644 index 0000000..e9da34b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundCookieResponsePacket.kt @@ -0,0 +1,60 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.readVarInt +import cn.rtast.libmc.common.writeVarInt +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.readIdentifier +import cn.rtast.libmc.protocol.protocol.game.writeIdentifier + +public data class ServerboundCookieResponsePacket(val key: Identifier, val payload: ByteArray?) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundCookieResponsePacket) { + buffer.writeIdentifier(value.key) + if (value.payload != null) { + buffer.writeBoolean(true) + buffer.writeVarInt(value.payload.size) + buffer.writeBytes(value.payload) + } else { + buffer.writeBoolean(false) + } + } + + override fun decode(buffer: BytesBuffer): ServerboundCookieResponsePacket { + val key = buffer.readIdentifier() + val hasPayload = buffer.readBoolean() + val payload = if (hasPayload) { + val length = buffer.readVarInt() + buffer.readBytes(length) + } else null + return ServerboundCookieResponsePacket(key, payload) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as ServerboundCookieResponsePacket + + if (key != other.key) return false + if (!payload.contentEquals(other.payload)) return false + + return true + } + + override fun hashCode(): Int { + var result = key.hashCode() + result = 31 * result + (payload?.contentHashCode() ?: 0) + return result + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundKeepAliveConfigurationPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundKeepAliveConfigurationPacket.kt new file mode 100644 index 0000000..e940090 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundKeepAliveConfigurationPacket.kt @@ -0,0 +1,24 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ServerboundKeepAliveConfigurationPacket(val id: Long) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundKeepAliveConfigurationPacket) { + buffer.writeLong(value.id) + } + + override fun decode(buffer: BytesBuffer): ServerboundKeepAliveConfigurationPacket { + return ServerboundKeepAliveConfigurationPacket(buffer.readLong()) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundPongPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundPongPacket.kt new file mode 100644 index 0000000..3fe9db0 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundPongPacket.kt @@ -0,0 +1,22 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ServerboundPongPacket(val id: Int) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundPongPacket) { + buffer.writeInt(value.id) + } + + override fun decode(buffer: BytesBuffer): ServerboundPongPacket = ServerboundPongPacket(buffer.readInt()) + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundSelectKnownPacksPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundSelectKnownPacksPacket.kt new file mode 100644 index 0000000..884cbfa --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/configuration/ServerboundSelectKnownPacksPacket.kt @@ -0,0 +1,29 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.configuration + +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.readVarInt +import cn.rtast.libmc.common.writeVarInt + +public data class ServerboundSelectKnownPacksPacket(val knownPacks: List) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundSelectKnownPacksPacket) { + buffer.writeVarInt(value.knownPacks.size) + value.knownPacks.forEach { KnownPacks.encode(buffer, it) } + } + + override fun decode(buffer: BytesBuffer): ServerboundSelectKnownPacksPacket { + val packsCount = buffer.readVarInt() + val packs = List(packsCount) { KnownPacks.decode(buffer) } + return ServerboundSelectKnownPacksPacket(packs) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/handshake/ServerboundHandshakePacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/handshake/ServerboundHandshakePacket.kt new file mode 100644 index 0000000..a8b1832 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/handshake/ServerboundHandshakePacket.kt @@ -0,0 +1,33 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.handshake + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.writeMcString +import cn.rtast.libmc.common.writeVarInt +import cn.rtast.libmc.protocol.protocol.state.HandshakeIntent + +public data class ServerboundHandshakePacket( + val protocolVersion: Int, + val serverAddress: String, + val serverPort: UShort, + val intent: HandshakeIntent, +) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundHandshakePacket) { + buffer.writeVarInt(value.protocolVersion) + buffer.writeMcString(value.serverAddress) + buffer.writeShort(value.serverPort.toShort()) + buffer.writeVarInt(value.intent.intentID) + } + + override fun decode(buffer: BytesBuffer): ServerboundHandshakePacket = throw UnsupportedOperationException() + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundDisconnectLoginPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundDisconnectLoginPacket.kt new file mode 100644 index 0000000..0ce7d65 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundDisconnectLoginPacket.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.login + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.readMcString + +public data class ClientboundDisconnectLoginPacket(val reason: String) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectLoginPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundDisconnectLoginPacket { + val reasonJson = buffer.readMcString() + return ClientboundDisconnectLoginPacket(reason = reasonJson) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundLoginSuccessPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundLoginSuccessPacket.kt new file mode 100644 index 0000000..286707e --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ClientboundLoginSuccessPacket.kt @@ -0,0 +1,26 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.login + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.readUuid +import cn.rtast.libmc.protocol.profile.GameProfile +import kotlin.uuid.Uuid + +public data class ClientboundLoginSuccessPacket(val gameProfile: GameProfile, val sessionId: Uuid) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundLoginSuccessPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundLoginSuccessPacket { + val gameProfile = GameProfile.decode(buffer) + val sessionId = buffer.readUuid() + return ClientboundLoginSuccessPacket(gameProfile, sessionId) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginAcknowledgedPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginAcknowledgedPacket.kt new file mode 100644 index 0000000..38f3576 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginAcknowledgedPacket.kt @@ -0,0 +1,19 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.login + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data object ServerboundLoginAcknowledgedPacket : MinecraftPacket, + PacketCodec { + + override fun encode(buffer: BytesBuffer, value: ServerboundLoginAcknowledgedPacket) {} + override fun decode(buffer: BytesBuffer): ServerboundLoginAcknowledgedPacket = throw UnsupportedOperationException() +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginStartPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginStartPacket.kt new file mode 100644 index 0000000..e930146 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/login/ServerboundLoginStartPacket.kt @@ -0,0 +1,26 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.login + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.writeMcString +import cn.rtast.libmc.common.writeUuid +import kotlin.uuid.Uuid + +public data class ServerboundLoginStartPacket(val username: String, val playerUuid: Uuid) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundLoginStartPacket) { + buffer.writeMcString(value.username) + buffer.writeUuid(value.playerUuid) + } + + override fun decode(buffer: BytesBuffer): ServerboundLoginStartPacket = throw UnsupportedOperationException() + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundDisconnectPlayPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundDisconnectPlayPacket.kt new file mode 100644 index 0000000..a8f5c8a --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundDisconnectPlayPacket.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.protocol.util.readMinimalTextNbt +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.BytesBuffer + +public data class ClientboundDisconnectPlayPacket(val reason: String) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundDisconnectPlayPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundDisconnectPlayPacket { + val reasonText = buffer.readMinimalTextNbt() + return ClientboundDisconnectPlayPacket(reason = reasonText) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundKeepAlivePlayPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundKeepAlivePlayPacket.kt new file mode 100644 index 0000000..cb656d7 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundKeepAlivePlayPacket.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ClientboundKeepAlivePlayPacket(val id: Long) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundKeepAlivePlayPacket) { + buffer.writeLong(value.id) + } + + override fun decode(buffer: BytesBuffer): ClientboundKeepAlivePlayPacket = + ClientboundKeepAlivePlayPacket(buffer.readLong()) + } +} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundLoginPlayPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundLoginPlayPacket.kt similarity index 79% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundLoginPlayPacket.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundLoginPlayPacket.kt index caebd1a..0cfaa91 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundLoginPlayPacket.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundLoginPlayPacket.kt @@ -5,16 +5,19 @@ */ -package cn.rtast.libmc.chat.packet.play +package cn.rtast.libmc.protocol.packet.play -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.chat.protocol.* -import cn.rtast.libmc.common.MinecraftPacket +import cn.rtast.libmc.common.packet.MinecraftPacket import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.readVarInt +import cn.rtast.libmc.protocol.protocol.game.BlockPos +import cn.rtast.libmc.protocol.protocol.game.GameMode +import cn.rtast.libmc.protocol.protocol.game.Identifier +import cn.rtast.libmc.protocol.protocol.game.readBlockPos +import cn.rtast.libmc.protocol.protocol.game.readIdentifier -internal data class ClientboundLoginPlayPacket( +public data class ClientboundLoginPlayPacket( val entityId: Int, val isHardcore: Boolean, val dimensionNames: List, @@ -38,12 +41,10 @@ internal data class ClientboundLoginPlayPacket( val seaLevel: Int, val isOnlineMode: Boolean, val enforceSecureChat: Boolean, -) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x31 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundLoginPlayPacket) {} - override fun decode(buffer: _Buffer): ClientboundLoginPlayPacket { +) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundLoginPlayPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundLoginPlayPacket { val entityId = buffer.readInt() val isHardcore = buffer.readBoolean() val dimensionNamesCount = buffer.readVarInt() diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundPingPlayPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundPingPlayPacket.kt new file mode 100644 index 0000000..51f50dc --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundPingPlayPacket.kt @@ -0,0 +1,24 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ClientboundPingPlayPacket(val id: Int) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundPingPlayPacket) { + buffer.writeInt(value.id) + } + + override fun decode(buffer: BytesBuffer): ClientboundPingPlayPacket { + return ClientboundPingPlayPacket(id = buffer.readInt()) + } + } +} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundPlayerChatMessagePacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundPlayerChatMessagePacket.kt similarity index 83% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundPlayerChatMessagePacket.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundPlayerChatMessagePacket.kt index f6016cd..13cde32 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/packet/play/ClientboundPlayerChatMessagePacket.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundPlayerChatMessagePacket.kt @@ -5,16 +5,16 @@ */ -package cn.rtast.libmc.chat.packet.play +package cn.rtast.libmc.protocol.packet.play -import cn.rtast.libmc.chat.chat.ChatFilterType -import cn.rtast.libmc.chat.chat.PreviousMessageEntry -import cn.rtast.libmc.chat.packet.PacketDirection -import cn.rtast.libmc.chat.util.writeMinimalTextNbt import cn.rtast.libmc.common.* +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.protocol.chat.ChatFilterType +import cn.rtast.libmc.protocol.chat.PreviousMessageEntry +import cn.rtast.libmc.protocol.util.writeMinimalTextNbt import kotlin.uuid.Uuid -internal data class ClientboundPlayerChatMessagePacket( +public data class ClientboundPlayerChatMessagePacket( val globalIndex: Int, val sender: Uuid, val index: Int, @@ -29,11 +29,9 @@ internal data class ClientboundPlayerChatMessagePacket( val chatType: Int, val senderName: String, val targetName: String?, -) : MinecraftPacket, PacketDirection.ClientboundPacket { - override val packetId: Int = 0x41 - - companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: ClientboundPlayerChatMessagePacket) { +) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundPlayerChatMessagePacket) { buffer.writeVarInt(value.globalIndex) buffer.writeUuid(value.sender) buffer.writeVarInt(value.index) @@ -68,7 +66,8 @@ internal data class ClientboundPlayerChatMessagePacket( value.targetName?.let { buffer.writeMinimalTextNbt(it) } } - override fun decode(buffer: _Buffer): ClientboundPlayerChatMessagePacket = throw UnsupportedOperationException() // TODO + override fun decode(buffer: BytesBuffer): ClientboundPlayerChatMessagePacket = + throw UnsupportedOperationException() // TODO } override fun equals(other: Any?): Boolean { @@ -82,7 +81,6 @@ internal data class ClientboundPlayerChatMessagePacket( if (timestamp != other.timestamp) return false if (salt != other.salt) return false if (chatType != other.chatType) return false - if (packetId != other.packetId) return false if (sender != other.sender) return false if (!messageSignature.contentEquals(other.messageSignature)) return false if (message != other.message) return false @@ -102,7 +100,6 @@ internal data class ClientboundPlayerChatMessagePacket( result = 31 * result + timestamp.hashCode() result = 31 * result + salt.hashCode() result = 31 * result + chatType - result = 31 * result + packetId result = 31 * result + sender.hashCode() result = 31 * result + (messageSignature?.contentHashCode() ?: 0) result = 31 * result + message.hashCode() diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundStartConfigurationPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundStartConfigurationPacket.kt new file mode 100644 index 0000000..5f2beb8 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ClientboundStartConfigurationPacket.kt @@ -0,0 +1,20 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data object ClientboundStartConfigurationPacket : MinecraftPacket, + PacketCodec { + override fun encode(buffer: BytesBuffer, value: ClientboundStartConfigurationPacket) {} + override fun decode(buffer: BytesBuffer): ClientboundStartConfigurationPacket { + return ClientboundStartConfigurationPacket + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundChatMessagePacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundChatMessagePacket.kt new file mode 100644 index 0000000..811e38d --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundChatMessagePacket.kt @@ -0,0 +1,34 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket +import cn.rtast.libmc.common.writeMcString +import cn.rtast.libmc.common.writeVarInt +import kotlin.time.Clock + +public data class ServerboundChatMessagePacket( + val message: String, + val timestamp: Long = Clock.System.now().toEpochMilliseconds(), + val salt: Long = 0L, +) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundChatMessagePacket) { + buffer.writeMcString(value.message) + buffer.writeLong(value.timestamp) + buffer.writeLong(value.salt) + buffer.writeBoolean(false) // has signature + buffer.writeVarInt(0) // message count + buffer.writeBytes(byteArrayOf(0, 0, 0)) + } + + override fun decode(buffer: BytesBuffer): ServerboundChatMessagePacket = throw UnsupportedOperationException() + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundConfigurationAcknowledgedPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundConfigurationAcknowledgedPacket.kt new file mode 100644 index 0000000..394c79f --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundConfigurationAcknowledgedPacket.kt @@ -0,0 +1,20 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public object ServerboundConfigurationAcknowledgedPacket : MinecraftPacket, + PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundConfigurationAcknowledgedPacket) {} + override fun decode(buffer: BytesBuffer): ServerboundConfigurationAcknowledgedPacket { + return ServerboundConfigurationAcknowledgedPacket + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundKeepAlivePlayPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundKeepAlivePlayPacket.kt new file mode 100644 index 0000000..80afe6d --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundKeepAlivePlayPacket.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ServerboundKeepAlivePlayPacket(val id: Long) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundKeepAlivePlayPacket) { + buffer.writeLong(value.id) + } + + override fun decode(buffer: BytesBuffer): ServerboundKeepAlivePlayPacket = + ServerboundKeepAlivePlayPacket(buffer.readLong()) + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundPongPlayPacket.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundPongPlayPacket.kt new file mode 100644 index 0000000..5e706d8 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/packet/play/ServerboundPongPlayPacket.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + +package cn.rtast.libmc.protocol.packet.play + +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.PacketCodec +import cn.rtast.libmc.common.packet.MinecraftPacket + +public data class ServerboundPongPlayPacket(val id: Int) : MinecraftPacket { + public companion object Codec : PacketCodec { + override fun encode(buffer: BytesBuffer, value: ServerboundPongPlayPacket) { + buffer.writeInt(value.id) + } + + override fun decode(buffer: BytesBuffer): ServerboundPongPlayPacket { + return ServerboundPongPlayPacket(id = buffer.readInt()) + } + } +} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/profile/GameProfile.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/profile/GameProfile.kt similarity index 84% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/profile/GameProfile.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/profile/GameProfile.kt index 3d9d6c5..18ca9d7 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/profile/GameProfile.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/profile/GameProfile.kt @@ -5,7 +5,7 @@ */ -package cn.rtast.libmc.chat.profile +package cn.rtast.libmc.protocol.profile import cn.rtast.libmc.common.* import kotlinx.serialization.Serializable @@ -24,14 +24,14 @@ public data class GameProfile( val signature: String?, ) { public companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: Property) { + override fun encode(buffer: BytesBuffer, value: Property) { buffer.writeMcString(value.name) buffer.writeMcString(value.value) buffer.writeBoolean(value.signature != null) value.signature?.let { buffer.writeMcString(it) } } - override fun decode(buffer: _Buffer): Property { + override fun decode(buffer: BytesBuffer): Property { val name = buffer.readMcString() val value = buffer.readMcString() val hasSignature = buffer.readBoolean() @@ -42,14 +42,14 @@ public data class GameProfile( } public companion object Codec : PacketCodec { - override fun encode(buffer: _Buffer, value: GameProfile) { + override fun encode(buffer: BytesBuffer, value: GameProfile) { buffer.writeUuid(value.uuid) buffer.writeMcString(value.username) buffer.writeVarInt(value.properties.size) // prefixed array value.properties.forEach { prop -> Property.encode(buffer, prop) } } - override fun decode(buffer: _Buffer): GameProfile { + override fun decode(buffer: BytesBuffer): GameProfile { val uuid = buffer.readUuid() val username = buffer.readMcString() val propertyCount = buffer.readVarInt() diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GameProtocols.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GameProtocols.kt new file mode 100644 index 0000000..30c2514 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/GameProtocols.kt @@ -0,0 +1,64 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + +package cn.rtast.libmc.protocol.protocol + +import cn.rtast.libmc.protocol.packet.configuration.* +import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket +import cn.rtast.libmc.protocol.packet.login.ClientboundDisconnectLoginPacket +import cn.rtast.libmc.protocol.packet.login.ClientboundLoginSuccessPacket +import cn.rtast.libmc.protocol.packet.login.ServerboundLoginAcknowledgedPacket +import cn.rtast.libmc.protocol.packet.login.ServerboundLoginStartPacket +import cn.rtast.libmc.protocol.packet.play.* +import cn.rtast.libmc.protocol.protocol.state.ProtocolState +import cn.rtast.libmc.protocol.protocol.state.ProtocolStateRegistry + +internal object GameProtocols { + val clientboundGameProtocols = ProtocolStateRegistry().apply { + register(ProtocolState.CONFIGURATION) { + register(0x00, ClientboundCookieRequestPacket) + register(0x01, ClientboundCustomPayloadPacket) + register(0x02, ClientboundDisconnectConfigurationPacket) + register(0x03, ClientboundFinishConfigurationPacket) + register(0x04, ClientboundKeepAliveConfigurationPacket) + register(0x05, ClientboundPingPacket) + register(0x0E, ClientboundSelectKnownPacksPacket) + } + register(ProtocolState.LOGIN) { + register(0x00, ClientboundDisconnectLoginPacket) + register(0x02, ClientboundLoginSuccessPacket) + } + register(ProtocolState.PLAY) { + register(0x2C, ClientboundKeepAlivePlayPacket) + register(0x2E, ClientboundLoginPlayPacket) + register(0x3A, ClientboundPingPlayPacket) + register(0x3D, ClientboundPlayerChatMessagePacket) + register(0x76, ClientboundStartConfigurationPacket) + } + } + + val serverboundGameProtocols = ProtocolStateRegistry().apply { + register(ProtocolState.HANDSHAKE) { + register(0x00, ServerboundHandshakePacket) + } + register(ProtocolState.CONFIGURATION) { + register(0x03, ServerboundAckFinishConfigurationPacket) + register(0x04, ServerboundKeepAliveConfigurationPacket) + register(0x05, ServerboundPongPacket) + register(0x07, ServerboundSelectKnownPacksPacket) + } + register(ProtocolState.LOGIN) { + register(0x00, ServerboundLoginStartPacket) + register(0x03, ServerboundLoginAcknowledgedPacket) + } + register(ProtocolState.PLAY) { + register(0x09, ServerboundChatMessagePacket) + register(0x0B, ServerboundPongPlayPacket) + register(0x0D, ServerboundConfigurationAcknowledgedPacket) + register(0x1C, ServerboundKeepAlivePlayPacket) + } + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/PacketDirection.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/PacketDirection.kt new file mode 100644 index 0000000..47bea6b --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/PacketDirection.kt @@ -0,0 +1,12 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.protocol + +internal enum class PacketDirection { + SERVERBOUND, CLIENTBOUND +} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/BlockPos.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/BlockPos.kt similarity index 76% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/BlockPos.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/BlockPos.kt index 3d73e0b..5a909fe 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/BlockPos.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/BlockPos.kt @@ -5,10 +5,10 @@ */ -package cn.rtast.libmc.chat.protocol +package cn.rtast.libmc.protocol.protocol.game import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer import kotlinx.serialization.Serializable /** @@ -22,7 +22,7 @@ public data class BlockPos(val x: Int, val y: Int, val z: Int) { private const val PACKED_Y_MASK = 0xFFFL // 12 bits private const val PACKED_Z_MASK = 0x3FFFFFFL // 26 bits - override fun decode(buffer: _Buffer): BlockPos { + override fun decode(buffer: BytesBuffer): BlockPos { val packed = buffer.readLong() val x = (packed shr 38).toInt() val y = (packed shl 52 shr 52).toInt() @@ -30,7 +30,7 @@ public data class BlockPos(val x: Int, val y: Int, val z: Int) { return BlockPos(x, y, z) } - override fun encode(buffer: _Buffer, value: BlockPos) { + override fun encode(buffer: BytesBuffer, value: BlockPos) { val xLong = (value.x.toLong() and PACKED_X_MASK) val yLong = (value.y.toLong() and PACKED_Y_MASK) val zLong = (value.z.toLong() and PACKED_Z_MASK) @@ -39,5 +39,5 @@ public data class BlockPos(val x: Int, val y: Int, val z: Int) { } } -internal fun _Buffer.readBlockPos(): BlockPos = BlockPos.decode(this) -internal fun _Buffer.writeBlockPos(pos: BlockPos) = BlockPos.encode(this, pos) \ No newline at end of file +internal fun BytesBuffer.readBlockPos(): BlockPos = BlockPos.decode(this) +internal fun BytesBuffer.writeBlockPos(pos: BlockPos) = BlockPos.encode(this, pos) \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/GameMode.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/GameMode.kt similarity index 91% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/GameMode.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/GameMode.kt index 4f0d29e..abbb196 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/GameMode.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/GameMode.kt @@ -5,7 +5,7 @@ */ -package cn.rtast.libmc.chat.protocol +package cn.rtast.libmc.protocol.protocol.game public enum class GameMode(public val id: Byte) { Survival(0), diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/Identifier.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/Identifier.kt similarity index 66% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/Identifier.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/Identifier.kt index be74559..bfc78bf 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/Identifier.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/game/Identifier.kt @@ -5,10 +5,10 @@ */ -package cn.rtast.libmc.chat.protocol +package cn.rtast.libmc.protocol.protocol.game import cn.rtast.libmc.common.PacketCodec -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer import cn.rtast.libmc.common.readMcString import cn.rtast.libmc.common.writeMcString import kotlin.jvm.JvmInline @@ -26,13 +26,13 @@ public value class Identifier(public val full: String) { public companion object Codec : PacketCodec { public fun of(namespace: String, path: String): Identifier = Identifier("$namespace:$path") - override fun encode(buffer: _Buffer, value: Identifier) { + override fun encode(buffer: BytesBuffer, value: Identifier) { buffer.writeMcString(value.toString()) } - override fun decode(buffer: _Buffer): Identifier = Identifier(buffer.readMcString()) + override fun decode(buffer: BytesBuffer): Identifier = Identifier(buffer.readMcString()) } } -internal fun _Buffer.readIdentifier(): Identifier = Identifier.decode(this) -internal fun _Buffer.writeIdentifier(identifier: Identifier) = Identifier.encode(this, identifier) \ No newline at end of file +internal fun BytesBuffer.readIdentifier(): Identifier = Identifier.decode(this) +internal fun BytesBuffer.writeIdentifier(identifier: Identifier) = Identifier.encode(this, identifier) \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/HandshakeIntent.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/HandshakeIntent.kt new file mode 100644 index 0000000..2251ab9 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/HandshakeIntent.kt @@ -0,0 +1,24 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.protocol.state + +import kotlin.jvm.JvmInline + +@JvmInline +public value class HandshakeIntent internal constructor(public val intentID: Int) { + public companion object { + public val STATUS: HandshakeIntent = HandshakeIntent(1) + public val LOGIN: HandshakeIntent = HandshakeIntent(2) + public val TRANSFER: HandshakeIntent = HandshakeIntent(3) + + public fun fromID(intentID: Int): HandshakeIntent = when (intentID) { + 1 -> STATUS; 2 -> LOGIN; 3 -> TRANSFER + else -> throw IllegalArgumentException("Unknown Handshake Intent ID") + } + } +} \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/ProtocolState.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/ProtocolState.kt similarity index 53% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/ProtocolState.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/ProtocolState.kt index e1edcc0..f1e59fe 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/protocol/ProtocolState.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/ProtocolState.kt @@ -5,11 +5,12 @@ */ -package cn.rtast.libmc.chat.protocol +package cn.rtast.libmc.protocol.protocol.state -internal enum class ProtocolState { +public enum class ProtocolState { HANDSHAKE, LOGIN, CONFIGURATION, - PLAY + PLAY, + DISCONNECTED } \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/ProtocolStateRegistry.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/ProtocolStateRegistry.kt new file mode 100644 index 0000000..9748fd0 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/protocol/state/ProtocolStateRegistry.kt @@ -0,0 +1,23 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.protocol.state + +import cn.rtast.libmc.common.packet.PacketRegistry +import kotlin.enums.enumEntries + +public class ProtocolStateRegistry { + // create registries for different state + private val registries = enumEntries().toTypedArray().associateWith { PacketRegistry() } + + public fun getRegistry(state: ProtocolState): PacketRegistry = + requireNotNull(registries[state]) { "No registry found for state $state" } + + public fun register(state: ProtocolState, block: PacketRegistry.() -> Unit) { + registries[state]?.apply(block) + } +} \ No newline at end of file diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/md5.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/md5.kt new file mode 100644 index 0000000..afd81e4 --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/md5.kt @@ -0,0 +1,254 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/5 + */ + + +package cn.rtast.libmc.protocol.util + +internal object CycloneMd5 { + const val BLOCK_SIZE = 64 + const val DIGEST_SIZE = 16 + const val MIN_PAD_SIZE = 9 + + val OID = byteArrayOf( + 0x2A.toByte(), 0x86.toByte(), 0x48.toByte(), 0x86.toByte(), + 0xF7.toByte(), 0x0D.toByte(), 0x02.toByte(), 0x05.toByte() + ) + + private val PADDING = ByteArray(64).apply { this[0] = 0x80.toByte() } + + private val K = intArrayOf( + 0xD76AA478.toInt(), 0xE8C7B756.toInt(), 0x242070DB, 0xC1BDCEEE.toInt(), + 0xF57C0FAF.toInt(), 0x4787C62A, 0xA8304613.toInt(), 0xFD469501.toInt(), + 0x698098D8, 0x8B44F7AF.toInt(), 0xFFFF5BB1.toInt(), 0x895CD7BE.toInt(), + 0x6B901122, 0xFD987193.toInt(), 0xA679438E.toInt(), 0x49B40821, + 0xF61E2562.toInt(), 0xC040B340.toInt(), 0x265E5A51, 0xE9B6C7AA.toInt(), + 0xD62F105D.toInt(), 0x02441453, 0xD8A1E681.toInt(), 0xE7D3FBC8.toInt(), + 0x21E1CDE6, 0xC33707D6.toInt(), 0xF4D50D87.toInt(), 0x455A14ED, + 0xA9E3E905.toInt(), 0xFCEFA3F8.toInt(), 0x676F02D9, 0x8D2A4C8A.toInt(), + 0xFFFA3942.toInt(), 0x8771F681.toInt(), 0x6D9D6122, 0xFDE5380C.toInt(), + 0xA4BEEA44.toInt(), 0x4BDECFA9, 0xF6BB4B60.toInt(), 0xBEBFBC70.toInt(), + 0x289B7EC6, 0xEAA127FA.toInt(), 0xD4EF3085.toInt(), 0x04881D05, + 0xD9D4D039.toInt(), 0xE6DB99E5.toInt(), 0x1FA27CF8, 0xC4AC5665.toInt(), + 0xF4292244.toInt(), 0x432AFF97, 0xAB9423A7.toInt(), 0xFC93A039.toInt(), + 0x655B59C3, 0x8F0CCC92.toInt(), 0xFFEFF47D.toInt(), 0x85845DD1.toInt(), + 0x6FA87E4F, 0xFE2CE6E0.toInt(), 0xA3014314.toInt(), 0x4E0811A1, + 0xF7537E82.toInt(), 0xBD3AF235.toInt(), 0x2AD7D2BB, 0xEB86D391.toInt() + ) + + private class Context { + val h = IntArray(4) + val buffer = ByteArray(64) + val x = IntArray(16) + var size: Int = 0 + var totalSize: Long = 0L + } + + fun compute(data: ByteArray): ByteArray { + val digest = ByteArray(DIGEST_SIZE) + val context = Context() + initContext(context) + updateContext(context, data, 0, data.size) + finalContext(context, digest) + return digest + } + + fun computeToHex(data: ByteArray): String { + return compute(data).toHexString() + } + + fun computeToHex(text: String): String { + return compute(text.encodeToByteArray()).toHexString() + } + + fun computeToBytes(data: ByteArray): ByteArray = compute(data) + + private fun initContext(context: Context) { + context.h[0] = 0x67452301 + context.h[1] = 0xEFCDAB89.toInt() + context.h[2] = 0x98BADCFE.toInt() + context.h[3] = 0x10325476 + context.size = 0 + context.totalSize = 0L + } + + private fun updateContext(context: Context, data: ByteArray, offset: Int, length: Int) { + var dataOffset = offset + var remLength = length + + while (remLength > 0) { + val n = minOf(remLength, 64 - context.size) + data.copyInto(context.buffer, context.size, dataOffset, dataOffset + n) + + context.size += n + context.totalSize += n + dataOffset += n + remLength -= n + + if (context.size == 64) { + processBlock(context) + context.size = 0 + } + } + } + + private fun finalContext(context: Context, digest: ByteArray) { + var totalBits: Long = context.totalSize * 8L + val paddingSize = if (context.size < 56) { + 56 - context.size + } else { + 64 + 56 - context.size + } + + updateContext(context, PADDING, 0, paddingSize) + + for (i in 0 until 8) { + context.buffer[56 + i] = (totalBits and 0xFFL).toByte() + totalBits = totalBits ushr 8 + } + + processBlock(context) + + for (i in 0 until (DIGEST_SIZE / 4)) { + store32le(context.h[i], digest, i * 4) + } + } + + private fun processBlock(context: Context) { + var a = context.h[0] + var b = context.h[1] + var c = context.h[2] + var d = context.h[3] + + val x = context.x + + for (i in 0 until 16) { + x[i] = load32le(context.buffer, i * 4) + } + + // Round 1 + a = ff(a, b, c, d, x[0], 7, K[0]) + d = ff(d, a, b, c, x[1], 12, K[1]) + c = ff(c, d, a, b, x[2], 17, K[2]) + b = ff(b, c, d, a, x[3], 22, K[3]) + a = ff(a, b, c, d, x[4], 7, K[4]) + d = ff(d, a, b, c, x[5], 12, K[5]) + c = ff(c, d, a, b, x[6], 17, K[6]) + b = ff(b, c, d, a, x[7], 22, K[7]) + a = ff(a, b, c, d, x[8], 7, K[8]) + d = ff(d, a, b, c, x[9], 12, K[9]) + c = ff(c, d, a, b, x[10], 17, K[10]) + b = ff(b, c, d, a, x[11], 22, K[11]) + a = ff(a, b, c, d, x[12], 7, K[12]) + d = ff(d, a, b, c, x[13], 12, K[13]) + c = ff(c, d, a, b, x[14], 17, K[14]) + b = ff(b, c, d, a, x[15], 22, K[15]) + + // Round 2 + a = gg(a, b, c, d, x[1], 5, K[16]) + d = gg(d, a, b, c, x[6], 9, K[17]) + c = gg(c, d, a, b, x[11], 14, K[18]) + b = gg(b, c, d, a, x[0], 20, K[19]) + a = gg(a, b, c, d, x[5], 5, K[20]) + d = gg(d, a, b, c, x[10], 9, K[21]) + c = gg(c, d, a, b, x[15], 14, K[22]) + b = gg(b, c, d, a, x[4], 20, K[23]) + a = gg(a, b, c, d, x[9], 5, K[24]) + d = gg(d, a, b, c, x[14], 9, K[25]) + c = gg(c, d, a, b, x[3], 14, K[26]) + b = gg(b, c, d, a, x[8], 20, K[27]) + a = gg(a, b, c, d, x[13], 5, K[28]) + d = gg(d, a, b, c, x[2], 9, K[29]) + c = gg(c, d, a, b, x[7], 14, K[30]) + b = gg(b, c, d, a, x[12], 20, K[31]) + + // Round 3 + a = hh(a, b, c, d, x[5], 4, K[32]) + d = hh(d, a, b, c, x[8], 11, K[33]) + c = hh(c, d, a, b, x[11], 16, K[34]) + b = hh(b, c, d, a, x[14], 23, K[35]) + a = hh(a, b, c, d, x[1], 4, K[36]) + d = hh(d, a, b, c, x[4], 11, K[37]) + c = hh(c, d, a, b, x[7], 16, K[38]) + b = hh(b, c, d, a, x[10], 23, K[39]) + a = hh(a, b, c, d, x[13], 4, K[40]) + d = hh(d, a, b, c, x[0], 11, K[41]) + c = hh(c, d, a, b, x[3], 16, K[42]) + b = hh(b, c, d, a, x[6], 23, K[43]) + a = hh(a, b, c, d, x[9], 4, K[44]) + d = hh(d, a, b, c, x[12], 11, K[45]) + c = hh(c, d, a, b, x[15], 16, K[46]) + b = hh(b, c, d, a, x[2], 23, K[47]) + + // Round 4 + a = ii(a, b, c, d, x[0], 6, K[48]) + d = ii(d, a, b, c, x[7], 10, K[49]) + c = ii(c, d, a, b, x[14], 15, K[50]) + b = ii(b, c, d, a, x[5], 21, K[51]) + a = ii(a, b, c, d, x[12], 6, K[52]) + d = ii(d, a, b, c, x[3], 10, K[53]) + c = ii(c, d, a, b, x[10], 15, K[54]) + b = ii(b, c, d, a, x[1], 21, K[55]) + a = ii(a, b, c, d, x[8], 6, K[56]) + d = ii(d, a, b, c, x[15], 10, K[57]) + c = ii(c, d, a, b, x[6], 15, K[58]) + b = ii(b, c, d, a, x[13], 21, K[59]) + a = ii(a, b, c, d, x[4], 6, K[60]) + d = ii(d, a, b, c, x[11], 10, K[61]) + c = ii(c, d, a, b, x[2], 15, K[62]) + b = ii(b, c, d, a, x[9], 21, K[63]) + + context.h[0] += a + context.h[1] += b + context.h[2] += c + context.h[3] += d + } + + private fun ByteArray.toHexString(): String { + val hexChars = CharArray(size * 2) + val hexArray = "0123456789abcdef".toCharArray() + for (i in indices) { + val v = this[i].toInt() and 0xFF + hexChars[i * 2] = hexArray[v ushr 4] + hexChars[i * 2 + 1] = hexArray[v and 0x0F] + } + return hexChars.concatToString() + } + + private fun rol32(a: Int, s: Int): Int = (a shl s) or (a ushr (32 - s)) + + private fun load32le(buf: ByteArray, offset: Int): Int { + return (buf[offset].toInt() and 0xFF) or + ((buf[offset + 1].toInt() and 0xFF) shl 8) or + ((buf[offset + 2].toInt() and 0xFF) shl 16) or + ((buf[offset + 3].toInt() and 0xFF) shl 24) + } + + private fun store32le(val32: Int, buf: ByteArray, offset: Int) { + buf[offset] = (val32 and 0xFF).toByte() + buf[offset + 1] = ((val32 ushr 8) and 0xFF).toByte() + buf[offset + 2] = ((val32 ushr 16) and 0xFF).toByte() + buf[offset + 3] = ((val32 ushr 24) and 0xFF).toByte() + } + + private fun f(x: Int, y: Int, z: Int): Int = (x and y) or (x.inv() and z) + private fun g(x: Int, y: Int, z: Int): Int = (x and z) or (y and z.inv()) + private fun h(x: Int, y: Int, z: Int): Int = x xor y xor z + private fun i(x: Int, y: Int, z: Int): Int = y xor (x or z.inv()) + + private fun ff(a: Int, b: Int, c: Int, d: Int, x: Int, s: Int, k: Int): Int = + rol32(a + f(b, c, d) + x + k, s) + b + + private fun gg(a: Int, b: Int, c: Int, d: Int, x: Int, s: Int, k: Int): Int = + rol32(a + g(b, c, d) + x + k, s) + b + + private fun hh(a: Int, b: Int, c: Int, d: Int, x: Int, s: Int, k: Int): Int = + rol32(a + h(b, c, d) + x + k, s) + b + + private fun ii(a: Int, b: Int, c: Int, d: Int, x: Int, s: Int, k: Int): Int = + rol32(a + i(b, c, d) + x + k, s) + b +} + +internal fun ByteArray.digest(): ByteArray = CycloneMd5.computeToBytes(this) \ No newline at end of file diff --git a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/util/nbt.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/nbt.kt similarity index 82% rename from libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/util/nbt.kt rename to libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/nbt.kt index 1c0c8a2..44a9223 100644 --- a/libmc-chat/src/commonMain/kotlin/cn/rtast/libmc/chat/util/nbt.kt +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/nbt.kt @@ -5,14 +5,14 @@ */ -package cn.rtast.libmc.chat.util +package cn.rtast.libmc.protocol.util -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer /** * tmp */ -internal fun _Buffer.writeMinimalTextNbt(text: String) { +internal fun BytesBuffer.writeMinimalTextNbt(text: String) { writeByte(0x0A) writeByte(0x08) val keyBytes = "text".encodeToByteArray() @@ -25,7 +25,7 @@ internal fun _Buffer.writeMinimalTextNbt(text: String) { writeByte(0x00) } -internal fun _Buffer.readMinimalTextNbt(): String { +internal fun BytesBuffer.readMinimalTextNbt(): String { val rootTagType = readByte().toInt() if (rootTagType != 0x0A) return "" var resultText = "" diff --git a/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/uuid.kt b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/uuid.kt new file mode 100644 index 0000000..c281c4a --- /dev/null +++ b/libmc-protocol/src/commonMain/kotlin/cn/rtast/libmc/protocol/util/uuid.kt @@ -0,0 +1,30 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package cn.rtast.libmc.protocol.util + +import kotlin.uuid.Uuid + + +/** + * translate from PHP code + * ref: https://gist.github.com/TuxCoding/2b6a00a8fc21fd3b88375f03c9e2e603 + */ +public fun generateOfflineUuid(username: String): Uuid { + val data = "OfflinePlayer:$username".encodeToByteArray().digest() + data[6] = ((data[6].toInt() and 0x0F) or 0x30).toByte() + data[8] = ((data[8].toInt() and 0x3F) or 0x80).toByte() + val hexChars = "0123456789abcdef" + val sb = StringBuilder(36) + for (i in 0 until 16) { + if (i == 4 || i == 6 || i == 8 || i == 10) sb.append('-') + val v = data[i].toInt() and 0xFF + sb.append(hexChars[v ushr 4]) + sb.append(hexChars[v and 0x0F]) + } + return Uuid.parse(sb.toString()) +} \ No newline at end of file diff --git a/libmc-protocol/src/commonTest/kotlin/test/TestClient.kt b/libmc-protocol/src/commonTest/kotlin/test/TestClient.kt new file mode 100644 index 0000000..6b0eec9 --- /dev/null +++ b/libmc-protocol/src/commonTest/kotlin/test/TestClient.kt @@ -0,0 +1,21 @@ +/* + * Copyright © 2026 RTAkland + * Author: RTAkland + * Date: 2026/9/4 + */ + + +package test + +import cn.rtast.libmc.protocol.client.MinecraftClient +import kotlinx.coroutines.test.runTest +import kotlin.test.Test + +class TestClient { + + @Test + fun `test client`() = runTest { + val cli = MinecraftClient("127.0.0.1", 25565, "123") + cli.connect() + } +} \ No newline at end of file diff --git a/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/buffer_ext.kt b/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/buffer_ext.kt index f3687a4..e3987ff 100644 --- a/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/buffer_ext.kt +++ b/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/buffer_ext.kt @@ -7,6 +7,6 @@ package cn.rtast.libmc.mcping.rconlib -import cn.rtast.libmc.common._Buffer +import cn.rtast.libmc.common.BytesBuffer -internal fun _Buffer.writeNull() = writeByte(0x00) \ No newline at end of file +internal fun BytesBuffer.writeNull() = writeByte(0x00) \ No newline at end of file diff --git a/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/packet.kt b/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/packet.kt index 41d9dec..0fdf528 100644 --- a/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/packet.kt +++ b/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/packet.kt @@ -8,9 +8,9 @@ package cn.rtast.libmc.mcping.rconlib import cn.rtast.libmc.common.ByteOrder -import cn.rtast.libmc.common._Buffer -import cn.rtast.libmc.common._ReadChannel -import cn.rtast.libmc.common._WriteChannel +import cn.rtast.libmc.common.BytesBuffer +import cn.rtast.libmc.common.ReadChannel +import cn.rtast.libmc.common.WriteChannel internal abstract class Packet { abstract val requestId: Int @@ -25,7 +25,7 @@ internal abstract class Packet { open val length: Int get() = 4 + 4 + payloadLength + 2 - fun writePayload(buf: _Buffer) { + fun writePayload(buf: BytesBuffer) { buf.writeInt(length, ByteOrder.LITTLE_ENDIAN) buf.writeInt(requestId, ByteOrder.LITTLE_ENDIAN) buf.writeInt(type, ByteOrder.LITTLE_ENDIAN) @@ -35,11 +35,11 @@ internal abstract class Packet { } companion object Codec { - fun decode(channel: _ReadChannel): ResponsePacket { + fun decode(channel: ReadChannel): ResponsePacket { val length = channel.readInt(ByteOrder.LITTLE_ENDIAN) val requestId = channel.readInt(ByteOrder.LITTLE_ENDIAN) val type = channel.readInt(ByteOrder.LITTLE_ENDIAN) - val payloadBuffer = _Buffer() + val payloadBuffer = BytesBuffer() while (true) { val b = channel.readByte() if (b == 0x00.toByte()) break @@ -73,11 +73,11 @@ internal data class ResponsePacket( ) : Packet() -internal fun _WriteChannel.sendPacket(packet: Packet) { - val buf = _Buffer() +internal fun WriteChannel.sendPacket(packet: Packet) { + val buf = BytesBuffer() packet.writePayload(buf) writeFully(buf.toByteArray()) flush() } -internal fun _ReadChannel.readPacket(): ResponsePacket = Packet.decode(this) \ No newline at end of file +internal fun ReadChannel.readPacket(): ResponsePacket = Packet.decode(this) \ No newline at end of file diff --git a/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/rconlib.kt b/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/rconlib.kt index 08757cb..f8ec539 100644 --- a/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/rconlib.kt +++ b/libmc-rconlib/src/commonMain/kotlin/cn/rtast/libmc/mcping/rconlib/rconlib.kt @@ -9,9 +9,9 @@ package cn.rtast.libmc.mcping.rconlib import cn.rtast.libmc.common.LibMCContext -import cn.rtast.libmc.common._ReadChannel -import cn.rtast.libmc.common._Socket -import cn.rtast.libmc.common._WriteChannel +import cn.rtast.libmc.common.ReadChannel +import cn.rtast.libmc.common.Socket +import cn.rtast.libmc.common.WriteChannel import kotlin.jvm.JvmName import kotlin.jvm.JvmOverloads @@ -20,13 +20,13 @@ public class RCONClient internal constructor( private val port: Int, private val context: LibMCContext, ) : AutoCloseable { - private var socket: _Socket? = null - private var readChannel: _ReadChannel? = null - private var writeChannel: _WriteChannel? = null + private var socket: Socket? = null + private var readChannel: ReadChannel? = null + private var writeChannel: WriteChannel? = null private var currentRequestId = 1 public fun connect(password: String): Boolean { - socket = _Socket(host, port, context) + socket = Socket(host, port, context) readChannel = socket!!.openReadChannel() writeChannel = socket!!.openWriteChannel() val authPacket = AuthPacket(password, currentRequestId) diff --git a/settings.gradle.kts b/settings.gradle.kts index a099062..cd3ecbb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,7 +6,7 @@ rootProject.name = "libmc" includeSubModule(":common") includeSubModule(":mcping") includeSubModule(":rconlib") -includeSubModule(":chat") +includeSubModule(":protocol") //includeSubModule(":nbt") fun includeSubModule(name: String) = include(name).also {