Просмотр исходного кода

Fix #470: Network Protocol: java.lang.IllegalStateException: returnCode = -10008

Him188 5 лет назад
Родитель
Сommit
43407aa427

+ 2 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/BotImpl.kt

@@ -82,7 +82,8 @@ internal abstract class BotImpl<N : BotNetworkHandler> constructor(
             when (event) {
                 is BotOfflineEvent.MsfOffline,
                 is BotOfflineEvent.Dropped,
-                is BotOfflineEvent.RequireReconnect
+                is BotOfflineEvent.RequireReconnect,
+                is BotOfflineEvent.PacketFactory10008
                 -> {
                     if (!_network.isActive) {
                         // normally closed

+ 11 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt

@@ -24,6 +24,7 @@ import net.mamoe.mirai.event.events.BotOfflineEvent
 import net.mamoe.mirai.event.events.BotOnlineEvent
 import net.mamoe.mirai.event.events.BotReloginEvent
 import net.mamoe.mirai.message.MessageEvent
+import net.mamoe.mirai.network.ForceOfflineException
 import net.mamoe.mirai.network.RetryLaterException
 import net.mamoe.mirai.network.UnsupportedSMSLoginException
 import net.mamoe.mirai.network.WrongPasswordException
@@ -32,6 +33,7 @@ import net.mamoe.mirai.qqandroid.contact.*
 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopNum
 import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc
 import net.mamoe.mirai.qqandroid.network.protocol.packet.*
+import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories.PacketFactoryIllegalState10008Exception
 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbGetMsg
 import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
@@ -456,7 +458,14 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
         return this.launch(
             start = CoroutineStart.ATOMIC
         ) {
-            input.use { parsePacket(it) }
+            input.use {
+                try {
+                    parsePacket(it)
+                } catch (e: PacketFactoryIllegalState10008Exception) {
+                    logger.warning { "Network force offline: ${e.message}" }
+                    bot.launch { BotOfflineEvent.PacketFactory10008(bot, e).broadcast() }
+                }
+            }
         }
     }
 
@@ -466,6 +475,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
      *
      * @param input 一个完整的包的内容, 去掉开头的 int 包长度
      */
+    @Throws(ForceOfflineException::class)
     suspend fun parsePacket(input: ByteReadPacket) {
         generifiedParsePacket<Packet>(input)
     }

+ 20 - 6
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt

@@ -37,6 +37,7 @@ import net.mamoe.mirai.qqandroid.utils.io.useBytes
 import net.mamoe.mirai.qqandroid.utils.io.withUse
 import net.mamoe.mirai.utils.*
 import kotlin.jvm.JvmName
+import kotlin.jvm.JvmOverloads
 
 internal sealed class PacketFactory<TPacket : Packet?> {
     /**
@@ -176,6 +177,11 @@ internal object KnownPacketFactories {
             ?: IncomingFactories.firstOrNull { it.receivingCommandName == commandName }
     }
 
+    class PacketFactoryIllegalState10008Exception @JvmOverloads constructor(
+        override val message: String? = null,
+        override val cause: Throwable? = null
+    ) : RuntimeException()
+
     // do not inline. Exceptions thrown will not be reported correctly
     @Suppress("UNCHECKED_CAST")
     suspend fun <T : Packet?> parseIncomingPacket(
@@ -247,11 +253,13 @@ internal object KnownPacketFactories {
             bot.network.logger.debug { "Received unknown commandName: ${it.commandName}" }
             PacketLogger.warning { "找不到 PacketFactory" }
             PacketLogger.verbose {
-                "传递给 PacketFactory 的数据 = ${it.data.useBytes { data, length ->
-                    data.toUHexString(
-                        length = length
-                    )
-                }}"
+                "传递给 PacketFactory 的数据 = ${
+                    it.data.useBytes { data, length ->
+                        data.toUHexString(
+                            length = length
+                        )
+                    }
+                }"
             }
             return
         }
@@ -304,8 +312,14 @@ internal object KnownPacketFactories {
         input.readPacketExact(input.readInt() - 4).withUse {
             ssoSequenceId = readInt()
             PacketLogger.verbose { "sequenceId = $ssoSequenceId" }
+
             val returnCode = readInt()
-            check(returnCode == 0) { "returnCode = $returnCode" }
+            check(returnCode == 0) {
+                if (returnCode == -10008) { // https://github.com/mamoe/mirai/issues/470
+                    throw PacketFactoryIllegalState10008Exception("returnCode = $returnCode")
+                } else "returnCode = $returnCode"
+            }
+
             if (PacketLogger.isEnabled) {
                 val extraData = readBytes(readInt() - 4)
                 PacketLogger.verbose { "(sso/inner)extraData = ${extraData.toUHexString()}" }

+ 14 - 11
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/bot.kt

@@ -43,8 +43,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
     public data class Active(
         public override val bot: Bot,
         public override val cause: Throwable?
-    ) : BotOfflineEvent(), BotActiveEvent,
-        CauseAware
+    ) : BotOfflineEvent(), BotActiveEvent, CauseAware
 
     /**
      * 被挤下线
@@ -53,9 +52,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
         public override val bot: Bot,
         public val title: String,
         public val message: String
-    ) :
-        BotOfflineEvent(), Packet,
-        BotPassiveEvent
+    ) : BotOfflineEvent(), Packet, BotPassiveEvent
 
     /**
      * 被服务器断开
@@ -65,9 +62,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
     public data class MsfOffline internal constructor(
         public override val bot: Bot,
         public override val cause: Throwable?
-    ) :
-        BotOfflineEvent(), Packet,
-        BotPassiveEvent, CauseAware
+    ) : BotOfflineEvent(), Packet, BotPassiveEvent, CauseAware
 
     /**
      * 因网络问题而掉线
@@ -75,9 +70,17 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
     public data class Dropped internal constructor(
         public override val bot: Bot,
         public override val cause: Throwable?
-    ) : BotOfflineEvent(),
-        Packet,
-        BotPassiveEvent, CauseAware
+    ) : BotOfflineEvent(), Packet, BotPassiveEvent, CauseAware
+
+    /**
+     * 因 returnCode = -10008 等原因掉线
+     */
+    @MiraiInternalAPI("This is very experimental and might be changed")
+    @SinceMirai("1.2.0")
+    public data class PacketFactory10008 internal constructor(
+        public override val bot: Bot,
+        public override val cause: Throwable
+    ) : BotOfflineEvent(), Packet, BotPassiveEvent, CauseAware
 
     /**
      * 服务器主动要求更换另一个服务器

+ 6 - 1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/ForceOfflineException.kt

@@ -3,8 +3,13 @@ package net.mamoe.mirai.network
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.Job
 import net.mamoe.mirai.Bot
+import kotlin.jvm.JvmOverloads
 
 /**
  * 当 [Bot] 被迫下线时抛出, 作为 [Job.cancel] 的 `cause`
  */
-public class ForceOfflineException(public override val message: String?) : CancellationException(message)
+public class ForceOfflineException
+@JvmOverloads constructor(
+    public override val message: String? = null,
+    public override val cause: Throwable? = null
+) : CancellationException(message)