Commit e4e526ca authored by Him188's avatar Him188

Support object implementations of Event

parent 23273718
......@@ -12,11 +12,14 @@
package net.mamoe.mirai.event
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.JavaFriendlyAPI
import net.mamoe.mirai.event.internal.broadcastInternal
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
import net.mamoe.mirai.utils.internal.runBlocking
import kotlin.jvm.JvmField
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
import kotlin.jvm.Volatile
......@@ -28,6 +31,9 @@ import kotlin.jvm.Volatile
*
* 所有 [Event] 都应继承 [AbstractEvent] 而不要直接实现 [Event]. 否则将无法广播也无法监听.
*
* ### 广播
* 广播事件的唯一方式为 [broadcast].
*
* @see subscribeAlways
* @see subscribeOnce
*
......@@ -53,7 +59,9 @@ interface Event {
*
* 当事件被 [拦截][Event.intercept] 后, 优先级较低 (靠右) 的监听器将不会被调用.
*
* @see [Listener.EventPriority] 查看优先级相关信息
* 优先级为 [Listener.EventPriority.MONITOR] 的监听器不应该调用这个函数.
*
* @see Listener.EventPriority 查看优先级相关信息
*/
@SinceMirai("1.0.0")
fun intercept()
......@@ -81,8 +89,13 @@ abstract class AbstractEvent : Event {
final override val DoNotImplementThisClassButExtendAbstractEvent: Nothing
get() = throw Error("Shouldn't be reached")
/** 限制一个事件实例不能并行广播. (适用于 object 广播的情况) */
@JvmField
internal val broadCastLock = Mutex()
@JvmField
@Volatile
private var _intercepted = false
internal var _intercepted = false
@Volatile
private var _cancelled = false
......@@ -142,14 +155,24 @@ interface CancellableEvent : Event {
/**
* 广播一个事件的唯一途径.
* @see __broadcastJava
*
* 当事件被实现为 Kotlin `object` 时, 同一时刻只能有一个 [广播][broadcast] 存在. 较晚执行的 [广播][broadcast] 将会挂起协程并等待之前的广播任务结束.
*
* @see __broadcastJava Java 使用
*/
@JvmSynthetic
suspend fun <E : Event> E.broadcast(): E = apply {
check(this is AbstractEvent) {
"Events must extend AbstractEvent"
}
if (this is BroadcastControllable && !this.shouldBroadcast) {
return@apply
}
this@broadcast.broadcastInternal() // inline, no extra cost
this.broadCastLock.withLock {
this._intercepted = false
this.broadcastInternal() // inline, no extra cost
}
}
/**
......@@ -164,7 +187,7 @@ fun <E : Event> E.__broadcastJava(): E = apply {
if (this is BroadcastControllable && !this.shouldBroadcast) {
return@apply
}
runBlocking { this@__broadcastJava.broadcastInternal() }
runBlocking { this@__broadcastJava.broadcast() }
}
/**
......
......@@ -126,13 +126,13 @@ internal expect class MiraiAtomicBoolean(initial: Boolean) {
// inline: NO extra Continuation
@Suppress("UNCHECKED_CAST")
internal suspend inline fun Event.broadcastInternal() {
internal suspend inline fun AbstractEvent.broadcastInternal() {
if (EventDisabled) return
callAndRemoveIfRequired(this@broadcastInternal as? AbstractEvent ?: error("Events must extends AbstractEvent"))
callAndRemoveIfRequired(this@broadcastInternal)
}
@Suppress("DuplicatedCode")
internal suspend fun <E : AbstractEvent> callAndRemoveIfRequired(
internal suspend inline fun <E : AbstractEvent> callAndRemoveIfRequired(
event: E
) {
for (p in Listener.EventPriority.valuesExceptMonitor) {
......
......@@ -28,9 +28,10 @@ import kotlin.reflect.jvm.kotlinFunction
*
* ### Kotlin 函数
* Kotlin 函数要求:
* - 接收者和函数参数: 所标注的 Kotlin 函数必须至少拥有一个接收者或一个函数参数, 或二者都具有. 接收者和函数参数的类型必须相同 (如果二者都有)
* - 接收者 (英 receiver) 和函数参数: 所标注的 Kotlin 函数必须至少拥有一个接收者或一个函数参数, 或二者都具有. 接收者和函数参数的类型必须相同 (如果二者都存在)
* 接收者或函数参数的类型都必须为 [Event] 或其子类.
* - 返回值: 为 [Unit] 或不指定返回值时将注册为 [CoroutineScope.subscribeAlways], 为 [ListeningStatus] 时将注册为 [CoroutineScope.subscribe]
* - 返回值: 为 [Unit] 或不指定返回值时将注册为 [CoroutineScope.subscribeAlways], 为 [ListeningStatus] 时将注册为 [CoroutineScope.subscribe].
* 任何其他类型的返回值将会在注册时抛出异常.
*
* 所有 Kotlin 非 `suspend` 的函数都将会在 [Dispatchers.IO] 中调用
*
......@@ -118,8 +119,8 @@ import kotlin.reflect.jvm.kotlinFunction
annotation class EventHandler(
/**
* 监听器优先级
* @see Listener.EventPriority
* @see Event.intercept
* @see Listener.EventPriority 查看优先级相关信息
* @see Event.intercept 拦截事件
*/
val priority: Listener.EventPriority = EventPriority.NORMAL,
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment