1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-25 21:12:40 +08:00

Fix event broadcast directly without checks. Add notes for broadcasting an event.

This commit is contained in:
Him188 2022-05-14 10:46:26 +01:00
parent f83d20bed9
commit 4495e36e7c
6 changed files with 55 additions and 36 deletions
mirai-core-api/src/commonMain/kotlin/event
mirai-core/src/commonMain/kotlin

View File

@ -64,7 +64,7 @@ public fun CoroutineScope.globalEventChannel(coroutineContext: CoroutineContext
}
/**
* @since 2.11
* @since 2.12
*/
@MiraiInternalApi
public interface InternalGlobalEventChannelProvider {

View File

@ -14,8 +14,6 @@ import io.ktor.client.engine.okhttp.*
import io.ktor.client.features.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.util.*
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
import kotlinx.serialization.json.Json
@ -34,6 +32,7 @@ import net.mamoe.mirai.internal.contact.info.FriendInfoImpl.Companion.impl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl.Companion.impl
import net.mamoe.mirai.internal.event.EventChannelToEventDispatcherAdapter
import net.mamoe.mirai.internal.event.InternalEventMechanism
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
import net.mamoe.mirai.internal.network.components.EventDispatcher
@ -299,6 +298,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
override suspend fun ignoreInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent) =
solveInvitedJoinGroupRequest(event, accept = false)
@OptIn(InternalEventMechanism::class)
override suspend fun broadcastEvent(event: Event) {
if (event is BotEvent) {
val bot = event.bot
@ -306,7 +306,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
bot.components[EventDispatcher].broadcast(event)
}
} else {
EventChannelToEventDispatcherAdapter.instance.callListeners(event)
EventChannelToEventDispatcherAdapter.instance.broadcastEventImpl(event)
}
}

View File

@ -20,6 +20,7 @@ import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.SHOW_VERBOSE_EVENT
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.verbose
@ -27,14 +28,27 @@ import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KClass
// You probably should only use EventChannelToEventDispatcherAdapter.instance, or just use EventDispatchers. Event.broadcast is also good to use internally!
// See docs below
@RequiresOptIn(
"Every EventChannelImpl has dedicated EventListeners registries. Use the constructor only when you know what you are doing.",
"You must not use this API unless you are writing Event infrastructure.",
level = RequiresOptIn.Level.ERROR
)
internal annotation class DangerousEventChannelImplConstructor
internal annotation class InternalEventMechanism
internal open class EventChannelImpl<E : Event> @DangerousEventChannelImplConstructor constructor(
// Note: You probably should only use EventChannelToEventDispatcherAdapter.instance, or just use EventDispatchers. Event.broadcast is also good to use internally!
/**
* Implementation of [EventChannel]. Every [EventChannelImpl] holds its own list of listeners, so one [EventChannelImpl] instance, [EventChannelToEventDispatcherAdapter] is shared in mirai.
*
* ## Broadcasting an event
*
* You should first consider using [EventDispatcher.broadcast] or [EventDispatcher.broadcastAsync].
* Use [Event.broadcast] if you have no access to [EventDispatcher] (though you should have).
*
* Note: using [Event.broadcast] for [BotEvent] is the same as using [EventDispatcher.broadcast] but the former is slower. So it's recommended to use [EventDispatcher].
*
* **Never ever** use [EventChannelToEventDispatcherAdapter.instance] directly.
*/
internal sealed class EventChannelImpl<E : Event> constructor(
baseEventClass: KClass<out E>, defaultCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : EventChannel<E>(baseEventClass, defaultCoroutineContext) {
private val eventListeners = EventListeners()
@ -46,7 +60,26 @@ internal open class EventChannelImpl<E : Event> @DangerousEventChannelImplConstr
private val logger by lazy { MiraiLogger.Factory.create(EventChannelImpl::class, "EventChannelImpl") }
}
suspend fun callListeners(event: Event) {
/**
* Basic entrance for broadcasting an event.
*/
@InternalEventMechanism
suspend fun <E : Event> broadcastEventImpl(event: E): E {
require(event is AbstractEvent) { "Events must extend AbstractEvent" }
if (event is BroadcastControllable && !event.shouldBroadcast) {
return event
}
event.broadCastLock.withLock {
event._intercepted = false
callListeners(event)
}
return event
}
@InternalEventMechanism
private suspend fun callListeners(event: Event) {
event as AbstractEvent
logEvent(event)
eventListeners.callListeners(event)
@ -78,21 +111,6 @@ internal open class EventChannelImpl<E : Event> @DangerousEventChannelImplConstr
)
}
private suspend fun <E : Event> broadcastImpl(event: E): E {
check(event is AbstractEvent) { "Events must extend AbstractEvent" }
if (event is BroadcastControllable && !event.shouldBroadcast) {
return event
}
event.broadCastLock.withLock {
event._intercepted = false
callListeners(event)
}
return event
}
private fun isVerboseEvent(event: Event): Boolean {
if (SHOW_VERBOSE_EVENT) return false
if (event is VerboseEvent) {

View File

@ -14,11 +14,11 @@ import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KClass
@OptIn(DangerousEventChannelImplConstructor::class)
internal class EventChannelToEventDispatcherAdapter<E : Event> private constructor(
baseEventClass: KClass<out E>, defaultCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : EventChannelImpl<E>(baseEventClass, defaultCoroutineContext) {
companion object {
@InternalEventMechanism
val instance by lazy { EventChannelToEventDispatcherAdapter(Event::class, EmptyCoroutineContext) }
}
}
}

View File

@ -13,7 +13,11 @@ import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.EventChannel
import net.mamoe.mirai.event.InternalGlobalEventChannelProvider
@InternalEventMechanism
internal class GlobalEventChannelProviderImpl : InternalGlobalEventChannelProvider {
@InternalEventMechanism
val instance = EventChannelToEventDispatcherAdapter.instance
@OptIn(InternalEventMechanism::class)
override fun getInstance(): EventChannel<Event> = instance
}

View File

@ -10,11 +10,13 @@
package net.mamoe.mirai.internal.network.components
import kotlinx.coroutines.*
import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.EventChannel
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.internal.event.EventChannelToEventDispatcherAdapter
import net.mamoe.mirai.internal.event.InternalEventMechanism
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.utils.*
import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@ -25,18 +27,12 @@ internal interface EventDispatcher {
val isActive: Boolean
/**
* Implement [Event.broadcast]
* Broadcast an event using [EventChannel]. It's safe to use this function internally.
*/
suspend fun broadcast(event: Event)
/**
* Implementor must call `event.broadcast()` within a coroutine with [EventDispatcherScopeFlag]
*/
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?,
@ -96,9 +92,10 @@ internal open class EventDispatcherImpl(
override val isActive: Boolean
get() = this.coroutineContext.isActive
@OptIn(InternalEventMechanism::class)
override suspend fun broadcast(event: Event) {
try {
EventChannelToEventDispatcherAdapter.instance.callListeners(event)
EventChannelToEventDispatcherAdapter.instance.broadcastEventImpl(event)
} catch (e: Exception) {
if (e is CancellationException) return
if (logger.isEnabled) {