Selaa lähdekoodia

Fix the problem listening super events

Him188 6 vuotta sitten
vanhempi
sitoutus
34236147e4

+ 11 - 6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt

@@ -90,19 +90,24 @@ internal object EventListenerManger {
 }
 
 // inline: NO extra Continuation
+@Suppress("UNCHECKED_CAST")
 internal suspend inline fun Subscribable.broadcastInternal() {
     if (EventDisabled) return
 
     callAndRemoveIfRequired(this::class.listeners())
 
-    this::class.supertypes.forEach { superType ->
-        val superListeners =
-            @Suppress("UNCHECKED_CAST")
-            (superType.classifier as? KClass<out Subscribable>)?.listeners() ?: return // return if super type is not Subscribable
+    var supertypes = this::class.supertypes
+    while (true) {
+        val superSubscribableType = supertypes.firstOrNull {
+            it.classifier as? KClass<out Subscribable> != null
+        }
+
+        superSubscribableType?.let {
+            callAndRemoveIfRequired((it.classifier as KClass<out Subscribable>).listeners())
+        }
 
-        callAndRemoveIfRequired(superListeners)
+        supertypes = (superSubscribableType?.classifier as? KClass<*>)?.supertypes ?: return
     }
-    return
 }
 
 private suspend inline fun <E : Subscribable> E.callAndRemoveIfRequired(listeners: EventListeners<E>) {

+ 39 - 4
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/event/EventTests.kt

@@ -1,5 +1,7 @@
 package net.mamoe.mirai.event
 
+import kotlinx.coroutines.CompletableJob
+import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.runBlocking
 import net.mamoe.mirai.test.shouldBeEqualTo
 import kotlin.system.exitProcess
@@ -16,28 +18,61 @@ class EventTests {
         runBlocking {
             val subscriber = subscribeAlways<TestEvent> {
                 triggered = true
-                println("Triggered")
             }
 
             TestEvent().broadcast().triggered shouldBeEqualTo true
             subscriber.complete()
-            println("finished")
         }
     }
 
     @Test
     fun testSubscribeGlobalScope() {
         runBlocking {
+            GlobalScope.subscribeAlways<TestEvent> {
+                triggered = true
+            }
+
             TestEvent().broadcast().triggered shouldBeEqualTo true
-            println("finished")
         }
+    }
+
 
+    open class ParentEvent : Subscribable {
+        var triggered = false
+    }
+
+    open class ChildEvent : ParentEvent()
+
+    open class ChildChildEvent : ChildEvent()
+
+    @Test
+    fun `broadcast Child to Parent`() {
+        runBlocking {
+            val job: CompletableJob
+            job = subscribeAlways<ParentEvent> {
+                triggered = true
+            }
+            ChildEvent().broadcast().triggered shouldBeEqualTo true
+            job.complete()
+        }
+    }
+
+    @Test
+    fun `broadcast ChildChild to Parent`() {
+        runBlocking {
+            val job: CompletableJob
+            job = subscribeAlways<ParentEvent> {
+                triggered = true
+            }
+            ChildChildEvent().broadcast().triggered shouldBeEqualTo true
+            job.complete()
+        }
     }
 
     companion object {
         @JvmStatic
         fun main(args: Array<String>) {
-            EventTests().testSubscribeGlobalScope()
+            EventTests().`broadcast ChildChild to Parent`()
             exitProcess(0)
         }
     }