浏览代码

Broadcast BotReloginEvent stably

Him188 4 年之前
父节点
当前提交
b02fa15b33

+ 6 - 4
mirai-core/src/commonMain/kotlin/QQAndroidBot.kt

@@ -43,7 +43,9 @@ import net.mamoe.mirai.internal.network.handler.state.safe
 import net.mamoe.mirai.internal.network.impl.netty.ForceOfflineException
 import net.mamoe.mirai.internal.network.impl.netty.ForceOfflineException
 import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandlerFactory
 import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandlerFactory
 import net.mamoe.mirai.internal.utils.subLogger
 import net.mamoe.mirai.internal.utils.subLogger
-import net.mamoe.mirai.utils.*
+import net.mamoe.mirai.utils.BotConfiguration
+import net.mamoe.mirai.utils.MiraiLogger
+import net.mamoe.mirai.utils.lateinitMutableProperty
 import kotlin.contracts.contract
 import kotlin.contracts.contract
 
 
 internal fun Bot.asQQAndroidBot(): QQAndroidBot {
 internal fun Bot.asQQAndroidBot(): QQAndroidBot {
@@ -91,10 +93,10 @@ internal open class QQAndroidBot constructor(
                     previous: BaseStateImpl,
                     previous: BaseStateImpl,
                     new: BaseStateImpl
                     new: BaseStateImpl
                 ) {
                 ) {
-                    eventDispatcher.broadcastAsync(BotOnlineEvent(bot)).onSuccess {
+                    eventDispatcher.broadcastAsync(BotOnlineEvent(bot)).thenBroadcast(eventDispatcher) {
                         if (!shouldBroadcastRelogin.compareAndSet(false, true)) {
                         if (!shouldBroadcastRelogin.compareAndSet(false, true)) {
-                            eventDispatcher.broadcastAsync(BotReloginEvent(bot, new.getCause()))
-                        }
+                            BotReloginEvent(bot, new.getCause())
+                        } else null
                     }
                     }
                 }
                 }
 
 

+ 27 - 0
mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt

@@ -34,6 +34,14 @@ internal interface EventDispatcher {
      */
      */
     fun broadcastAsync(event: Event, additionalContext: CoroutineContext = EmptyCoroutineContext): EventBroadcastJob
     fun broadcastAsync(event: Event, additionalContext: CoroutineContext = EmptyCoroutineContext): EventBroadcastJob
 
 
+    /**
+     * Implementor must call `event.broadcast()` within a coroutine with [EventDispatcherScopeFlag]
+     */
+    fun broadcastAsync(
+        additionalContext: CoroutineContext = EmptyCoroutineContext,
+        event: suspend () -> Event?,
+    ): EventBroadcastJob
+
     /**
     /**
      * Join all jobs. Joins also jobs launched during this call.
      * Join all jobs. Joins also jobs launched during this call.
      */
      */
@@ -58,6 +66,13 @@ internal value class EventBroadcastJob(
             if (it == null) action()
             if (it == null) action()
         }
         }
     }
     }
+
+    inline fun thenBroadcast(eventDispatcher: EventDispatcher, crossinline event: suspend () -> Event?) {
+        eventDispatcher.broadcastAsync {
+            job.join()
+            event()
+        }
+    }
 }
 }
 
 
 
 
@@ -93,6 +108,18 @@ internal open class EventDispatcherImpl(
         return EventBroadcastJob(job)
         return EventBroadcastJob(job)
     }
     }
 
 
+    override fun broadcastAsync(additionalContext: CoroutineContext, event: suspend () -> Event?): EventBroadcastJob {
+        val job = launch(
+            additionalContext + EventDispatcherScopeFlag,
+            start = CoroutineStart.UNDISPATCHED
+        ) {
+            event()?.let { broadcast(it) }
+        }
+        // UNDISPATCHED: starts the coroutine NOW in the current thread until its first suspension point,
+        // so that after `broadcastAsync` the job is always already started and `joinBroadcast` will work normally.
+        return EventBroadcastJob(job)
+    }
+
     protected fun optimizeEventToString(event: Event): String {
     protected fun optimizeEventToString(event: Event): String {
         val qualified = event::class.java.canonicalName ?: return event.toString()
         val qualified = event::class.java.canonicalName ?: return event.toString()
         return qualified.substringAfter("net.mamoe.mirai.event.events.", "").ifEmpty { event.toString() }
         return qualified.substringAfter("net.mamoe.mirai.event.events.", "").ifEmpty { event.toString() }