mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-24 14:30:09 +08:00
QQA Debugging update
This commit is contained in:
parent
8eb3daca82
commit
77ddb6f78d
@ -1,8 +1,8 @@
|
||||
package net.mamoe.mirai.qqandroid.network
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.use
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.ObjectPool
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
@ -14,11 +14,8 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.RegPushReason
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.SvcReqRegisterPacket
|
||||
import net.mamoe.mirai.utils.LockFreeLinkedList
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.getValue
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.unsafeWeakRef
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@ -46,25 +43,137 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
SvcReqRegisterPacket(bot.client, RegPushReason.setOnlineStatus).sendAndExpect<SvcReqRegisterPacket.Response>()
|
||||
}
|
||||
|
||||
internal fun launchPacketProcessor(rawInput: ByteReadPacket): Job = launch(CoroutineName("Incoming Packet handler")) {
|
||||
rawInput.debugPrint("Received").use { input ->
|
||||
if (input.remaining == 0L) {
|
||||
bot.logger.error("Empty packet received. Consider if bad packet was sent.")
|
||||
return@launch
|
||||
/**
|
||||
* 单线程处理包的接收, 分割和连接.
|
||||
*/
|
||||
@Suppress("PrivatePropertyName")
|
||||
private val PacketReceiveDispatcher = newCoroutineDispatcher(1)
|
||||
|
||||
/**
|
||||
* 单线程处理包的解析 (协程挂起效率够)
|
||||
*/
|
||||
@Suppress("PrivatePropertyName")
|
||||
private val PacketProcessDispatcher = newCoroutineDispatcher(1)
|
||||
|
||||
/**
|
||||
* 缓存的包
|
||||
*/
|
||||
private var cachedPacket: ByteReadPacket? = null
|
||||
/**
|
||||
* 缓存的包还差多少长度
|
||||
*/
|
||||
private var expectingRemainingLength: Long = 0
|
||||
|
||||
/**
|
||||
* 在 [PacketProcessDispatcher] 调度器中解析包内容.
|
||||
* [input] 将会被 [ObjectPool.recycle].
|
||||
*
|
||||
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
|
||||
*/
|
||||
fun parsePacketAsync(input: IoBuffer, pool: ObjectPool<IoBuffer> = IoBuffer.Pool): Job =
|
||||
this.launch(PacketProcessDispatcher) {
|
||||
try {
|
||||
parsePacket(input)
|
||||
} finally {
|
||||
input.discard()
|
||||
input.release(pool)
|
||||
}
|
||||
KnownPacketFactories.parseIncomingPacket(bot, input) { packet: Packet, packetId: PacketId, sequenceId: Int ->
|
||||
if (PacketReceivedEvent(packet).broadcast().cancelled) {
|
||||
return@parseIncomingPacket
|
||||
}
|
||||
packetListeners.forEach { listener ->
|
||||
if (listener.filter(packetId, sequenceId) && packetListeners.remove(listener)) {
|
||||
listener.complete(packet)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 [PacketProcessDispatcher] 调度器中解析包内容.
|
||||
* [input] 将会被 [Input.close], 因此 [input] 不能为 [IoBuffer]
|
||||
*
|
||||
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
|
||||
*/
|
||||
fun parsePacketAsync(input: Input): Job {
|
||||
require(input !is IoBuffer) { "input cannot be IoBuffer" }
|
||||
return this.launch(PacketProcessDispatcher) {
|
||||
input.use { parsePacket(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析包内容
|
||||
* **注意**: 需要函数调用者 close 这个 [input]
|
||||
*
|
||||
* @param input 一个完整的包的内容, 去掉开头的 int 包长度
|
||||
*/
|
||||
suspend fun parsePacket(input: Input) {
|
||||
KnownPacketFactories.parseIncomingPacket(bot, input) { packet: Packet, packetId: PacketId, sequenceId: Int ->
|
||||
if (PacketReceivedEvent(packet).broadcast().cancelled) {
|
||||
return@parseIncomingPacket
|
||||
}
|
||||
packetListeners.forEach { listener ->
|
||||
if (listener.filter(packetId, sequenceId) && packetListeners.remove(listener)) {
|
||||
listener.complete(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理从服务器接收过来的包. 这些包可能是粘在一起的, 也可能是不完整的. 将会自动处理
|
||||
*/
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
internal suspend fun processPacket(rawInput: ByteReadPacket): Unit = rawInput.debugPrint("Received").let { input: ByteReadPacket ->
|
||||
if (input.remaining == 0L) {
|
||||
return
|
||||
}
|
||||
|
||||
if (cachedPacket == null) {
|
||||
// 没有缓存
|
||||
var length: Int = input.readInt() - 4
|
||||
if (input.remaining == length.toLong()) {
|
||||
// 捷径: 当包长度正好, 直接传递剩余数据.
|
||||
parsePacketAsync(input)
|
||||
return
|
||||
}
|
||||
// 循环所有完整的包
|
||||
while (input.remaining > length) {
|
||||
parsePacketAsync(input.readIoBuffer(length))
|
||||
|
||||
length = input.readInt() - 4
|
||||
}
|
||||
|
||||
if (input.remaining != 0L) {
|
||||
// 剩余的包长度不够, 缓存后接收下一个包
|
||||
expectingRemainingLength = length - input.remaining
|
||||
cachedPacket = input
|
||||
} else {
|
||||
cachedPacket = null // 表示包长度正好
|
||||
}
|
||||
} else {
|
||||
// 有缓存
|
||||
|
||||
if (input.remaining >= expectingRemainingLength) {
|
||||
// 剩余长度够, 连接上去, 处理这个包.
|
||||
parsePacketAsync(buildPacket {
|
||||
writePacket(cachedPacket!!)
|
||||
writePacket(input, expectingRemainingLength)
|
||||
})
|
||||
cachedPacket = null // 缺少的长度已经给上了.
|
||||
|
||||
if (input.remaining != 0L) {
|
||||
processPacket(input) // 继续处理剩下内容
|
||||
}
|
||||
} else {
|
||||
// 剩余不够, 连接上去
|
||||
expectingRemainingLength -= input.remaining
|
||||
cachedPacket = buildPacket {
|
||||
writePacket(cachedPacket!!)
|
||||
writePacket(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (input.remaining == 0L) {
|
||||
bot.logger.error("Empty packet received. Consider if bad packet was sent.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
private suspend fun processReceive() {
|
||||
while (channel.isOpen) {
|
||||
val rawInput = try {
|
||||
@ -81,7 +190,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
bot.logger.error("Caught unexpected exceptions", e)
|
||||
continue
|
||||
}
|
||||
launchPacketProcessor(rawInput)
|
||||
launch(context = PacketReceiveDispatcher + CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) {
|
||||
processPacket(rawInput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,16 +62,13 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
|
||||
// 00 00 08 E0 00 00 00 0A 01 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 B4 16 A6 D7 A3 E4 9E 53 99 CD 77 14 70 1F 51 3E 8B 79 F6 93 2B E0 92 E4 32 E2 87 6C 3A 9C 1B 29 87 CB 3C 60 45 9C 41 71 63 6A F6 99 FC 05 01 68 86 B3 6F 37 97 52 C5 D3 0E 66 B3 F6 40 CC EB 18 A3 AE 15 3E 31 B1 E9 7C 6F EC E4 4D 31 F1 1E 2C 0C 1C 45 66 CD F7 1B 90 11 9A D8 CE DD 6D 6C 63 9F EB CD 69 33 AF 6C 8E BA 8C CB C3 FF 27 A2 A6 C3 28 06 4A B5 79 79 12 AB 52 04 62 CA 7D 11 59 85 5C 0B D6 8D 2A E7 9C 04 97 62 7D 05 11 3E 2C 11 60 E3 E3 B3 DA 7A 7C 13 AF 22 01 53 80 69 D0 F9 C8 86 EC 25 8C F3 67 5C 82 45 08 FB 34 43 50 01 0E EA 43 77 D8 CF EC 55 E6 4E 66 5B 26 21 C9 E8 78 92 AE 5C 61 F0 5E 0B E7 34 1F 53 D6 EA 28 9C 02 1A E9 F0 55 61 4B 06 F8 56 3B AC 93 B2 2C CD 66 0D D1 18 CB BD 29 50 DE 0F 82 6D 28 63 AB 21 E1 6C BA B1 9F 69 A4 E3 C9 20 F8 11 82 39 04 2B 54 44 50 FA 2E 86 68 6D DC 5D 9E 18 F4 DD 19 09 BC CF E8 41 68 A3 8D 86 42 80 51 C4 C1 ED 54 DB 50 F5 1D A7 28 2A 0D E8 14 1A 4E F7 96 29 00 6C 9D 4A 2E 3E 7B 4C AC 20 78 F1 3C 70 6B 61 96 D7 EC 77 AD CB AD AF BB 47 C3 1F A0 6C 6C 9C 9F F3 6C EB 6C A4 D0 7F 2B E1 AA 68 26 99 B9 C8 A1 F5 C4 7E E7 E7 81 EE 66 00 96 33 49 C0 EE A2 F9 F6 52 C5 A6 5D EE 9D C5 E5 CE DA 31 FC FF 4B 02 97 68 3D 6A 99 4A CF 69 D9 F4 53 68 31 E7 32 2F 85 E7 7F 16 82 AE FA 73 D5 42 09 9C CB 53 26 79 41 63 80 B0 E2 6A 8B B9 C6 71 08 B4 2B E0 48 D3 C4 0F B0 00 D0 FA 8C 29 DE E9 71 6A D7 89 76 E7 5D 33 14 10 6F E2 44 6A A0 DC C1 CB F3 9A C3 13 CB D1 82 2C DF 34 68 79 E3 09 BD CC 2B 25 79 A8 E7 BE 29 6C 97 C3 D7 F4 0E CC 2B 74 71 02 BA 2B 5B 57 1B C2 C8 C2 BF 54 23 72 EA E4 38 54 20 7D 88 E4 39 7C C5 8A 1B C0 EC D2 1E 7D 1B 6B 7A BC EC 73 1E 53 4A 6F 4F EA F0 56 12 80 BD 0B 37 67 BD FD A8 29 23 2D 8E 66 7E 31 A9 F6 CE 7E BC 4F 38 D0 33 D4 C7 4A E9 43 9D 28 2E 8F 7C D5 81 F4 8C F9 6F 21 AC A1 08 FD F4 01 FB E8 CE 61 91 BE 68 5B E4 3A 5F F8 FB DA 5D 9B 2A AF E2 0C D3 A4 1F 42 90 96 E1 28 44 85 8D E1 CF 19 A9 47 04 8D 28 D9 B3 35 79 48 70 D9 ED 45 B6 24 B5 56 FA 1E DE 02 F3 EB 69 08 7D 24 9C 60 35 97 8D 13 4A 5A 57 BA B3 14 C1 EE 70 22 CA B2 65 F7 BB 3F A2 D9 14 AA 4C 52 E6 E4 10 D3 FD C6 2B DD BF C0 CF E5 35 57 9E 9F D0 77 C8 E6 EF 2B 8E 01 88 96 F8 68 95 A7 0D 58 81 30 60 88 44 CC 31 5B C1 D4 92 6E ED 17 CA 0A 01 69 90 4E 6A C0 D7 09 6C E5 33 64 CA 6E 5C 07 C3 AD 46 36 F9 DF DE B7 71 B2 87 CB 3D 76 C0 44 B8 6B 15 27 B2 03 99 C7 51 8A 00 35 C9 1C 76 55 32 AE 49 5A 34 6A 4E FD 20 7A 24 BF 34 E8 B4 18 BC 92 64 A1 F3 0A 2E 7B 00 EA B6 52 E7 AC 34 FD AE FF 1E 5D 6D D6 1F 6D 06 31 09 9D A9 9C 86 DB 5E 05 07 BA 4A 49 2B D2 7F EE 88 64 B2 6F 15 70 39 1B E9 57 6A 4E 29 4A A4 57 EA 80 3D 86 4C E9 F7 F5 2B C4 9F 35 62 76 09 0E 1C A4 99 50 99 82 2F 84 90 0E 9E 9F 75 C3 15 B0 61 34 D1 67 2D 30 16 FE D3 BF 59 6A B1 74 02 C4 EF 92 85 E0 16 4B 0C C5 9D 65 BB 5D 52 8F 52 5B 7C 7B 74 D9 EC 41 A9 5B FA 2D 95 D4 AE 5D F1 68 88 F6 82 ED 09 05 21 2E 5D 93 64 A0 96 15 64 A6 50 3C 03 2B FC 3E 80 89 90 62 CC D9 23 8E D7 BD 05 02 30 86 32 31 6A 5F F8 C4 BD 61 D0 CE B9 54 4E 93 E9 AE B9 4F 2B 98 DC 23 31 CC A8 06 89 A8 08 60 99 DC D4 81 98 13 C9 27 36 32 24 C1 B0 6B F0 3D EB CC 3B 32 5F 20 72 23 B3 DF 0B 48 3C 35 FD F1 FB DC 3E 2A BE B9 0F 42 56 F1 39 94 86 85 C6 1E A0 4C EC B8 69 45 5F 3D AB 3C 3B A2 70 61 91 9D 2C DD 6D C5 E9 EF 47 36 A6 A3 E0 96 C2 B8 EF 92 E9 E0 26 88 C6 B5 51 BA DE FD C5 BA 4C 6A 9A FE 6F DE B8 10 05 7F 9C 5D 40 11 39 75 CD 36 4F 6B A8 A1 94 57 5F 8F F2 D0 E2 36 A0 A4 24 05 FD 9E F5 51 93 C9 6E 5A 10 8D C3 33 2D E5 09 7A E0 DB 44 63 9C EA A5 ED BF 0B 98 32 F1 BA 04 96 F6 14 49 F1 F8 58 EA 6E 5E 5E 49 CA 2D E2 93 E6 AD 20 B2 CD 98 A7 3E BA 3E A8
|
||||
|
||||
|
||||
/**
|
||||
* full packet without length
|
||||
*/
|
||||
// do not inline. Exceptions thrown will not be reported correctly
|
||||
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
|
||||
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer) =
|
||||
rawInput.debugIfFail("Incoming packet") {
|
||||
require(remaining < Int.MAX_VALUE) { "rawInput is too long" }
|
||||
val expectedLength = readUInt().toInt() - 4
|
||||
if (expectedLength > 16e7) {
|
||||
bot.logger.warning("Detect incomplete packet, ignoring.")
|
||||
return@debugIfFail
|
||||
}
|
||||
check(remaining.toInt() == expectedLength) { "Invalid packet length. Expected $expectedLength, got ${this.remaining} Probably packets merged? " }
|
||||
// login
|
||||
val flag1 = readInt()
|
||||
|
||||
@ -88,12 +85,12 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
|
||||
//debugPrint("remaining")
|
||||
|
||||
when (flag1) {
|
||||
0x0A -> parseLoginSsoPacket(bot, decryptBy(DECRYPTER_16_ZERO), consumer)
|
||||
0x0A -> parseLoginSsoPacket(bot, if (flag2 == 2) decryptBy(DECRYPTER_16_ZERO) else decryptBy(bot.client.wLoginSigInfo.d2Key), consumer)
|
||||
0x0B -> parseUniPacket(bot, decryptBy(DECRYPTER_16_ZERO), consumer)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun parseUniPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
|
||||
private fun parseUniPacket(bot: QQAndroidBot, rawInput: ByteReadPacket, consumer: PacketConsumer) =
|
||||
rawInput.debugIfFail("Login sso packet") {
|
||||
readIoBuffer(readInt() - 4).withUse {
|
||||
//00 01 4E 64 FF FF D8 E8 00 00 00 14 6E 65 65 64 20 41 32 20 61 6E 64 20 49 4D 45 49 00 00 00 04 00 00 00 08 60 7F B6 23 00 00 00 00 00 00 00 04
|
||||
|
@ -68,8 +68,12 @@ private val shareKeyCalculatedByConstPubKey = ECDH.calculateShareKey(
|
||||
)
|
||||
|
||||
fun main() {
|
||||
|
||||
|
||||
val data = """
|
||||
20da22db750806141ef448110800450001b45126400080060000c0a8030a71600dd0fe501f908b8c585508ceeec6501801fc448900000000018c0000000a0100000044e0e22a59327abb9ce80cf63be86694210344fab2f2b065d7785a32cacfa4075cd509f49cb37a075ad0685cbd472344bfdef1ede611c0ad81129be9e2d7e476d2000000000e31393934373031303231f00754574170e9a5d13ca9426984103d1813e22d9c2147f8da5a0af64f64e4ebffde9ce8bfda6c1f798364a1de449adc701595fdce57fa4643e5e14eb4444ad0aea85261a6d5ee90c42a29b5af461a971a7fe85ecf1ed0d582d1cce60d1c34f6a3dc74a75d2f525d590da954098a1a7a4973773195dd209e662cb5c1e0db69843aa75425993bdc8876f21fd9c875d25fa47b689ebc302f3087de8e4a9862cff283703237602e50fa6bb281e974315b70f04f436d8ae9c5d67e22af3495e64aada419940e63c8d0ddbb066fa3d2b8cb27209a687d782d4ec32e6505593625bf0de257d59332f779ed6edb849502591e2c37b983974dbc3ec4740dfd0a9244baf958219067ca7d435ec8bde8e57b36f04d31622a86cb9c8d59d81074c9af38d57f42bb331a0a15f9ac5e216ae54abe8f8a
|
||||
20da22db750806141ef448110800450000d4512f400080060000c0a8030a71600dd0fe501f908b8c5c9908cf2416501801ff43a90000000000ac0000000b0100014f0d000000000e3139393437303130323193c94c8ce2871f6d5f6664df9e9231dedce5c7cb914cf5d616cf64af478aea9f210098e7f5efae9a952742b8fff704681885e5cc14a44e40d88910258f4a4a5cfcadd642cc159fdc475478475b18ba225cfad1c8bc2c5c828a9cc3ebaed1aa040d04b94f577fcfdb0861fd19754622733a242c5bcb37ca8597a2935d9910162111e44839b6787bb9be80058ad9c921c2
|
||||
|
||||
|
||||
""".trimIndent()
|
||||
.trim().split("\n").map {
|
||||
val bytes = it.trim().autoHexToBytes()
|
||||
|
@ -3,7 +3,8 @@
|
||||
package test
|
||||
|
||||
import net.mamoe.mirai.utils.cryptor.protoFieldNumber
|
||||
import net.mamoe.mirai.utils.cryptor.protoType
|
||||
|
||||
intArrayOf(10, 18, 26, 34, 42, 50, 58, 66, 74).forEach {
|
||||
println(protoFieldNumber(it.toUInt()))
|
||||
intArrayOf(8, 18, 26, 34, 80).forEach {
|
||||
println(protoFieldNumber(it.toUInt()).toString() + " -> " + protoType(it.toUInt()))
|
||||
}
|
@ -3,12 +3,15 @@ package net.mamoe.mirai.utils
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.DataInput
|
||||
import java.io.EOFException
|
||||
import java.io.InputStream
|
||||
import java.net.InetAddress
|
||||
import java.security.MessageDigest
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.zip.CRC32
|
||||
import java.util.zip.Inflater
|
||||
|
||||
@ -91,4 +94,8 @@ actual fun ByteArray.unzip(): ByteArray {
|
||||
}
|
||||
inflater.end()
|
||||
return output.toByteArray()
|
||||
}
|
||||
|
||||
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {
|
||||
return Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
|
||||
}
|
@ -4,6 +4,7 @@ package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.util.date.GMTDate
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
@ -46,3 +47,5 @@ expect fun localIpAddress(): String
|
||||
* Ktor HttpClient. 不同平台使用不同引擎.
|
||||
*/
|
||||
expect val Http: HttpClient
|
||||
|
||||
expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher
|
@ -2,7 +2,10 @@ package net.mamoe.mirai.utils.io
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.pool.useInstance
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.MiraiDebugAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.withSwitch
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
@ -74,7 +77,7 @@ inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteR
|
||||
ByteArrayPool.useInstance {
|
||||
val count = this.readAvailable(it)
|
||||
try {
|
||||
return block(it.toReadPacket(0, count))
|
||||
return it.toReadPacket(0, count).use(block)
|
||||
} catch (e: Throwable) {
|
||||
onFail(it.take(count).toByteArray()).readAvailable(it)
|
||||
DebugLogger.debug("Error in ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count))
|
||||
|
@ -4,15 +4,15 @@ package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import kotlinx.io.core.IoBuffer
|
||||
import kotlinx.io.core.Output
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.io.core.copyTo
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.streams.asInput
|
||||
import kotlinx.io.streams.asOutput
|
||||
import java.io.*
|
||||
import java.net.InetAddress
|
||||
import java.security.MessageDigest
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.zip.CRC32
|
||||
import java.util.zip.Inflater
|
||||
|
||||
@ -68,4 +68,8 @@ actual fun ByteArray.unzip(): ByteArray {
|
||||
}
|
||||
inflater.end()
|
||||
return output.toByteArray()
|
||||
}
|
||||
|
||||
actual fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher {
|
||||
return Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher()
|
||||
}
|
@ -2,15 +2,17 @@
|
||||
|
||||
package test
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import kotlinx.serialization.protobuf.ProtoNumberType
|
||||
import kotlinx.serialization.protobuf.ProtoType
|
||||
import kotlinx.serialization.serializer
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.cryptor.readProtoMap
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.read
|
||||
import net.mamoe.mirai.utils.io.toUHexString
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Serializable
|
||||
@ -25,15 +27,14 @@ data class ProtoTest(
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
suspend fun main() {
|
||||
println("PNG".toUtf8Bytes().toUHexString())
|
||||
deserializeTest()
|
||||
}
|
||||
|
||||
suspend fun deserializeTest() {
|
||||
val bytes =
|
||||
"""
|
||||
|
||||
""".trimIndent()
|
||||
08 02 1A 55 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 1A 25 2F 34 35 35 38 66 39 30 38 2D 37 62 39 61 2D 34 65 32 66 2D 38 63 36 39 2D 34 61 35 32 61 66 62 33 36 35 61 37 20 01 30 04 38 05 40 09 48 01 58 00 60 01 6A 0A 38 2E 32 2E 30 2E 31 32 39 36 70 E0 8C B2 F0 05 78 01 50 03
|
||||
""".trimIndent()
|
||||
.replace("\n", " ")
|
||||
.replace("UVarInt", "", ignoreCase = true)
|
||||
.replace("uint", "", ignoreCase = true)
|
||||
|
Loading…
Reference in New Issue
Block a user