Add all packets handler

This commit is contained in:
2026-09-07 10:07:10 +08:00
parent d7300451f8
commit c0f9679090
25 files changed
+202 -105

No files matched your search

+2
View File
@@ -13,6 +13,8 @@ A lightweight minecraft client-side protocol library and related library, includ
# libmc-protocol # libmc-protocol
[Use libmc-protocol](https://repo.rtast.cn/packages/-/cn.rtast.libmc:protocol)
--- ---
# Minecraft Protocol Library Status & Roadmap # Minecraft Protocol Library Status & Roadmap
+1 -1
View File
@@ -7,7 +7,7 @@ allprojects {
group = "cn.rtast.libmc" group = "cn.rtast.libmc"
val libVersion = getProperty("libVersion") val libVersion = getProperty("libVersion")
version = when (name) { version = when (name) {
"protocol" -> "${getProperty("protocolVersion")}-$libVersion" "protocol" -> getProperty("protocolVersion")
else -> libVersion else -> libVersion
} }
+9 -23
View File
@@ -4,27 +4,13 @@
* **Auth Join Request** (`AuthenticationProvider`): Sends the HTTP POST request to Mojang's session server * **Auth Join Request** (`AuthenticationProvider`): Sends the HTTP POST request to Mojang's session server
(`https://sessionserver.mojang.com/session/minecraft/join`). (`https://sessionserver.mojang.com/session/minecraft/join`).
[Use libmc-protocol-encrypt](https://repo.rtast.cn/packages/-/cn.rtast.libmc:protocol-encrypt)
```kotlin ```kotlin
val client = createMinecraftClient( fun main() {
host = "127.0.0.1", val client = createMinecraftClient(
username = "Player", // other parameter
accessToken = accessToken crypto = DefaultProtocolContext
) { )
// 1. AES-128-CFB8 Cipher Implementation }
cipherFactory = { sharedKey -> JvmAesCipher(sharedKey) } ```
// 2. SHA-1 Hasher Implementation
sha1Hasher = Sha1Hasher { serverId, secretKey, publicKey ->
/* your SHA-1 implementation */
}
// 3. RSA-1024 Encryptor Implementation
rsaEncryptor = RSA1024Encryptor { publicKey, data ->
/* your RSA encryption implementation */
}
// 4. Mojang Auth HTTP Join Provider
authProvider = AuthenticationProvider { accessToken, uuid, serverIdHash ->
/* send HTTP POST to session server using your preferred HTTP client (Ktor, OkHttp, etc.) */
}
}
+2 -2
View File
@@ -3,5 +3,5 @@ kotlin.native.ignoreDisabledTargets=true
kotlin.native.enableKlibsCrossCompilation=true kotlin.native.enableKlibsCrossCompilation=true
kotlin.daemon.jvmargs=-Xmx2048M kotlin.daemon.jvmargs=-Xmx2048M
org.gradle.jvmargs=-Xmx3g -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx3g -Dfile.encoding=UTF-8
libVersion=0.1.0 libVersion=0.1.1
protocolVersion=26.2 protocolVersion=26.2-0.1.1
+3
View File
@@ -6,6 +6,7 @@ coroutines-test = "1.11.0"
kotlinx-coroutines = "1.11.0" kotlinx-coroutines = "1.11.0"
ktor-core = "3.5.2" ktor-core = "3.5.2"
cryptography-core = "0.6.0" cryptography-core = "0.6.0"
kotlinx-atomicfu = "0.33.0"
#kotlinx-serialization = "1.11.0" #kotlinx-serialization = "1.11.0"
[libraries] [libraries]
@@ -20,10 +21,12 @@ ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "kto
ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor-core" } ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor-core" }
ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor-core" } ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor-core" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor-core" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor-core" }
kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version.ref = "kotlinx-atomicfu" }
#kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" } #kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
#kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } #kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
[plugins] [plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
maven-publish = { id = "maven-publish" } maven-publish = { id = "maven-publish" }
kotlinx-atomicfu = { id = "org.jetbrains.kotlinx.atomicfu", version.ref = "kotlinx-atomicfu" }
#kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } #kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
@@ -15,9 +15,7 @@ public class PacketRegistry {
private val idToCodec = mutableMapOf<Int, PacketCodec<out MinecraftPacket>>() private val idToCodec = mutableMapOf<Int, PacketCodec<out MinecraftPacket>>()
private val classToInfo = mutableMapOf<KClass<out MinecraftPacket>, RegisteredPacket<*>>() private val classToInfo = mutableMapOf<KClass<out MinecraftPacket>, RegisteredPacket<*>>()
private data class RegisteredPacket<P : MinecraftPacket>( private data class RegisteredPacket<P : MinecraftPacket>(val id: Int, val codec: PacketCodec<P>)
val id: Int, val codec: PacketCodec<P>,
)
public fun <T : MinecraftPacket> register(id: Int, kClass: KClass<T>, codec: PacketCodec<T>) { public fun <T : MinecraftPacket> register(id: Int, kClass: KClass<T>, codec: PacketCodec<T>) {
idToCodec[id] = codec idToCodec[id] = codec
@@ -40,8 +40,3 @@ public expect class BytesBuffer {
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE")
public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this) public inline fun ByteArray.wrap(): BytesBuffer = BytesBuffer(this)
public suspend fun ReadChannel.readPacketFrame(): BytesBuffer {
val length = this.readVarInt()
return this.readBytes(length).wrap()
}
@@ -7,6 +7,9 @@
package cn.rtast.libmc.stream package cn.rtast.libmc.stream
/**
* Platform specified raw byte read channel
*/
public expect open class ReadChannel() { public expect open class ReadChannel() {
public open suspend fun readByte(): Byte public open suspend fun readByte(): Byte
public open suspend fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short public open suspend fun readShort(endian: ByteOrder = ByteOrder.BIG_ENDIAN): Short
@@ -16,21 +19,10 @@ public expect open class ReadChannel() {
public open suspend fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size) public open suspend fun readFully(out: ByteArray, start: Int = 0, end: Int = out.size)
} }
/**
* Platform specified raw byte write channel
*/
public expect open class WriteChannel() { public expect open class WriteChannel() {
public open suspend fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size) public open suspend fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
public open suspend fun flush() public open suspend fun flush()
}
public suspend fun ReadChannel.readVarInt(): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = this.readByte()
val value = (read.toInt() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
} while ((read.toInt() and 0x80) != 0)
return result
} }
@@ -6,7 +6,7 @@
package cn.rtast.libmc.stream package cn.rtast.libmc.stream
import cn.rtast.libmc.common.LibMCContext import cn.rtast.libmc.LibMCContext
import io.ktor.network.sockets.* import io.ktor.network.sockets.*
import io.ktor.utils.io.core.* import io.ktor.utils.io.core.*
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@@ -7,7 +7,7 @@
package cn.rtast.libmc.mcping.bedrock package cn.rtast.libmc.mcping.bedrock
import cn.rtast.libmc.common.LibMCContext import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.stream.UdpSocket import cn.rtast.libmc.stream.UdpSocket
import cn.rtast.libmc.stream.wrap import cn.rtast.libmc.stream.wrap
import cn.rtast.libmc.mcping.PingResponse import cn.rtast.libmc.mcping.PingResponse
@@ -0,0 +1,31 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/9/7
*/
package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.stream.BytesBuffer
import cn.rtast.libmc.stream.ReadChannel
import cn.rtast.libmc.stream.wrap
private suspend fun ReadChannel.readVarInt(): Int {
var numRead = 0
var result = 0
var read: Byte
do {
read = this.readByte()
val value = (read.toInt() and 0x7F)
result = result or (value shl (7 * numRead))
numRead++
if (numRead > 5) throw IllegalArgumentException("VarInt is too big")
} while ((read.toInt() and 0x80) != 0)
return result
}
public suspend fun ReadChannel.readPacketFrame(): BytesBuffer {
val length = this.readVarInt()
return this.readBytes(length).wrap()
}
@@ -7,13 +7,12 @@
package cn.rtast.libmc.mcping.java package cn.rtast.libmc.mcping.java
import cn.rtast.libmc.common.* import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.mcping.PingResponse
import cn.rtast.libmc.mcping.sendPacket
import cn.rtast.libmc.primitives.McStringCodec import cn.rtast.libmc.primitives.McStringCodec
import cn.rtast.libmc.primitives.VarIntCodec import cn.rtast.libmc.primitives.VarIntCodec
import cn.rtast.libmc.stream.Socket import cn.rtast.libmc.stream.Socket
import cn.rtast.libmc.stream.readPacketFrame
import cn.rtast.libmc.mcping.PingResponse
import cn.rtast.libmc.mcping.sendPacket
import kotlin.time.Clock import kotlin.time.Clock
internal suspend fun pingJavaServer(host: String, port: Int, context: LibMCContext): PingResponse { internal suspend fun pingJavaServer(host: String, port: Int, context: LibMCContext): PingResponse {
@@ -9,7 +9,7 @@
package cn.rtast.libmc.mcping package cn.rtast.libmc.mcping
import cn.rtast.libmc.common.LibMCContext import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.mcping.bedrock.pingBedrockServer import cn.rtast.libmc.mcping.bedrock.pingBedrockServer
import cn.rtast.libmc.mcping.java.pingJavaServer import cn.rtast.libmc.mcping.java.pingJavaServer
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
@@ -33,7 +33,7 @@ class TestJvmClient {
) )
// cli.on<ClientboundSystemChatMessagePacket> { println(it) } // cli.on<ClientboundSystemChatMessagePacket> { println(it) }
// cli.on<ClientboundLoginSuccessPacket> { println(it) } // cli.on<ClientboundLoginSuccessPacket> { println(it) }
cli.on<MinecraftPacket> { println(it) } cli.onPacket<MinecraftPacket> { println(it) }
cli.launch { cli.connect() } cli.launch { cli.connect() }
while (true) { while (true) {
} }
+14 -1
View File
@@ -22,11 +22,24 @@ kotlin {
commonTest.dependencies { commonTest.dependencies {
implementation(kotlin("test")) implementation(kotlin("test"))
implementation(project(":protocol-encrypt"))
implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.coroutines.test)
} }
jvmTest.dependencies { jvmTest.dependencies {
implementation(project(":protocol-encrypt")) implementation(libs.ktor.client.okhttp)
}
linuxTest.dependencies {
implementation(libs.ktor.client.curl)
}
mingwTest.dependencies {
implementation(libs.ktor.client.winhttp)
}
appleTest.dependencies {
implementation(libs.ktor.client.darwin)
} }
} }
} }
@@ -39,11 +39,7 @@ public class MinecraftClient internal constructor(
internal val stateMachine = ClientStateMachine() internal val stateMachine = ClientStateMachine()
public val networkChannel: NetworkChannel = NetworkChannel( public val networkChannel: NetworkChannel = NetworkChannel(
host = host, host, port, context, stateMachine, cryptoContext.cipherFactory, this
port = port,
context = context,
stateMachine = stateMachine,
cipherProvider = cryptoContext.cipherFactory
) )
private val internalPacketDispatcher = InternalPacketDispatcher(this, authProvider) private val internalPacketDispatcher = InternalPacketDispatcher(this, authProvider)
@@ -57,13 +53,18 @@ public class MinecraftClient internal constructor(
networkChannel.connect() networkChannel.connect()
startListening() startListening()
networkChannel.sendPacket( networkChannel.sendPacket(
ServerboundHandshakePacket(protocolVersion, host, port.toUShort(), HandshakeIntent.LOGIN) ServerboundHandshakePacket(
protocolVersion,
host, port.toUShort(),
HandshakeIntent.LOGIN
)
) )
stateMachine.transitionTo(ProtocolState.LOGIN) stateMachine.transitionTo(ProtocolState.LOGIN)
networkChannel.sendPacket(ServerboundLoginStartPacket(username, uuid)) networkChannel.sendPacket(ServerboundLoginStartPacket(username, uuid))
} }
public fun setCompression(threshold: Int): Unit = networkChannel.setCompression(threshold) public fun setCompression(threshold: Int): Unit = networkChannel.setCompression(threshold)
private fun startListening() { private fun startListening() {
listenJob = launch { listenJob = launch {
try { try {
@@ -28,13 +28,16 @@ import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundPongPlayPacket
import cn.rtast.libmc.protocol.protocol.state.ProtocolState import cn.rtast.libmc.protocol.protocol.state.ProtocolState
import cn.rtast.libmc.protocol.util.generateRandom16Bytes import cn.rtast.libmc.protocol.util.generateRandom16Bytes
/**
* Internal simple state machine trigger,
* Auto respond packets the server needed.
* Only including `Handshake`, `Login` and `Configuration` State
*/
public class InternalPacketDispatcher( public class InternalPacketDispatcher(
private val client: MinecraftClient, private val client: MinecraftClient,
private val authProvider: AuthenticationProvider, private val authProvider: AuthenticationProvider,
) { ) {
private suspend fun dispatchEvent(packet: MinecraftPacket) = client.dispatch(packet)
public suspend fun handleIncomingPackets(packet: MinecraftPacket) { public suspend fun handleIncomingPackets(packet: MinecraftPacket) {
this.dispatchEvent(packet)
when (packet) { when (packet) {
// login // login
is ClientboundDisconnectLoginPacket -> client.close() is ClientboundDisconnectLoginPacket -> client.close()
@@ -4,23 +4,77 @@
* Date: 2026/9/5 * Date: 2026/9/5
*/ */
package cn.rtast.libmc.protocol.event package cn.rtast.libmc.protocol.event
import cn.rtast.libmc.packet.MinecraftPacket import cn.rtast.libmc.packet.MinecraftPacket
import cn.rtast.libmc.protocol.protocol.PacketDirection
import kotlin.concurrent.Volatile
import kotlin.reflect.KClass import kotlin.reflect.KClass
public open class PacketEventDispatcher { private typealias Handler = suspend (MinecraftPacket) -> Unit
@PublishedApi private typealias DirectionalHandler = suspend (MinecraftPacket, PacketDirection) -> Unit
internal val eventHandlers: MutableMap<KClass<out MinecraftPacket>, MutableList<suspend (MinecraftPacket) -> Unit>> =
mutableMapOf()
internal suspend fun dispatch(event: MinecraftPacket) { public open class PacketEventDispatcher {
eventHandlers[event::class]?.forEach { it.invoke(event) } @Volatile
@PublishedApi
internal var receiveHandlers: Map<KClass<out MinecraftPacket>, List<Handler>> = emptyMap()
@Volatile
@PublishedApi
internal var sentHandlers: Map<KClass<out MinecraftPacket>, List<Handler>> = emptyMap()
@Volatile
@PublishedApi
internal var globalHandlers: List<DirectionalHandler> = emptyList()
@PublishedApi
internal fun <T : MinecraftPacket> addTypedHandler(
isReceive: Boolean,
key: KClass<T>,
handler: Handler,
) {
if (isReceive) {
val current = receiveHandlers[key] ?: emptyList()
receiveHandlers = receiveHandlers + (key to (current + handler))
} else {
val current = sentHandlers[key] ?: emptyList()
sentHandlers = sentHandlers + (key to (current + handler))
}
} }
public inline fun <reified T : MinecraftPacket> on(crossinline block: suspend (T) -> Unit) { private suspend fun dispatch(
val handlers = eventHandlers.getOrPut(T::class) { mutableListOf() } handlersMap: Map<KClass<out MinecraftPacket>, List<Handler>>,
handlers.add { event -> block(event as T) } packet: MinecraftPacket,
direction: PacketDirection,
) {
handlersMap[packet::class]?.forEach { handler -> handler(packet) }
globalHandlers.forEach { handler -> handler(packet, direction) }
}
internal suspend fun dispatchReceive(packet: MinecraftPacket) =
dispatch(receiveHandlers, packet, PacketDirection.CLIENTBOUND)
internal suspend fun dispatchSent(packet: MinecraftPacket) =
dispatch(sentHandlers, packet, PacketDirection.SERVERBOUND)
/**
* Lambda will be invoked when received a packet
*/
public inline fun <reified T : MinecraftPacket> onPacket(crossinline block: suspend (T) -> Unit) {
addTypedHandler(true, T::class) { block(it as T) }
}
/**
* Lambda will be invoked after a packet sent
*/
public inline fun <reified T : MinecraftPacket> onSent(crossinline block: suspend (T) -> Unit) {
addTypedHandler(false, T::class) { block(it as T) }
}
/**
* All packets will be appeared here, including `Outbound(Serverbound)` and `Inbound(Clientbound)` packet
*/
public fun on(block: suspend (packet: MinecraftPacket, direction: PacketDirection) -> Unit) {
globalHandlers = globalHandlers + block
} }
} }
@@ -11,6 +11,9 @@ import cn.rtast.libmc.crypto.NetworkCipher
import cn.rtast.libmc.stream.ReadChannel import cn.rtast.libmc.stream.ReadChannel
import cn.rtast.libmc.stream.WriteChannel import cn.rtast.libmc.stream.WriteChannel
/**
* AES-128-CFB8 ***ciphered*** read channel
*/
internal class CipherReadChannel( internal class CipherReadChannel(
private val delegate: ReadChannel, private val delegate: ReadChannel,
private val crypto: NetworkCipher, private val crypto: NetworkCipher,
@@ -34,6 +37,9 @@ internal class CipherReadChannel(
} }
} }
/**
* AES-128-CFB8 ***ciphered*** write channel
*/
internal class CipherWriteChannel( internal class CipherWriteChannel(
private val delegate: WriteChannel, private val delegate: WriteChannel,
private val crypto: NetworkCipher, private val crypto: NetworkCipher,
@@ -13,6 +13,7 @@ import cn.rtast.libmc.packet.writeBuffer
import cn.rtast.libmc.primitives.readVarInt import cn.rtast.libmc.primitives.readVarInt
import cn.rtast.libmc.primitives.writeVarInt import cn.rtast.libmc.primitives.writeVarInt
import cn.rtast.libmc.protocol.client.ClientStateMachine import cn.rtast.libmc.protocol.client.ClientStateMachine
import cn.rtast.libmc.protocol.event.PacketEventDispatcher
import cn.rtast.libmc.protocol.protocol.GameProtocols import cn.rtast.libmc.protocol.protocol.GameProtocols
import cn.rtast.libmc.stream.BytesBuffer import cn.rtast.libmc.stream.BytesBuffer
import cn.rtast.libmc.stream.wrap import cn.rtast.libmc.stream.wrap
@@ -26,6 +27,7 @@ public class NetworkChannel internal constructor(
context: LibMCContext, context: LibMCContext,
private val stateMachine: ClientStateMachine, private val stateMachine: ClientStateMachine,
cipherProvider: (ByteArray) -> NetworkCipher, cipherProvider: (ByteArray) -> NetworkCipher,
private val dispatcher: PacketEventDispatcher,
) { ) {
internal val session: NetworkSession = NetworkSession(host, port, context, cipherProvider) internal val session: NetworkSession = NetworkSession(host, port, context, cipherProvider)
@@ -35,6 +37,11 @@ public class NetworkChannel internal constructor(
public fun connect(): Unit = session.connect() public fun connect(): Unit = session.connect()
public fun setCompression(threshold: Int): Unit = run { this.threshold = threshold } public fun setCompression(threshold: Int): Unit = run { this.threshold = threshold }
/**
* This function receive all inbound packets, and dispatch as a packet event
* See [PacketEventDispatcher.dispatchReceive],
* Use [PacketEventDispatcher.onPacket] to get packet event
*/
public suspend fun readNextPacket(): MinecraftPacket { public suspend fun readNextPacket(): MinecraftPacket {
val packetLength = session.readVarInt() val packetLength = session.readVarInt()
val rawFrameBytes = session.readBytes(packetLength) val rawFrameBytes = session.readBytes(packetLength)
@@ -46,11 +53,18 @@ public class NetworkChannel internal constructor(
} }
val currentState = stateMachine.currentState val currentState = stateMachine.currentState
val packetId = payloadBuf.readVarInt() val packetId = payloadBuf.readVarInt()
return GameProtocols.clientboundGameProtocols val packet = GameProtocols.clientboundGameProtocols
.getRegistry(currentState) .getRegistry(currentState)
.decodePacket(packetId, payloadBuf) .decodePacket(packetId, payloadBuf)
dispatcher.dispatchReceive(packet)
return packet
} }
/**
* This function will dispatch a packet event when packet was sent.
* See [PacketEventDispatcher.dispatchSent],
* Use [PacketEventDispatcher.onSent] to get packet event
*/
public suspend fun sendPacket(packet: MinecraftPacket) { public suspend fun sendPacket(packet: MinecraftPacket) {
val uncompressedBodyBuf = BytesBuffer() val uncompressedBodyBuf = BytesBuffer()
GameProtocols.serverboundGameProtocols GameProtocols.serverboundGameProtocols
@@ -75,6 +89,7 @@ public class NetworkChannel internal constructor(
frameBuffer.writeBuffer(contentBuf) frameBuffer.writeBuffer(contentBuf)
} }
session.writeFully(frameBuffer.toByteArray()) session.writeFully(frameBuffer.toByteArray())
dispatcher.dispatchSent(packet)
} }
public fun close(): Unit = session.close() public fun close(): Unit = session.close()
@@ -7,6 +7,6 @@
package cn.rtast.libmc.protocol.protocol package cn.rtast.libmc.protocol.protocol
internal enum class PacketDirection { public enum class PacketDirection {
SERVERBOUND, CLIENTBOUND SERVERBOUND, CLIENTBOUND
} }
@@ -8,7 +8,6 @@
package test package test
import cn.rtast.libmc.protocol.client.createMinecraftClient import cn.rtast.libmc.protocol.client.createMinecraftClient
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundAwardStatisticsPacket
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlin.test.Test import kotlin.test.Test
@@ -28,7 +27,7 @@ class TestClient {
// rsaEncryptor = RSA1024Encryptor { data, sharedKey -> } // rsaEncryptor = RSA1024Encryptor { data, sharedKey -> }
} }
cli.launch { cli.connect() } cli.launch { cli.connect() }
cli.on<ClientboundAwardStatisticsPacket> { println(it) } cli.on { packet, direction -> println("${direction} -> $packet") }
while (true) { while (true) {
} }
} }
@@ -7,12 +7,11 @@
package test package test
import cn.rtast.libmc.protocol.client.createMinecraftClient import cn.rtast.libmc.crypto.AuthenticationProvider
import cn.rtast.libmc.crypto.RSA1024Encryptor import cn.rtast.libmc.crypto.RSA1024Encryptor
import cn.rtast.libmc.crypto.Sha1Hasher import cn.rtast.libmc.crypto.Sha1Hasher
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundLoginSuccessPacket import cn.rtast.libmc.protocol.client.createMinecraftClient
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundSystemChatMessagePacket import cn.rtast.libmc.protocol.crypto.DefaultProtocolContext
import cn.rtast.libmc.crypto.AuthenticationProvider
import cn.rtast.libmc.protocol.util.generateOfflineUuid import cn.rtast.libmc.protocol.util.generateOfflineUuid
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.junit.Test import org.junit.Test
@@ -28,7 +27,6 @@ import kotlin.uuid.Uuid
class TestClientTestInJvm { class TestClientTestInJvm {
val accessToken = File("src/jvmTest/resources/accessToken.txt").readText() val accessToken = File("src/jvmTest/resources/accessToken.txt").readText()
fun encrypt(publicKeyBytes: ByteArray, data: ByteArray): ByteArray { fun encrypt(publicKeyBytes: ByteArray, data: ByteArray): ByteArray {
@@ -63,25 +61,27 @@ class TestClientTestInJvm {
// generateOfflineUuid("RTAkland"), // generateOfflineUuid("RTAkland"),
Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"), Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"),
// null, // null,
accessToken accessToken,
) { crypto = DefaultProtocolContext
rsaEncryptor = RSA1024Encryptor { key, data -> encrypt(key, data) } )
sha1Hasher = // {
Sha1Hasher { serverId, secretKey, publicKey -> minecraftServerIdHash(serverId, secretKey, publicKey) } // rsaEncryptor = RSA1024Encryptor { key, data -> encrypt(key, data) }
cipherFactory = { key -> JvmAesCipher(key) } // sha1Hasher =
// Sha1Hasher { serverId, secretKey, publicKey -> minecraftServerIdHash(serverId, secretKey, publicKey) }
authProvider = AuthenticationProvider { url, accessToken, uuid, serverIdHash -> // cipherFactory = { key -> JvmAesCipher(key) }
val connection = URL(url).openConnection() as HttpURLConnection // authProvider = AuthenticationProvider { url, accessToken, uuid, serverIdHash ->
connection.requestMethod = "POST" // val connection = URL(url).openConnection() as HttpURLConnection
connection.doOutput = true // connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json") // connection.doOutput = true
connection.getOutputStream() // connection.setRequestProperty("Content-Type", "application/json")
.use { it.write("{\"accessToken\":\"$accessToken\", \"selectedProfile\":\"$uuid\", \"serverId\":\"$serverIdHash\"}".encodeToByteArray()) } // connection.getOutputStream()
connection.disconnect() // .use { it.write("{\"accessToken\":\"$accessToken\", \"selectedProfile\":\"$uuid\", \"serverId\":\"$serverIdHash\"}".encodeToByteArray()) }
} // connection.disconnect()
} // }
cli.on<ClientboundSystemChatMessagePacket> { println(it) } // }
cli.on<ClientboundLoginSuccessPacket> { println(it) } // cli.onPacket<ClientboundSystemChatMessagePacket> { println(it) }
// cli.onPacket<ClientboundLoginSuccessPacket> { println(it) }
cli.on { packet, direction -> println("${direction} -> $packet") }
cli.launch { cli.connect() } cli.launch { cli.connect() }
while (true) { while (true) {
} }
@@ -8,7 +8,7 @@
package cn.rtast.libmc.mcping.rconlib package cn.rtast.libmc.mcping.rconlib
import cn.rtast.libmc.common.LibMCContext import cn.rtast.libmc.LibMCContext
import cn.rtast.libmc.stream.ReadChannel import cn.rtast.libmc.stream.ReadChannel
import cn.rtast.libmc.stream.Socket import cn.rtast.libmc.stream.Socket
import cn.rtast.libmc.stream.WriteChannel import cn.rtast.libmc.stream.WriteChannel
+1 -1
View File
@@ -10,7 +10,7 @@ includeSubModule(":rconlib")
includeSubModule(":protocol") includeSubModule(":protocol")
includeSubModule(":protocol-encrypt") includeSubModule(":protocol-encrypt")
includeSubModule(":nbt") includeSubModule(":nbt")
includeSubModule(":snbt") //includeSubModule(":snbt")
fun includeSubModule(name: String, path: String? = null) = include(name).also { fun includeSubModule(name: String, path: String? = null) = include(name).also {
project(name).projectDir = file(path ?: "libmc-${name.removePrefix(":")}") project(name).projectDir = file(path ?: "libmc-${name.removePrefix(":")}")