Add all packets handler
This commit is contained in:
25 files changed
+202
-105
No files matched your search
@@ -13,6 +13,8 @@ A lightweight minecraft client-side protocol library and related library, includ
|
||||
|
||||
# libmc-protocol
|
||||
|
||||
[Use libmc-protocol](https://repo.rtast.cn/packages/-/cn.rtast.libmc:protocol)
|
||||
|
||||
---
|
||||
|
||||
# Minecraft Protocol Library Status & Roadmap
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ allprojects {
|
||||
group = "cn.rtast.libmc"
|
||||
val libVersion = getProperty("libVersion")
|
||||
version = when (name) {
|
||||
"protocol" -> "${getProperty("protocolVersion")}-$libVersion"
|
||||
"protocol" -> getProperty("protocolVersion")
|
||||
else -> libVersion
|
||||
}
|
||||
|
||||
|
||||
@@ -4,27 +4,13 @@
|
||||
* **Auth Join Request** (`AuthenticationProvider`): Sends the HTTP POST request to Mojang's session server
|
||||
(`https://sessionserver.mojang.com/session/minecraft/join`).
|
||||
|
||||
[Use libmc-protocol-encrypt](https://repo.rtast.cn/packages/-/cn.rtast.libmc:protocol-encrypt)
|
||||
|
||||
```kotlin
|
||||
val client = createMinecraftClient(
|
||||
host = "127.0.0.1",
|
||||
username = "Player",
|
||||
accessToken = accessToken
|
||||
) {
|
||||
// 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.) */
|
||||
}
|
||||
}
|
||||
fun main() {
|
||||
val client = createMinecraftClient(
|
||||
// other parameter
|
||||
crypto = DefaultProtocolContext
|
||||
)
|
||||
}
|
||||
```
|
||||
+2
-2
@@ -3,5 +3,5 @@ kotlin.native.ignoreDisabledTargets=true
|
||||
kotlin.native.enableKlibsCrossCompilation=true
|
||||
kotlin.daemon.jvmargs=-Xmx2048M
|
||||
org.gradle.jvmargs=-Xmx3g -Dfile.encoding=UTF-8
|
||||
libVersion=0.1.0
|
||||
protocolVersion=26.2
|
||||
libVersion=0.1.1
|
||||
protocolVersion=26.2-0.1.1
|
||||
@@ -6,6 +6,7 @@ coroutines-test = "1.11.0"
|
||||
kotlinx-coroutines = "1.11.0"
|
||||
ktor-core = "3.5.2"
|
||||
cryptography-core = "0.6.0"
|
||||
kotlinx-atomicfu = "0.33.0"
|
||||
#kotlinx-serialization = "1.11.0"
|
||||
|
||||
[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-curl = { module = "io.ktor:ktor-client-curl", 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-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
|
||||
|
||||
[plugins]
|
||||
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
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" }
|
||||
@@ -15,9 +15,7 @@ public class PacketRegistry {
|
||||
private val idToCodec = mutableMapOf<Int, PacketCodec<out MinecraftPacket>>()
|
||||
private val classToInfo = mutableMapOf<KClass<out MinecraftPacket>, RegisteredPacket<*>>()
|
||||
|
||||
private data class RegisteredPacket<P : MinecraftPacket>(
|
||||
val id: Int, val codec: PacketCodec<P>,
|
||||
)
|
||||
private data class RegisteredPacket<P : MinecraftPacket>(val id: Int, val codec: PacketCodec<P>)
|
||||
|
||||
public fun <T : MinecraftPacket> register(id: Int, kClass: KClass<T>, codec: PacketCodec<T>) {
|
||||
idToCodec[id] = codec
|
||||
|
||||
@@ -40,8 +40,3 @@ public expect class BytesBuffer {
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
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
|
||||
|
||||
/**
|
||||
* Platform specified raw byte read channel
|
||||
*/
|
||||
public expect open class ReadChannel() {
|
||||
public open suspend fun readByte(): Byte
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform specified raw byte write channel
|
||||
*/
|
||||
public expect open class WriteChannel() {
|
||||
public open suspend fun writeFully(value: ByteArray, startIndex: Int = 0, endIndex: Int = value.size)
|
||||
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
|
||||
|
||||
import cn.rtast.libmc.common.LibMCContext
|
||||
import cn.rtast.libmc.LibMCContext
|
||||
import io.ktor.network.sockets.*
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
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.wrap
|
||||
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
|
||||
|
||||
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.VarIntCodec
|
||||
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
|
||||
|
||||
internal suspend fun pingJavaServer(host: String, port: Int, context: LibMCContext): PingResponse {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
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.java.pingJavaServer
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
@@ -33,7 +33,7 @@ class TestJvmClient {
|
||||
)
|
||||
// cli.on<ClientboundSystemChatMessagePacket> { println(it) }
|
||||
// cli.on<ClientboundLoginSuccessPacket> { println(it) }
|
||||
cli.on<MinecraftPacket> { println(it) }
|
||||
cli.onPacket<MinecraftPacket> { println(it) }
|
||||
cli.launch { cli.connect() }
|
||||
while (true) {
|
||||
}
|
||||
|
||||
@@ -22,11 +22,24 @@ kotlin {
|
||||
|
||||
commonTest.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(project(":protocol-encrypt"))
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -39,11 +39,7 @@ public class MinecraftClient internal constructor(
|
||||
internal val stateMachine = ClientStateMachine()
|
||||
|
||||
public val networkChannel: NetworkChannel = NetworkChannel(
|
||||
host = host,
|
||||
port = port,
|
||||
context = context,
|
||||
stateMachine = stateMachine,
|
||||
cipherProvider = cryptoContext.cipherFactory
|
||||
host, port, context, stateMachine, cryptoContext.cipherFactory, this
|
||||
)
|
||||
|
||||
private val internalPacketDispatcher = InternalPacketDispatcher(this, authProvider)
|
||||
@@ -57,13 +53,18 @@ public class MinecraftClient internal constructor(
|
||||
networkChannel.connect()
|
||||
startListening()
|
||||
networkChannel.sendPacket(
|
||||
ServerboundHandshakePacket(protocolVersion, host, port.toUShort(), HandshakeIntent.LOGIN)
|
||||
ServerboundHandshakePacket(
|
||||
protocolVersion,
|
||||
host, port.toUShort(),
|
||||
HandshakeIntent.LOGIN
|
||||
)
|
||||
)
|
||||
stateMachine.transitionTo(ProtocolState.LOGIN)
|
||||
networkChannel.sendPacket(ServerboundLoginStartPacket(username, uuid))
|
||||
}
|
||||
|
||||
public fun setCompression(threshold: Int): Unit = networkChannel.setCompression(threshold)
|
||||
|
||||
private fun startListening() {
|
||||
listenJob = launch {
|
||||
try {
|
||||
|
||||
+5
-2
@@ -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.util.generateRandom16Bytes
|
||||
|
||||
/**
|
||||
* Internal simple state machine trigger,
|
||||
* Auto respond packets the server needed.
|
||||
* Only including `Handshake`, `Login` and `Configuration` State
|
||||
*/
|
||||
public class InternalPacketDispatcher(
|
||||
private val client: MinecraftClient,
|
||||
private val authProvider: AuthenticationProvider,
|
||||
) {
|
||||
private suspend fun dispatchEvent(packet: MinecraftPacket) = client.dispatch(packet)
|
||||
public suspend fun handleIncomingPackets(packet: MinecraftPacket) {
|
||||
this.dispatchEvent(packet)
|
||||
when (packet) {
|
||||
// login
|
||||
is ClientboundDisconnectLoginPacket -> client.close()
|
||||
|
||||
+64
-10
@@ -4,23 +4,77 @@
|
||||
* Date: 2026/9/5
|
||||
*/
|
||||
|
||||
|
||||
package cn.rtast.libmc.protocol.event
|
||||
|
||||
import cn.rtast.libmc.packet.MinecraftPacket
|
||||
import cn.rtast.libmc.protocol.protocol.PacketDirection
|
||||
import kotlin.concurrent.Volatile
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public open class PacketEventDispatcher {
|
||||
@PublishedApi
|
||||
internal val eventHandlers: MutableMap<KClass<out MinecraftPacket>, MutableList<suspend (MinecraftPacket) -> Unit>> =
|
||||
mutableMapOf()
|
||||
private typealias Handler = suspend (MinecraftPacket) -> Unit
|
||||
private typealias DirectionalHandler = suspend (MinecraftPacket, PacketDirection) -> Unit
|
||||
|
||||
internal suspend fun dispatch(event: MinecraftPacket) {
|
||||
eventHandlers[event::class]?.forEach { it.invoke(event) }
|
||||
public open class PacketEventDispatcher {
|
||||
@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) {
|
||||
val handlers = eventHandlers.getOrPut(T::class) { mutableListOf() }
|
||||
handlers.add { event -> block(event as T) }
|
||||
private suspend fun dispatch(
|
||||
handlersMap: Map<KClass<out MinecraftPacket>, List<Handler>>,
|
||||
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.WriteChannel
|
||||
|
||||
/**
|
||||
* AES-128-CFB8 ***ciphered*** read channel
|
||||
*/
|
||||
internal class CipherReadChannel(
|
||||
private val delegate: ReadChannel,
|
||||
private val crypto: NetworkCipher,
|
||||
@@ -34,6 +37,9 @@ internal class CipherReadChannel(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AES-128-CFB8 ***ciphered*** write channel
|
||||
*/
|
||||
internal class CipherWriteChannel(
|
||||
private val delegate: WriteChannel,
|
||||
private val crypto: NetworkCipher,
|
||||
|
||||
+16
-1
@@ -13,6 +13,7 @@ import cn.rtast.libmc.packet.writeBuffer
|
||||
import cn.rtast.libmc.primitives.readVarInt
|
||||
import cn.rtast.libmc.primitives.writeVarInt
|
||||
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.stream.BytesBuffer
|
||||
import cn.rtast.libmc.stream.wrap
|
||||
@@ -26,6 +27,7 @@ public class NetworkChannel internal constructor(
|
||||
context: LibMCContext,
|
||||
private val stateMachine: ClientStateMachine,
|
||||
cipherProvider: (ByteArray) -> NetworkCipher,
|
||||
private val dispatcher: PacketEventDispatcher,
|
||||
) {
|
||||
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 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 {
|
||||
val packetLength = session.readVarInt()
|
||||
val rawFrameBytes = session.readBytes(packetLength)
|
||||
@@ -46,11 +53,18 @@ public class NetworkChannel internal constructor(
|
||||
}
|
||||
val currentState = stateMachine.currentState
|
||||
val packetId = payloadBuf.readVarInt()
|
||||
return GameProtocols.clientboundGameProtocols
|
||||
val packet = GameProtocols.clientboundGameProtocols
|
||||
.getRegistry(currentState)
|
||||
.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) {
|
||||
val uncompressedBodyBuf = BytesBuffer()
|
||||
GameProtocols.serverboundGameProtocols
|
||||
@@ -75,6 +89,7 @@ public class NetworkChannel internal constructor(
|
||||
frameBuffer.writeBuffer(contentBuf)
|
||||
}
|
||||
session.writeFully(frameBuffer.toByteArray())
|
||||
dispatcher.dispatchSent(packet)
|
||||
}
|
||||
|
||||
public fun close(): Unit = session.close()
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@
|
||||
|
||||
package cn.rtast.libmc.protocol.protocol
|
||||
|
||||
internal enum class PacketDirection {
|
||||
public enum class PacketDirection {
|
||||
SERVERBOUND, CLIENTBOUND
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
package test
|
||||
|
||||
import cn.rtast.libmc.protocol.client.createMinecraftClient
|
||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundAwardStatisticsPacket
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
@@ -28,7 +27,7 @@ class TestClient {
|
||||
// rsaEncryptor = RSA1024Encryptor { data, sharedKey -> }
|
||||
}
|
||||
cli.launch { cli.connect() }
|
||||
cli.on<ClientboundAwardStatisticsPacket> { println(it) }
|
||||
cli.on { packet, direction -> println("${direction} -> $packet") }
|
||||
while (true) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
|
||||
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.Sha1Hasher
|
||||
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundLoginSuccessPacket
|
||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundSystemChatMessagePacket
|
||||
import cn.rtast.libmc.crypto.AuthenticationProvider
|
||||
import cn.rtast.libmc.protocol.client.createMinecraftClient
|
||||
import cn.rtast.libmc.protocol.crypto.DefaultProtocolContext
|
||||
import cn.rtast.libmc.protocol.util.generateOfflineUuid
|
||||
import kotlinx.coroutines.launch
|
||||
import org.junit.Test
|
||||
@@ -28,7 +27,6 @@ import kotlin.uuid.Uuid
|
||||
|
||||
|
||||
class TestClientTestInJvm {
|
||||
|
||||
val accessToken = File("src/jvmTest/resources/accessToken.txt").readText()
|
||||
|
||||
fun encrypt(publicKeyBytes: ByteArray, data: ByteArray): ByteArray {
|
||||
@@ -63,25 +61,27 @@ class TestClientTestInJvm {
|
||||
// generateOfflineUuid("RTAkland"),
|
||||
Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"),
|
||||
// null,
|
||||
accessToken
|
||||
) {
|
||||
rsaEncryptor = RSA1024Encryptor { key, data -> encrypt(key, data) }
|
||||
sha1Hasher =
|
||||
Sha1Hasher { serverId, secretKey, publicKey -> minecraftServerIdHash(serverId, secretKey, publicKey) }
|
||||
cipherFactory = { key -> JvmAesCipher(key) }
|
||||
|
||||
authProvider = AuthenticationProvider { url, accessToken, uuid, serverIdHash ->
|
||||
val connection = URL(url).openConnection() as HttpURLConnection
|
||||
connection.requestMethod = "POST"
|
||||
connection.doOutput = true
|
||||
connection.setRequestProperty("Content-Type", "application/json")
|
||||
connection.getOutputStream()
|
||||
.use { it.write("{\"accessToken\":\"$accessToken\", \"selectedProfile\":\"$uuid\", \"serverId\":\"$serverIdHash\"}".encodeToByteArray()) }
|
||||
connection.disconnect()
|
||||
}
|
||||
}
|
||||
cli.on<ClientboundSystemChatMessagePacket> { println(it) }
|
||||
cli.on<ClientboundLoginSuccessPacket> { println(it) }
|
||||
accessToken,
|
||||
crypto = DefaultProtocolContext
|
||||
)
|
||||
// {
|
||||
// rsaEncryptor = RSA1024Encryptor { key, data -> encrypt(key, data) }
|
||||
// sha1Hasher =
|
||||
// Sha1Hasher { serverId, secretKey, publicKey -> minecraftServerIdHash(serverId, secretKey, publicKey) }
|
||||
// cipherFactory = { key -> JvmAesCipher(key) }
|
||||
// authProvider = AuthenticationProvider { url, accessToken, uuid, serverIdHash ->
|
||||
// val connection = URL(url).openConnection() as HttpURLConnection
|
||||
// connection.requestMethod = "POST"
|
||||
// connection.doOutput = true
|
||||
// connection.setRequestProperty("Content-Type", "application/json")
|
||||
// connection.getOutputStream()
|
||||
// .use { it.write("{\"accessToken\":\"$accessToken\", \"selectedProfile\":\"$uuid\", \"serverId\":\"$serverIdHash\"}".encodeToByteArray()) }
|
||||
// connection.disconnect()
|
||||
// }
|
||||
// }
|
||||
// cli.onPacket<ClientboundSystemChatMessagePacket> { println(it) }
|
||||
// cli.onPacket<ClientboundLoginSuccessPacket> { println(it) }
|
||||
cli.on { packet, direction -> println("${direction} -> $packet") }
|
||||
cli.launch { cli.connect() }
|
||||
while (true) {
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
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.Socket
|
||||
import cn.rtast.libmc.stream.WriteChannel
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ includeSubModule(":rconlib")
|
||||
includeSubModule(":protocol")
|
||||
includeSubModule(":protocol-encrypt")
|
||||
includeSubModule(":nbt")
|
||||
includeSubModule(":snbt")
|
||||
//includeSubModule(":snbt")
|
||||
|
||||
fun includeSubModule(name: String, path: String? = null) = include(name).also {
|
||||
project(name).projectDir = file(path ?: "libmc-${name.removePrefix(":")}")
|
||||
|
||||
Reference in New Issue
Block a user