From c17c736054eaa156a05ce0c2eff191afa4bfb3cb Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 29 Jan 2020 16:27:04 +0800 Subject: [PATCH] Generify packet consumer --- .../network/QQAndroidBotNetworkHandler.kt | 18 ++- .../protocol/packet/OutgoingPacketAndroid.kt | 114 +++++++++++------- .../network/protocol/packet/PacketFactory.kt | 34 ++++-- 3 files changed, 106 insertions(+), 60 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index 8d9d9a078..44afb0ed5 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -13,6 +13,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.* import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc @@ -20,6 +21,7 @@ import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.io.* import kotlin.coroutines.CoroutineContext +@Suppress("MemberVisibilityCanBePrivate") @UseExperimental(MiraiInternalAPI::class) internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() { override val bot: QQAndroidBot by bot.unsafeWeakRef() @@ -145,18 +147,20 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler */ suspend fun parsePacket(input: Input) { try { - KnownPacketFactories.parseIncomingPacket(bot, input) { packet: Packet, commandName: String, sequenceId: Int -> - if (PacketReceivedEvent(packet).broadcast().cancelled) { - return@parseIncomingPacket - } - - // pass to listeners (attached by sendAndExpect). + KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory, packet: Packet, commandName: String, sequenceId: Int -> + // highest priority: pass to listeners (attached by sendAndExpect). packetListeners.forEach { listener -> if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) { listener.complete(packet) } } + // check top-level cancelling + if (PacketReceivedEvent(packet).broadcast().cancelled) { + return@parseIncomingPacket + } + + // broadcast if (packet is Subscribable) { if (packet is BroadcastControllable) { @@ -168,6 +172,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler if (packet is Cancellable && packet.cancelled) return@parseIncomingPacket } + packetFactory.run { packet.handle(bot) } + bot.logger.info(packet) } } finally { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt index e6dfda0d3..f2a4d1487 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt @@ -7,8 +7,6 @@ import kotlinx.io.core.buildPacket import kotlinx.io.core.writeFully import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.utils.MiraiInternalAPI -import net.mamoe.mirai.utils.cryptor.DecrypterByteArray -import net.mamoe.mirai.utils.cryptor.encryptAndWrite import net.mamoe.mirai.utils.io.encryptAndWrite import net.mamoe.mirai.utils.io.writeHex import net.mamoe.mirai.utils.io.writeIntLVPacket @@ -50,22 +48,13 @@ internal val EMPTY_BYTE_ARRAY = ByteArray(0) * * byte[] body encrypted by 16 zero */ -/* - * 00 00 02 34 // remaining.length + 4 - * 00 00 00 0B - * 01 - * 00 01 4E 73 // sequence - * 00 - * 00 00 00 0E - * 31 39 39 34 37 30 31 30 32 31 - * 18 5D 8F 17 7D 67 71 61 FE DB 30 A4 4D 16 DD 0E 8D 84 0A F2 44 BE FB BB 11 BB B4 AC 79 50 50 9F 4C 99 CC 77 0B AA B6 E0 06 0C F7 91 79 99 57 31 3D EF 38 92 2C C8 81 33 79 83 FF C6 2F BA 18 2A 33 F8 D9 4E CD 62 07 D8 08 B7 1A 1E C7 EB AC AB B4 1E C9 9D A9 15 9C 29 29 2A 99 F6 BB D0 43 65 D6 5E 9C 93 A8 8D 17 08 5B 6A 29 92 58 6A 75 C9 B5 45 B3 0E A5 D3 52 8F 9A A4 88 36 A0 14 3A 21 F2 46 C3 91 66 A3 73 67 6A 3E F7 9D 8E 44 52 87 7B 8A C7 1B E2 D3 98 62 E8 25 30 2A 43 5C 5A B2 C6 45 F5 39 EC 85 81 BF 7D 22 4C E8 01 87 92 48 38 06 6B A0 83 70 0B 51 ED CF 7A FF E2 F2 06 3E A7 95 4E E5 29 23 32 1C FE 79 C6 08 C5 7A 39 B9 AF CD 4F 80 3E 5D 74 4D 0B E1 10 33 8D F0 54 8E 0E 22 96 B4 06 7F 29 01 1E CA 30 35 FD 8A 2E 51 04 20 79 7B 08 DC DF F6 64 21 6B C5 95 34 B3 40 D2 E8 CE BB DC 69 89 75 62 A6 0B 4A 49 9D 90 BA 68 2B BD 8A 50 2D 68 6B 56 40 0C 39 F2 08 20 1B EB A4 A5 20 1D 1F 7E FA 4B B8 2E 58 79 2A 16 54 26 6C C8 44 6C 4F 64 2D 5C 0C 47 2E 90 13 A9 D7 33 4A 51 17 6E 3F 3E 48 AE 39 D8 45 05 2C 0C 3C 9F 92 39 DB 62 B3 BB 64 EE 7E 91 C5 84 92 10 96 D9 F1 13 02 94 00 EA DA 87 7C 85 7B 68 BA 8D A1 AB F5 CD 9C EB 4C CD A0 38 78 43 80 DD E5 1D 28 25 1F F0 25 EF 0D 95 91 0F 21 5D 41 06 00 03 48 77 E0 98 09 3E 04 5A B0 93 63 3B AE 8E 49 0C C2 12 BA DD C3 5A ED FF 68 98 22 C4 5E F6 1E 85 57 15 E8 7E 26 22 E3 70 C2 57 F4 CE 2F CB C4 DC 39 4A 9C FE DE 27 18 D3 36 66 88 92 D7 69 D0 04 8E 93 9B AD E9 2E 5A 2C 91 CD 28 DF BE 62 CF 2C 72 8E FD A9 1F 0E 8E 00 9E 54 28 50 25 0C E7 DC 98 85 C9 B3 59 A8 97 F5 2E 7F 44 4C 43 3C C4 65 E5 AB DB 5B 3C 50 2D 53 B3 EA 74 3C 39 F4 0A 52 31 34 30 F5 E6 82 CD 36 D9 - */ @UseExperimental(MiraiInternalAPI::class) -internal inline fun PacketFactory<*>.buildOutgingPacket( +internal inline fun PacketFactory<*>.buildOutgoingPacket( client: QQAndroidClient, + bodyType: Byte = 1, // 1: PB? name: String? = this.commandName, commandName: String = this.commandName, - key: ByteArray, + key: ByteArray = client.wLoginSigInfo.d2Key, body: BytePacketBuilder.(sequenceId: Int) -> Unit ): OutgoingPacket { val sequenceId: Int = client.nextSsoSequenceId() @@ -73,7 +62,7 @@ internal inline fun PacketFactory<*>.buildOutgingPacket( return OutgoingPacket(name, commandName, sequenceId, buildPacket { writeIntLVPacket(lengthOffset = { it + 4 }) { writeInt(0x0B) - writeByte(1) + writeByte(bodyType) writeInt(sequenceId) writeByte(0) client.uin.toString().let { @@ -87,6 +76,68 @@ internal inline fun PacketFactory<*>.buildOutgingPacket( }) } +/** + * buildOutgoingPacket 与 writeUniPacket 的 fast-path + */ +@UseExperimental(MiraiInternalAPI::class) +internal inline fun PacketFactory<*>.buildOutgoingUniPacket( + client: QQAndroidClient, + bodyType: Byte = 1, // 1: PB? + name: String? = this.commandName, + commandName: String = this.commandName, + key: ByteArray = client.wLoginSigInfo.d2Key, + extraData: ByteReadPacket = BRP_STUB, + body: BytePacketBuilder.(sequenceId: Int) -> Unit +): OutgoingPacket { + val sequenceId: Int = client.nextSsoSequenceId() + + return OutgoingPacket(name, commandName, sequenceId, buildPacket { + writeIntLVPacket(lengthOffset = { it + 4 }) { + writeInt(0x0B) + writeByte(bodyType) + writeInt(sequenceId) + writeByte(0) + client.uin.toString().let { + writeInt(it.length + 4) + writeStringUtf8(it) + } + encryptAndWrite(key) { + writeUniPacket(commandName, extraData) { + body(sequenceId) + } + } + } + }) +} + +@UseExperimental(MiraiInternalAPI::class) +internal inline fun BytePacketBuilder.writeUniPacket( + commandName: String, + extraData: ByteReadPacket = BRP_STUB, + body: BytePacketBuilder.() -> Unit +) { + writeIntLVPacket(lengthOffset = { it + 4 }) { + commandName.let { + writeInt(it.length + 4) + writeStringUtf8(it) + } + + writeInt(4 + 4) + writeInt(45112203) // 02 B0 5B 8B + + if (extraData === BRP_STUB) { + writeInt(0x04) + } else { + writeInt((extraData.remaining + 4).toInt()) + writePacket(extraData) + } + } + + // body + writeIntLVPacket(lengthOffset = { it + 4 }, builder = body) +} + + /** * 最外层的包. 结构适用于登录. * @@ -213,37 +264,6 @@ internal inline fun BytePacketBuilder.writeSsoPacket( } -/** - * Outermost packet, not for login - * - * **Packet structure** - * int remaining.length + 4 - * int 0x0B - * byte 0x01 - * byte 0 - * int [uinAccount].length + 4 - * byte[] uinAccount - * - * byte[] body encrypted by [sessionKey] - */ -internal inline fun PacketFactory<*>.buildSessionOutgoingPacket( - uinAccount: String, - sessionKey: DecrypterByteArray, - body: BytePacketBuilder.() -> Unit -): ByteReadPacket = buildPacket { - writeIntLVPacket(lengthOffset = { it + 4 }) { - writeInt(0x00_00_00_0B) - writeByte(0x01) - - writeByte(0) - - writeInt(uinAccount.length + 4) - writeStringUtf8(uinAccount) - - encryptAndWrite(sessionKey, body) - } -} - /** * Writes a request packet * This is the innermost packet structure @@ -293,6 +313,8 @@ internal fun BytePacketBuilder.writeOicqRequestPacket( writeByte(0x03) // tail // } } + + /* 00 00 01 64 00 00 00 0A diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index 00414644e..fb5d12777 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -3,6 +3,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet import kotlinx.io.core.* import kotlinx.io.pool.useInstance import net.mamoe.mirai.data.Packet +import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.io.serialization.loadAs import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc @@ -33,9 +34,14 @@ internal abstract class PacketFactory( val commandName: String ) { /** - * **解码**服务器的回复数据包 + * **解码**服务器的回复数据包. 返回的包若是 [Subscribable], 则会 broadcast. */ abstract suspend fun ByteReadPacket.decode(bot: QQAndroidBot): TPacket + + /** + * 可选的处理这个包. 可以在这里面发新的包. + */ + open suspend fun @UnsafeVariance TPacket.handle(bot: QQAndroidBot) {} } @JvmName("decode0") @@ -43,7 +49,7 @@ private suspend inline fun

PacketFactory

.decode(bot: QQAndroidBo internal val DECRYPTER_16_ZERO = ByteArray(16) -internal typealias PacketConsumer = suspend (packet: Packet, commandName: String, ssoSequenceId: Int) -> Unit +internal typealias PacketConsumer = suspend (packetFactory: PacketFactory, packet: T, commandName: String, ssoSequenceId: Int) -> Unit @PublishedApi internal val PacketLogger: MiraiLogger = DefaultLogger("Packet") @@ -66,7 +72,8 @@ internal object KnownPacketFactories : List> by mutableListOf( * full packet without length */ // do not inline. Exceptions thrown will not be reported correctly - suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer) { + @Suppress("UNCHECKED_CAST") + suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer) { rawInput.readBytes().let { PacketLogger.verbose("开始处理包: ${it.toUHexString()}") it.toReadPacket() @@ -124,12 +131,13 @@ internal object KnownPacketFactories : List> by mutableListOf( 1 ->//it.data.parseUniResponse(bot, it.packetFactory, it.sequenceId, consumer) { consumer( + it.packetFactory as PacketFactory, it.packetFactory.run { decode(bot, it.data) }, it.packetFactory.commandName, it.sequenceId ) } - 2 -> it.data.parseOicqResponse(bot, it.packetFactory, it.sequenceId, consumer) + 2 -> it.data.parseOicqResponse(bot, it.packetFactory as PacketFactory, it.sequenceId, consumer) else -> error("unknown flag2: $flag2. Body to be parsed for inner packet=${it.data.readBytes().toUHexString()}") } } ?: inline { @@ -200,7 +208,12 @@ internal object KnownPacketFactories : List> by mutableListOf( return IncomingPacket(packetFactory, ssoSequenceId, input) } - private suspend fun ByteReadPacket.parseOicqResponse(bot: QQAndroidBot, packetFactory: PacketFactory<*>, ssoSequenceId: Int, consumer: PacketConsumer) { + private suspend fun ByteReadPacket.parseOicqResponse( + bot: QQAndroidBot, + packetFactory: PacketFactory, + ssoSequenceId: Int, + consumer: PacketConsumer + ) { val qq: Long readIoBuffer(readInt() - 4).withUse { check(readByte().toInt() == 2) @@ -243,14 +256,19 @@ internal object KnownPacketFactories : List> by mutableListOf( else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod") } - consumer(packet, packetFactory.commandName, ssoSequenceId) + consumer(packetFactory, packet, packetFactory.commandName, ssoSequenceId) } } - private suspend fun ByteReadPacket.parseUniResponse(bot: QQAndroidBot, packetFactory: PacketFactory<*>, ssoSequenceId: Int, consumer: PacketConsumer) { + private suspend fun ByteReadPacket.parseUniResponse( + bot: QQAndroidBot, + packetFactory: PacketFactory<*>, + ssoSequenceId: Int, + consumer: PacketConsumer + ) { val uni = readBytes(readInt() - 4).loadAs(RequestPacket.serializer()) PacketLogger.verbose(uni.toString()) - /// consumer(packetFactory.decode(bot, uni.sBuffer.toReadPacket()), uni.sServantName + "." + uni.sFuncName, ssoSequenceId) + /// consumer(packetFactory.decode(bot, uni.sBuffer.toReadPacket()), uni.sServantName + "." + uni.sFuncName, ssoSequenceId) } }