From 6ef78118ad60607e444996e7fedf3fcd18341058 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 31 Jan 2020 23:10:31 +0800 Subject: [PATCH] Fix bugs --- .../network/QQAndroidBotNetworkHandler.kt | 97 ++++++++----------- .../network/protocol/data/jce/FriendList.kt | 2 +- .../protocol/packet/OutgoingPacketAndroid.kt | 1 - .../protocol/packet/login/LoginPacket.kt | 13 ++- .../network/TIMPCBotNetworkHandler.kt | 2 +- .../kotlin/net.mamoe.mirai/BotAccount.kt | 3 +- .../kotlin/net.mamoe.mirai/BotImpl.kt | 12 +-- .../network/BotNetworkHandler.kt | 8 +- 8 files changed, 59 insertions(+), 79 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 2b5b577ea..ae2921bb9 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 @@ -3,29 +3,29 @@ package net.mamoe.mirai.qqandroid.network import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.atomic import kotlinx.coroutines.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.Input import kotlinx.io.core.buildPacket import kotlinx.io.core.use -import net.mamoe.mirai.contact.Contact -import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.Packet -import net.mamoe.mirai.event.BroadcastControllable -import net.mamoe.mirai.event.Cancellable -import net.mamoe.mirai.event.Subscribable -import net.mamoe.mirai.event.broadcast +import net.mamoe.mirai.event.* import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQImpl +import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.network.protocol.packet.* import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc -import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.cryptor.contentToString +import net.mamoe.mirai.utils.LockFreeLinkedList +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.getValue import net.mamoe.mirai.utils.io.* +import net.mamoe.mirai.utils.unsafeWeakRef import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -41,14 +41,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler private lateinit var channel: PlatformSocket - override suspend fun login() { if (::channel.isInitialized) { channel.close() } channel = PlatformSocket() channel.connect("113.96.13.208", 8080) - launch(CoroutineName("Incoming Packet Receiver")) { processReceive() } + this.launch(CoroutineName("Incoming Packet Receiver")) { processReceive() } // bot.logger.info("Trying login") var response: LoginPacket.LoginPacketResponse = LoginPacket.SubCommand9(bot.client).sendAndExpect() @@ -104,27 +103,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } override suspend fun init() { - //start updating friend/group list - bot.logger.info("Start updating friend/group list") - - /* - val data = FriendList.GetFriendGroupList( - bot.client, - 0, - 20, - 0, - 0 - ).sendAndExpect() - - - println(data.contentToString()) - */ + bot.logger.info("开始加载好友信息") + this@QQAndroidBotNetworkHandler.subscribeAlways { + if (this@QQAndroidBotNetworkHandler.bot == this.bot) { + close() + } + } /* * 开始加载Contact表 * */ var currentFriendCount = 0 - var totalFriendCount: Short = 0 + var totalFriendCount: Short while (true) { val data = FriendList.GetFriendGroupList( bot.client, @@ -134,14 +124,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler 0 ).sendAndExpect() totalFriendCount = data.totalFriendCount - bot.qqs.delegate.addAll( - data.friendList.map { - QQImpl(this@QQAndroidBotNetworkHandler.bot, EmptyCoroutineContext, it.friendUin!!).also { - currentFriendCount++ - } - } - ) - bot.logger.info("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}") + data.friendList.forEach { + // atomic add + bot.qqs.delegate.addLast(QQImpl(bot, EmptyCoroutineContext, it.friendUin).also { + currentFriendCount++ + }) + } + bot.logger.verbose("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}") if (currentFriendCount >= totalFriendCount) { break } @@ -155,22 +144,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ).sendAndExpect(100000) println(data.contentToString()) */ - } - - /** - * 单线程处理包的接收, 分割和连接. - */ - @Suppress("PrivatePropertyName") - private val PacketReceiveDispatcher = newCoroutineDispatcher(1) - - /** - * 单线程处理包的解析 (协程挂起效率够) - */ - @Suppress("PrivatePropertyName") - private val PacketProcessDispatcher = newCoroutineDispatcher(1) - /** * 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理 */ @@ -185,12 +160,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler private var expectingRemainingLength: Long = 0 /** - * 在 [PacketProcessDispatcher] 调度器中解析包内容. + * 解析包内容. * * @param input 一个完整的包的内容, 去掉开头的 int 包长度 */ fun parsePacketAsync(input: Input): Job { - return this.launch(PacketProcessDispatcher) { + return this.launch { input.use { parsePacket(it) } } } @@ -337,28 +312,32 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler val rawInput = try { channel.read() } catch (e: ClosedChannelException) { - dispose() + close() bot.tryReinitializeNetworkHandler(e) return } catch (e: ReadPacketInternalException) { bot.logger.error("Socket channel read failed: ${e.message}") - dispose() + close() bot.tryReinitializeNetworkHandler(e) return } catch (e: CancellationException) { return } catch (e: Throwable) { bot.logger.error("Caught unexpected exceptions", e) - dispose() + close() bot.tryReinitializeNetworkHandler(e) return } - launch(context = PacketReceiveDispatcher + CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) { - processPacket(rawInput) + launch(CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) { + packetReceiveLock.withLock { + processPacket(rawInput) + } } } } + private val packetReceiveLock: Mutex = Mutex() + /** * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms) */ @@ -381,7 +360,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } private suspend inline fun OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E { - channel.send(delegate) + withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) { + channel.send(delegate) + } bot.logger.info("Send: ${this.commandName}") return withTimeoutOrNull(timeoutMillis) { @Suppress("UNCHECKED_CAST") @@ -403,11 +384,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId } - override fun dispose(cause: Throwable?) { + override fun close(cause: Throwable?) { if (::channel.isInitialized) { channel.close() } - super.dispose(cause) + super.close(cause) } override suspend fun awaitDisconnection() = supervisor.join() diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt index 1f979fc26..0e3529024 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt @@ -67,7 +67,7 @@ internal class FriendListSubSrvRspCode( @Serializable internal class FriendInfo( - @SerialId(0) val friendUin: Long? = 0, + @SerialId(0) val friendUin: Long, @SerialId(1) val groupId: Byte, @SerialId(2) val faceId: Short, @SerialId(3) val remark: String = "", 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 f1b05fd0a..7eb5d35c1 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 @@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ * 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket]. * 只有最终的包才会被包装为 [OutgoingPacket]. */ -@UseExperimental(ExperimentalUnsignedTypes::class) internal class OutgoingPacket constructor( name: String?, val commandName: String, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index 0ec5f14f2..7b031ecdd 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -12,7 +12,6 @@ import net.mamoe.mirai.qqandroid.utils.GuidSource import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag import net.mamoe.mirai.qqandroid.utils.guidFlag import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.discardExact @@ -317,13 +316,13 @@ internal object LoginPacket : PacketFactory("wt val subCommand = readUShort().toInt() - println("subCommand=$subCommand") + // println("subCommand=$subCommand") val type = readUByte() - println("type=$type") + // println("type=$type") discardExact(2) val tlvMap: TlvMap = this.readTLVMap() - tlvMap.printTLVMap() + // tlvMap.printTLVMap() return when (type.toInt()) { 0 -> onLoginSuccess(tlvMap, bot) 1, 15 -> onErrorMessage(tlvMap) @@ -340,7 +339,7 @@ internal object LoginPacket : PacketFactory("wt bot: QQAndroidBot ): LoginPacketResponse.DeviceLockLogin { bot.client.t104 = tlvMap.getOrFail(0x104) - println("403: " + tlvMap[0x403]?.toUHexString()) + // println("403: " + tlvMap[0x403]?.toUHexString()) return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403)) } @@ -392,10 +391,10 @@ internal object LoginPacket : PacketFactory("wt @UseExperimental(MiraiDebugAPI::class) private fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { val client = bot.client - println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) + //println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) tlvMap[0x150]?.let { client.analysisTlv150(it) } - tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } + // tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } tlvMap[0x161]?.let { client.analysisTlv161(it) } tlvMap[0x119]?.let { t119Data -> t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply { diff --git a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/TIMPCBotNetworkHandler.kt b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/TIMPCBotNetworkHandler.kt index b3140ee04..c4ec231f1 100644 --- a/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/TIMPCBotNetworkHandler.kt +++ b/mirai-core-timpc/src/commonMain/kotlin/net.mamoe.mirai.timpc/network/TIMPCBotNetworkHandler.kt @@ -92,7 +92,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor } override fun dispose(cause: Throwable?) { - super.dispose(cause) + super.close(cause) this.heartbeatJob?.cancel(CancellationException("handler closed")) this.heartbeatJob = null diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt index 63cd58025..8b425a220 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt @@ -22,7 +22,8 @@ data class BotAccount( /** * 标记直接访问 [BotAccount.id], 而不是访问 [Bot.uin]. 这可能会不兼容未来的 API 修改. */ +@MiraiInternalAPI @Retention(AnnotationRetention.SOURCE) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) @Experimental(level = Experimental.Level.WARNING) -internal annotation class RawAccountIdUse \ No newline at end of file +annotation class RawAccountIdUse \ No newline at end of file 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 6a6293f31..52973abbe 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt @@ -92,20 +92,20 @@ abstract class BotImpl constructor( try { if (::_network.isInitialized) { BotOfflineEvent(this).broadcast() - _network.dispose(cause) + _network.close(cause) } } catch (e: Exception) { logger.error("Cannot close network handler", e) } - _network = createNetworkHandler(this.coroutineContext) loginLoop@ while (true) { + _network = createNetworkHandler(this.coroutineContext) try { _network.login() break@loginLoop } catch (e: Exception) { e.logStacktrace() - _network.dispose(e) + _network.close(e) } logger.warning("Login failed. Retrying in 3s...") delay(3000) @@ -116,7 +116,7 @@ abstract class BotImpl constructor( return _network.init() } catch (e: Exception) { e.logStacktrace() - _network.dispose(e) + _network.close(e) } logger.warning("Init failed. Retrying in 3s...") delay(3000) @@ -130,12 +130,12 @@ abstract class BotImpl constructor( @UseExperimental(MiraiInternalAPI::class) override fun dispose(throwable: Throwable?) { if (throwable == null) { - network.dispose() + network.close() this.botJob.complete() groups.delegate.clear() qqs.delegate.clear() } else { - network.dispose(throwable) + network.close(throwable) this.botJob.completeExceptionally(throwable) groups.delegate.clear() qqs.delegate.clear() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt index 35042adef..ee8d11a5a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt @@ -24,7 +24,7 @@ import net.mamoe.mirai.utils.io.PlatformDatagramChannel * - Key 刷新 * - 所有数据包处理和发送 * - * [BotNetworkHandler.dispose] 时将会 [取消][Job.cancel] 所有此作用域下的协程 + * [BotNetworkHandler.close] 时将会 [取消][Job.cancel] 所有此作用域下的协程 */ @Suppress("PropertyName") abstract class BotNetworkHandler : CoroutineScope { @@ -64,12 +64,12 @@ abstract class BotNetworkHandler : CoroutineScope { /** * 关闭网络接口, 停止所有有关协程和任务 */ - open fun dispose(cause: Throwable? = null) { + open fun close(cause: Throwable? = null) { if (supervisor.isActive) { if (cause != null) { - supervisor.cancel(CancellationException("handler closed", cause)) + supervisor.cancel(CancellationException("NetworkHandler closed", cause)) } else { - supervisor.cancel() + supervisor.cancel(CancellationException("NetworkHandler closed")) } } }