Generify packet consumer

This commit is contained in:
Him188 2020-01-29 16:27:04 +08:00
parent 849519afc6
commit c17c736054
3 changed files with 106 additions and 60 deletions

View File

@ -13,6 +13,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories 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.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
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.* import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc 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 net.mamoe.mirai.utils.io.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@Suppress("MemberVisibilityCanBePrivate")
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() { internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler() {
override val bot: QQAndroidBot by bot.unsafeWeakRef() override val bot: QQAndroidBot by bot.unsafeWeakRef()
@ -145,18 +147,20 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
*/ */
suspend fun parsePacket(input: Input) { suspend fun parsePacket(input: Input) {
try { try {
KnownPacketFactories.parseIncomingPacket(bot, input) { packet: Packet, commandName: String, sequenceId: Int -> KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<Packet>, packet: Packet, commandName: String, sequenceId: Int ->
if (PacketReceivedEvent(packet).broadcast().cancelled) { // highest priority: pass to listeners (attached by sendAndExpect).
return@parseIncomingPacket
}
// pass to listeners (attached by sendAndExpect).
packetListeners.forEach { listener -> packetListeners.forEach { listener ->
if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) { if (listener.filter(commandName, sequenceId) && packetListeners.remove(listener)) {
listener.complete(packet) listener.complete(packet)
} }
} }
// check top-level cancelling
if (PacketReceivedEvent(packet).broadcast().cancelled) {
return@parseIncomingPacket
}
// broadcast // broadcast
if (packet is Subscribable) { if (packet is Subscribable) {
if (packet is BroadcastControllable) { if (packet is BroadcastControllable) {
@ -168,6 +172,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (packet is Cancellable && packet.cancelled) return@parseIncomingPacket if (packet is Cancellable && packet.cancelled) return@parseIncomingPacket
} }
packetFactory.run { packet.handle(bot) }
bot.logger.info(packet) bot.logger.info(packet)
} }
} finally { } finally {

View File

@ -7,8 +7,6 @@ import kotlinx.io.core.buildPacket
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.utils.MiraiInternalAPI 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.encryptAndWrite
import net.mamoe.mirai.utils.io.writeHex import net.mamoe.mirai.utils.io.writeHex
import net.mamoe.mirai.utils.io.writeIntLVPacket import net.mamoe.mirai.utils.io.writeIntLVPacket
@ -50,22 +48,13 @@ internal val EMPTY_BYTE_ARRAY = ByteArray(0)
* *
* byte[] body encrypted by 16 zero * 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) @UseExperimental(MiraiInternalAPI::class)
internal inline fun PacketFactory<*>.buildOutgingPacket( internal inline fun PacketFactory<*>.buildOutgoingPacket(
client: QQAndroidClient, client: QQAndroidClient,
bodyType: Byte = 1, // 1: PB?
name: String? = this.commandName, name: String? = this.commandName,
commandName: String = this.commandName, commandName: String = this.commandName,
key: ByteArray, key: ByteArray = client.wLoginSigInfo.d2Key,
body: BytePacketBuilder.(sequenceId: Int) -> Unit body: BytePacketBuilder.(sequenceId: Int) -> Unit
): OutgoingPacket { ): OutgoingPacket {
val sequenceId: Int = client.nextSsoSequenceId() val sequenceId: Int = client.nextSsoSequenceId()
@ -73,7 +62,7 @@ internal inline fun PacketFactory<*>.buildOutgingPacket(
return OutgoingPacket(name, commandName, sequenceId, buildPacket { return OutgoingPacket(name, commandName, sequenceId, buildPacket {
writeIntLVPacket(lengthOffset = { it + 4 }) { writeIntLVPacket(lengthOffset = { it + 4 }) {
writeInt(0x0B) writeInt(0x0B)
writeByte(1) writeByte(bodyType)
writeInt(sequenceId) writeInt(sequenceId)
writeByte(0) writeByte(0)
client.uin.toString().let { 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 * Writes a request packet
* This is the innermost packet structure * This is the innermost packet structure
@ -293,6 +313,8 @@ internal fun BytePacketBuilder.writeOicqRequestPacket(
writeByte(0x03) // tail writeByte(0x03) // tail
// } // }
} }
/* /*
00 00 01 64 00 00 01 64
00 00 00 0A 00 00 00 0A

View File

@ -3,6 +3,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet
import kotlinx.io.core.* import kotlinx.io.core.*
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.loadAs import net.mamoe.mirai.qqandroid.io.serialization.loadAs
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
@ -33,9 +34,14 @@ internal abstract class PacketFactory<out TPacket : Packet>(
val commandName: String val commandName: String
) { ) {
/** /**
* **解码**服务器的回复数据包 * **解码**服务器的回复数据包. 返回的包若是 [Subscribable], 则会 broadcast.
*/ */
abstract suspend fun ByteReadPacket.decode(bot: QQAndroidBot): TPacket abstract suspend fun ByteReadPacket.decode(bot: QQAndroidBot): TPacket
/**
* 可选的处理这个包. 可以在这里面发新的包.
*/
open suspend fun @UnsafeVariance TPacket.handle(bot: QQAndroidBot) {}
} }
@JvmName("decode0") @JvmName("decode0")
@ -43,7 +49,7 @@ private suspend inline fun <P : Packet> PacketFactory<P>.decode(bot: QQAndroidBo
internal val DECRYPTER_16_ZERO = ByteArray(16) internal val DECRYPTER_16_ZERO = ByteArray(16)
internal typealias PacketConsumer = suspend (packet: Packet, commandName: String, ssoSequenceId: Int) -> Unit internal typealias PacketConsumer<T> = suspend (packetFactory: PacketFactory<T>, packet: T, commandName: String, ssoSequenceId: Int) -> Unit
@PublishedApi @PublishedApi
internal val PacketLogger: MiraiLogger = DefaultLogger("Packet") internal val PacketLogger: MiraiLogger = DefaultLogger("Packet")
@ -66,7 +72,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
* full packet without length * full packet without length
*/ */
// do not inline. Exceptions thrown will not be reported correctly // do not inline. Exceptions thrown will not be reported correctly
suspend fun parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer) { @Suppress("UNCHECKED_CAST")
suspend fun <T : Packet> parseIncomingPacket(bot: QQAndroidBot, rawInput: Input, consumer: PacketConsumer<T>) {
rawInput.readBytes().let { rawInput.readBytes().let {
PacketLogger.verbose("开始处理包: ${it.toUHexString()}") PacketLogger.verbose("开始处理包: ${it.toUHexString()}")
it.toReadPacket() it.toReadPacket()
@ -124,12 +131,13 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
1 ->//it.data.parseUniResponse(bot, it.packetFactory, it.sequenceId, consumer) 1 ->//it.data.parseUniResponse(bot, it.packetFactory, it.sequenceId, consumer)
{ {
consumer( consumer(
it.packetFactory as PacketFactory<T>,
it.packetFactory.run { decode(bot, it.data) }, it.packetFactory.run { decode(bot, it.data) },
it.packetFactory.commandName, it.packetFactory.commandName,
it.sequenceId it.sequenceId
) )
} }
2 -> it.data.parseOicqResponse(bot, it.packetFactory, it.sequenceId, consumer) 2 -> it.data.parseOicqResponse(bot, it.packetFactory as PacketFactory<T>, it.sequenceId, consumer)
else -> error("unknown flag2: $flag2. Body to be parsed for inner packet=${it.data.readBytes().toUHexString()}") else -> error("unknown flag2: $flag2. Body to be parsed for inner packet=${it.data.readBytes().toUHexString()}")
} }
} ?: inline { } ?: inline {
@ -200,7 +208,12 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
return IncomingPacket(packetFactory, ssoSequenceId, input) return IncomingPacket(packetFactory, ssoSequenceId, input)
} }
private suspend fun ByteReadPacket.parseOicqResponse(bot: QQAndroidBot, packetFactory: PacketFactory<*>, ssoSequenceId: Int, consumer: PacketConsumer) { private suspend fun <T : Packet> ByteReadPacket.parseOicqResponse(
bot: QQAndroidBot,
packetFactory: PacketFactory<T>,
ssoSequenceId: Int,
consumer: PacketConsumer<T>
) {
val qq: Long val qq: Long
readIoBuffer(readInt() - 4).withUse { readIoBuffer(readInt() - 4).withUse {
check(readByte().toInt() == 2) check(readByte().toInt() == 2)
@ -243,14 +256,19 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
else -> error("Illegal encryption method. expected 0 or 4, got $encryptionMethod") 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<Packet>
) {
val uni = readBytes(readInt() - 4).loadAs(RequestPacket.serializer()) val uni = readBytes(readInt() - 4).loadAs(RequestPacket.serializer())
PacketLogger.verbose(uni.toString()) 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)
} }
} }