From b02fa15b33d73e74445e771cd0b238e9e4f7b788 Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 24 Jun 2021 01:04:03 +0800 Subject: [PATCH] Broadcast BotReloginEvent stably --- .../src/commonMain/kotlin/QQAndroidBot.kt | 10 ++++--- .../network/components/EventDispatcher.kt | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt index 510a6d41e..797c7df9a 100644 --- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt +++ b/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.NettyNetworkHandlerFactory 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 internal fun Bot.asQQAndroidBot(): QQAndroidBot { @@ -91,10 +93,10 @@ internal open class QQAndroidBot constructor( previous: BaseStateImpl, new: BaseStateImpl ) { - eventDispatcher.broadcastAsync(BotOnlineEvent(bot)).onSuccess { + eventDispatcher.broadcastAsync(BotOnlineEvent(bot)).thenBroadcast(eventDispatcher) { if (!shouldBroadcastRelogin.compareAndSet(false, true)) { - eventDispatcher.broadcastAsync(BotReloginEvent(bot, new.getCause())) - } + BotReloginEvent(bot, new.getCause()) + } else null } } diff --git a/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt b/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt index fde477c12..52b91ba2d 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt @@ -34,6 +34,14 @@ internal interface EventDispatcher { */ 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. */ @@ -58,6 +66,13 @@ internal value class EventBroadcastJob( 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) } + 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 { val qualified = event::class.java.canonicalName ?: return event.toString() return qualified.substringAfter("net.mamoe.mirai.event.events.", "").ifEmpty { event.toString() }