From faf8002de99a673ff026cf3bc1ad899fdd633cc3 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 8 Apr 2020 13:25:20 +0800 Subject: [PATCH] Fix massive group list loading --- .../mirai/qqandroid/QQAndroidBot.common.kt | 4 +- .../network/QQAndroidBotNetworkHandler.kt | 105 +++++++++--------- .../protocol/packet/chat/TroopManagement.kt | 2 +- .../packet/chat/receive/OnlinePush.kt | 12 +- .../mirai/qqandroid/utils/ByteArrayPool.kt | 2 +- .../kotlin/net.mamoe.mirai/BotImpl.kt | 2 +- 6 files changed, 68 insertions(+), 59 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt index 9b88f9fa7..ebbc20bcc 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt @@ -206,7 +206,7 @@ internal abstract class QQAndroidBotBase constructor( TroopManagement.GetGroupInfo( client = bot.client, groupCode = groupCode - ).sendAndExpect(retry = 2) + ).sendAndExpect(retry = 3) } @OptIn(LowLevelAPI::class) @@ -224,7 +224,7 @@ internal abstract class QQAndroidBotBase constructor( targetGroupUin = groupUin, targetGroupCode = groupCode, nextUin = nextUin - ).sendAndExpect(timeoutMillis = 3000) + ).sendAndExpect(retry = 3) sequence += data.members.asSequence().map { troopMemberInfo -> MemberInfoImpl(troopMemberInfo, ownerId) } 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 66e6a5107..f3287dbf0 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 @@ -201,7 +201,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler _pendingEnabled.value = true } - coroutineScope { + supervisorScope { launch { lateinit var loadFriends: suspend () -> Unit // 不要用 fun, 不要 join declaration, 不要用 val, 编译失败警告 @@ -262,56 +262,60 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler try { logger.info("开始加载群组列表与群成员列表") val troopListData = FriendList.GetTroopListSimplify(bot.client) - .sendAndExpect(retry = 2) + .sendAndExpect(retry = 3) - troopListData.groups.forEach { troopNum -> - // 别用 fun, 别 val, 编译失败警告 - lateinit var loadGroup: suspend () -> Unit + troopListData.groups.chunked(100).forEach { chunk -> + coroutineScope { + chunk.forEach { troopNum -> + // 别用 fun, 别 val, 编译失败警告 + lateinit var loadGroup: suspend () -> Unit - loadGroup = suspend { - retryCatching(3) { - bot.groups.delegate.addLast( - @Suppress("DuplicatedCode") - (GroupImpl( - bot = bot, - coroutineContext = bot.coroutineContext, - id = troopNum.groupCode, - groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply { - this as GroupInfoImpl + loadGroup = suspend { + retryCatching(3) { + bot.groups.delegate.addLast( + @Suppress("DuplicatedCode") + (GroupImpl( + bot = bot, + coroutineContext = bot.coroutineContext, + id = troopNum.groupCode, + groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply { + this as GroupInfoImpl - if (this.delegate.groupName == null) { - this.delegate.groupName = troopNum.groupName - } + if (this.delegate.groupName == null) { + this.delegate.groupName = troopNum.groupName + } - if (this.delegate.groupMemo == null) { - this.delegate.groupMemo = troopNum.groupMemo - } + if (this.delegate.groupMemo == null) { + this.delegate.groupMemo = troopNum.groupMemo + } - if (this.delegate.groupUin == null) { - this.delegate.groupUin = troopNum.groupUin - } + if (this.delegate.groupUin == null) { + this.delegate.groupUin = troopNum.groupUin + } - this.delegate.groupCode = troopNum.groupCode - }, - members = bot._lowLevelQueryGroupMemberList( - troopNum.groupUin, - troopNum.groupCode, - troopNum.dwGroupOwnerUin + this.delegate.groupCode = troopNum.groupCode + }, + members = bot._lowLevelQueryGroupMemberList( + troopNum.groupUin, + troopNum.groupCode, + troopNum.dwGroupOwnerUin + ) + )) ) - )) - ) - }.exceptionOrNull()?.let { - logger.error { "群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试" } - logger.error(it) - this@QQAndroidBotNetworkHandler.launch { - delay(10_000) + }.exceptionOrNull()?.let { + logger.error { "群${troopNum.groupCode}的列表拉取失败, 一段时间后将会重试" } + logger.error(it) + this@QQAndroidBotNetworkHandler.launch { + delay(10_000) + loadGroup() + } + } + Unit // 别删, 编译失败警告 + } + launch { loadGroup() } } - Unit // 别删, 编译失败警告 - } - launch { - loadGroup() } } logger.info { "群组列表与群成员加载完成, 共 ${troopListData.groups.size}个" } @@ -322,17 +326,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } } - withTimeoutOrNull(5000) { - lateinit var listener: Listener - listener = this.subscribeAlways { - if (it.packet is MessageSvc.PbGetMsg.GetMsgSuccess) { - listener.complete() + supervisorScope { + withTimeoutOrNull(30000) { + lateinit var listener: Listener + listener = this.subscribeAlways { + if (it.packet is MessageSvc.PbGetMsg.GetMsgSuccess) { + listener.complete() + } } - } - - MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect() - } ?: error("timeout syncing friend message history") + MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect() + } ?: error("timeout syncing friend message history") + } bot.firstLoginSucceed = true _pendingEnabled.value = false diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt index cee8ab35e..a4d9ef3a0 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt @@ -33,7 +33,7 @@ import net.mamoe.mirai.data.GroupInfo as MiraiGroupInfo @OptIn(LowLevelAPI::class) internal class GroupInfoImpl( internal val delegate: Oidb0x88d.GroupInfo -) : MiraiGroupInfo, Packet { +) : MiraiGroupInfo, Packet, Packet.NoLog { override val uin: Long get() = delegate.groupUin ?: error("cannot find groupUin") override val owner: Long get() = delegate.groupOwner ?: error("cannot find groupOwner") override val groupCode: Long get() = Group.calculateGroupCodeByGroupUin(uin) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt index 9ea63e1ff..f2091dac8 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt @@ -35,11 +35,8 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.TroopTips0x857 import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgType0x210 -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.onlinePush0x210.OnlinePush0x210Factory import net.mamoe.mirai.qqandroid.utils.io.JceStruct import net.mamoe.mirai.qqandroid.utils.io.readString -import net.mamoe.mirai.qqandroid.utils.io.serialization.* import net.mamoe.mirai.qqandroid.utils.io.serialization.decodeUniPacket import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId import net.mamoe.mirai.qqandroid.utils.io.serialization.jceRequestSBuffer @@ -310,7 +307,14 @@ internal class OnlinePush { } 0x10 -> { val dataBytes = this.readBytes(26) - val message = this.readString(this.readByte().toInt()) + val size = this.readByte().toInt() // orthodox, don't `readUByte` + if (size < 0) { + error( + "negative array size: $size, remaining bytes=${this.readBytes() + .toUHexString()}" + ) + } + val message = this.readString(size) // println(dataBytes.toUHexString()) if (dataBytes[0].toInt() != 59) { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ByteArrayPool.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ByteArrayPool.kt index b4b49f6b5..82287aa17 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ByteArrayPool.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ByteArrayPool.kt @@ -9,7 +9,7 @@ internal object ByteArrayPool : DefaultPool(256) { /** * 每一个 [ByteArray] 的大小 */ - const val BUFFER_SIZE: Int = 81920 / 2 + const val BUFFER_SIZE: Int = 8192000 override fun produceInstance(): ByteArray = ByteArray(BUFFER_SIZE) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt index 8202e1364..479bf908a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt @@ -150,8 +150,8 @@ abstract class BotImpl constructor( suspend fun doInit() { tryNTimesOrException(2) { if (it != 0) { - delay(3000) logger.warning("Init failed. Retrying in 3s...") + delay(3000) } _network.init() }?.let {