Catch and forward event broadcasting to EventDispatcher

This commit is contained in:
Him188 2021-06-07 18:18:07 +08:00
parent 9cf45bd9ca
commit 9619338d5b
4 changed files with 56 additions and 7 deletions

View File

@ -19,6 +19,9 @@ import io.ktor.client.engine.okhttp.*
import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.UserProfile
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.broadcastImpl
import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent
import net.mamoe.mirai.event.events.MemberJoinRequestEvent
import net.mamoe.mirai.event.events.NewFriendRequestEvent
@ -299,6 +302,13 @@ public interface IMirai : LowLevelApiAccessor {
* @param event 邀请入群的事件对象
*/
public suspend fun ignoreInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent)
/**
* 广播一个事件. [Event.broadcast] 调用.
*/
public suspend fun broadcastEvent(event: Event) {
event.broadcastImpl()
}
}
/**

View File

@ -14,6 +14,7 @@ package net.mamoe.mirai.event
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.internal.event.callAndRemoveIfRequired
@ -144,7 +145,11 @@ public interface CancellableEvent : Event {
* @see __broadcastJava Java 使用
*/
@JvmSynthetic
public suspend fun <E : Event> E.broadcast(): E {
public suspend fun <E : Event> E.broadcast(): E = apply { Mirai.broadcastEvent(this) }
@JvmName("broadcastImpl") // avoid mangling
internal suspend fun <E : Event> E.broadcastImpl(): E {
val event = this
check(event is AbstractEvent) {
"Events must extend AbstractEvent"

View File

@ -15,22 +15,23 @@ import io.ktor.client.features.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
import kotlinx.serialization.json.*
import net.mamoe.mirai.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.*
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent
import net.mamoe.mirai.event.events.FriendAddEvent
import net.mamoe.mirai.event.events.MemberJoinRequestEvent
import net.mamoe.mirai.event.events.NewFriendRequestEvent
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.EventDispatcherScopeFlag
import net.mamoe.mirai.internal.network.highway.*
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcDevLoginInfo
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
@ -284,6 +285,20 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
override suspend fun ignoreInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent) =
solveInvitedJoinGroupRequest(event, accept = false)
override suspend fun broadcastEvent(event: Event) {
if (currentCoroutineContext()[EventDispatcherScopeFlag] != null) {
// called by [EventDispatcher]
return super.broadcastEvent(event)
}
if (event is BotEvent) {
val bot = event.bot
if (bot is QQAndroidBot) {
bot.components[EventDispatcher].broadcast(event)
}
} else {
super.broadcastEvent(event)
}
}
private suspend fun solveInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent, accept: Boolean) {
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")

View File

@ -19,8 +19,18 @@ import net.mamoe.mirai.utils.childScope
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/**
* All events will be caught and forwarded to [EventDispatcher]. Invocation of [Event.broadcast] and [EventDispatcher.broadcast] are effectively equal.
*/
internal interface EventDispatcher {
/**
* Implementor must call `event.broadcast()` within a coroutine with [EventDispatcherScopeFlag]
*/
suspend fun broadcast(event: Event)
/**
* Implementor must call `event.broadcast()` within a coroutine with [EventDispatcherScopeFlag]
*/
fun broadcastAsync(event: Event, additionalContext: CoroutineContext = EmptyCoroutineContext): EventBroadcastJob
/**
@ -31,6 +41,10 @@ internal interface EventDispatcher {
companion object : ComponentKey<EventDispatcher>
}
internal object EventDispatcherScopeFlag : CoroutineContext.Element, CoroutineContext.Key<EventDispatcherScopeFlag> {
override val key: CoroutineContext.Key<*> get() = this
}
@JvmInline
internal value class EventBroadcastJob(
private val job: Job
@ -53,7 +67,9 @@ internal class EventDispatcherImpl(
override suspend fun broadcast(event: Event) {
try {
event.broadcast()
withContext(EventDispatcherScopeFlag) {
event.broadcast()
}
} catch (e: Exception) {
if (logger.isEnabled) {
val msg = optimizeEventToString(event)
@ -63,7 +79,10 @@ internal class EventDispatcherImpl(
}
override fun broadcastAsync(event: Event, additionalContext: CoroutineContext): EventBroadcastJob {
val job = launch(additionalContext, start = CoroutineStart.UNDISPATCHED) { broadcast(event) }
val job = launch(
additionalContext + EventDispatcherScopeFlag,
start = CoroutineStart.UNDISPATCHED
) { broadcast(event) }
// 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)