aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt37
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/test/TestMod.kt128
-rw-r--r--src/main/resources/META-INF/mods.toml20
3 files changed, 158 insertions, 27 deletions
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt
index c35c4e9..3f3fb66 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt
@@ -18,6 +18,9 @@ import java.util.function.Consumer
/** @since 1.2.0
* Fixes [addListener] and [addGenericListener] for Kotlin KCallable.
+ *
+ * @param builder The BusBuilder used to configure this event bus
+ * @param synthetic Whether this event bus is just a wrapper for another bus
*/
public open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEventBus, IEventExceptionHandler {
@Suppress("LeakingThis")
@@ -305,26 +308,24 @@ public open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false
*/
private fun reflectKotlinSAM(consumer: Consumer<*>): Class<*>? {
val clazz = consumer.javaClass
+ val forgeType = TypeResolver.resolveRawArgument(Consumer::class.java, consumer.javaClass)
- when {
- clazz.simpleName.contains("$\$Lambda$") -> {
- return TypeResolver.resolveRawArgument(Consumer::class.java, consumer.javaClass)
- }
- clazz.simpleName.contains("\$sam$") -> {
- try {
- val functionField = clazz.getDeclaredField("function")
- functionField.isAccessible = true
- val function = functionField[consumer]
-
- // Function should have two type parameters (parameter type and return type)
- return TypeResolver.resolveRawArguments(kotlin.jvm.functions.Function1::class.java, function.javaClass)[0]
- } catch (e: NoSuchFieldException) {
- // Kotlin SAM interfaces compile to classes with a "function" field
- LOGGER.log(Level.FATAL, "Tried to register invalid Kotlin SAM interface: Missing 'function' field")
- throw e
- }
+ if (clazz.simpleName.contains("\$sam$")) {
+ try {
+ val functionField = clazz.getDeclaredField("function")
+ functionField.isAccessible = true
+ val function = functionField[consumer]
+
+ // Function should have two type parameters (parameter type and return type)
+ return TypeResolver.resolveRawArguments(kotlin.jvm.functions.Function1::class.java, function.javaClass)[0]
+ } catch (e: NoSuchFieldException) {
+ // Kotlin SAM interfaces compile to classes with a "function" field
+ LOGGER.log(Level.FATAL, "Tried to register invalid Kotlin SAM interface: Missing 'function' field")
+ throw e
}
- else -> return null
+ } else {
+ // Kotlin 1.4 seems to have fixed some of its lambda problems
+ return forgeType
}
}
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/test/TestMod.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/test/TestMod.kt
new file mode 100644
index 0000000..01de386
--- /dev/null
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/test/TestMod.kt
@@ -0,0 +1,128 @@
+package thedarkcolour.kotlinforforge.test
+
+import net.minecraftforge.eventbus.api.BusBuilder
+import net.minecraftforge.eventbus.api.Event
+import net.minecraftforge.eventbus.api.EventPriority
+import net.minecraftforge.eventbus.api.GenericEvent
+import org.junit.Assert.assertTrue
+import thedarkcolour.kotlinforforge.eventbus.KotlinEventBus
+import java.util.function.Consumer
+
+/**
+ * Uncomment the `@Mod` annotation and uncomment the entry in mods.toml to run the test mod
+ */
+//@Mod("test_mod")
+public class TestMod {
+
+ init {
+ val l = arrayListOf<Throwable>()
+
+ try {
+ testAddListenerBridge()
+ } catch (t: Throwable) {
+ l.add(t)
+ }
+ try {
+ testFunctionReference()
+ } catch (t: Throwable) {
+ l.add(t)
+ }
+ try {
+ testGenericListeners()
+ } catch (t: Throwable) {
+ l.add(t)
+ }
+
+ l.forEach { throwable ->
+ throwable.printStackTrace()
+ }
+
+ if (l.isNotEmpty()) {
+ throw RuntimeException("Test mod did not pass")
+ }
+ }
+
+ // Test event class
+ public open class TestEvent(public var value: String) : Event()
+
+ /**
+ * Tests the new bridge lambdas introduced in Kotlin 1.4.
+ */
+ private fun testAddListenerBridge() {
+ val bus = KotlinEventBus(BusBuilder.builder())
+
+ // Make sure the type checker works properly in Kotlin 1.4
+ bus.addListener<TestEvent> { event ->
+ event.value = "Foo"
+ }
+
+ // Check that the event posts for added listeners
+ val event = TestEvent("Bar")
+ bus.post(event)
+ assertTrue(event.value == "Foo")
+ }
+
+ // Test function for function references
+ private fun exampleFunction(event: TestEvent) {
+ event.value = "Foo"
+ }
+
+ /**
+ * Test that function references work properly in [KotlinEventBus.addListener].
+ */
+ private fun testFunctionReference() {
+ val bus = KotlinEventBus(BusBuilder.builder())
+ val event = TestEvent("Bar")
+
+ bus.addListener(::exampleFunction)
+ bus.post(event)
+ assertTrue(event.value == "Foo")
+ }
+
+ // generic event
+ public class TestGenericEvent<T>(type: Class<T>, internal var value: T) : GenericEvent<T>(type)
+
+ // test the function references here as well
+ private fun functionReference(event: TestGenericEvent<String>) {
+ event.value = "Far"
+ }
+
+ // Consumer class
+ public class EventConsumer : Consumer<TestGenericEvent<String>> {
+ override fun accept(t: TestGenericEvent<String>) {
+ t.value = "Jar"
+ }
+ }
+
+ /**
+ * Test that [KotlinEventBus.addGenericListener] respects the generic type
+ * of an event fired on the [KotlinEventBus] instance.
+ */
+ public fun testGenericListeners() {
+ // event bus
+ val bus = KotlinEventBus(BusBuilder.builder())
+
+ @Suppress("JoinDeclarationAndAssignment")
+ var fooEvent: TestGenericEvent<String>
+
+ // test fooEvent
+ fooEvent = TestGenericEvent(String::class.java, "Foo")
+ bus.addGenericListener(priority = EventPriority.HIGHEST) { e: TestGenericEvent<String> ->
+ e.value = "Bar"
+ }
+ bus.post(fooEvent)
+ assertTrue(fooEvent.value == "Bar")
+
+ // test consumer subclass
+ fooEvent = TestGenericEvent(String::class.java, "Foo")
+ bus.addGenericListener(priority = EventPriority.HIGH, EventConsumer())
+ bus.post(fooEvent)
+ assertTrue(fooEvent.value == "Jar")
+
+ // test barEvent for function references
+ fooEvent = TestGenericEvent(String::class.java, "Foo")
+ bus.addGenericListener(priority = EventPriority.NORMAL, ::functionReference)
+ bus.post(fooEvent)
+ assertTrue(fooEvent.value == "Far")
+ }
+} \ No newline at end of file
diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml
index 27b9220..e0174e8 100644
--- a/src/main/resources/META-INF/mods.toml
+++ b/src/main/resources/META-INF/mods.toml
@@ -1,13 +1,7 @@
modLoader="kotlinforforge" # IModLanguageProvider
-loaderVersion="[1.4,)" # IModLanguageProvider version
+loaderVersion="[1.6,)" # IModLanguageProvider version
license="GPL v3.0"
-issueTrackerURL="https://github.com/thedarkcolour/KotlinForForge/issues" # Issues page
-
-description='''
-Kotlin for Forge. Allows mods to use the Kotlin programming language.
-'''
-
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
[[dependencies.kotlinforforge]] #optional
modId="forge" #mandatory
@@ -29,9 +23,17 @@ Kotlin for Forge. Allows mods to use the Kotlin programming language.
# --------------------------------------------------------- #
# --------------------------------------------------------- #
+# UNCOMMENT THIS TO RUN TEST MOD
+# [[mods]]
+# modId="test_mod"
+
[[mods]] #mandatory
displayName="Kotlin for Forge" # Name of mod
modId="kotlinforforge" # Modid
-version="1.4.0" # Version of kotlinforforge
+version="1.6.2" # Version of kotlinforforge
authors="TheDarkColour" # Author
-credits="Herobrine knows all." # Credits \ No newline at end of file
+credits="Herobrine knows all." # Credits
+description='''
+Kotlin for Forge. Allows mods to use the Kotlin programming language.
+'''
+issueTrackerURL="https://github.com/thedarkcolour/KotlinForForge/issues" # Issues page \ No newline at end of file