Commit c17c7360 authored by Him188's avatar Him188

Generify packet consumer

parent 849519af
...@@ -13,6 +13,7 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot ...@@ -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.* ...@@ -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 ...@@ -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 ...@@ -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 {
......
...@@ -7,8 +7,6 @@ import kotlinx.io.core.buildPacket ...@@ -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) ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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
......
...@@ -3,6 +3,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet ...@@ -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>( ...@@ -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 ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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)
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment