aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorthedarkcolour <30441001+thedarkcolour@users.noreply.github.com>2020-05-29 12:46:32 -0700
committerthedarkcolour <30441001+thedarkcolour@users.noreply.github.com>2020-05-29 12:46:32 -0700
commitd86d1dab91376ea944de5e9387a20575b50ccf1c (patch)
tree8d58a04667de724e3f2c2825c01448b12d7f7216 /src
parent7f2e66f7dd2efddaf08f329d81c114143bc00cf6 (diff)
downloadKotlinForForge-d86d1dab91376ea944de5e9387a20575b50ccf1c.tar.gz
KotlinForForge-d86d1dab91376ea944de5e9387a20575b50ccf1c.tar.bz2
KotlinForForge-d86d1dab91376ea944de5e9387a20575b50ccf1c.zip
Kotlin for Forge 1.2.2
Diffstat (limited to 'src')
-rw-r--r--src/example/kotlin/thedarkcolour/kotlinforforge/ExampleMod.kt80
-rw-r--r--src/example/kotlin/thedarkcolour/kotlinforforge/package.md7
-rw-r--r--src/example/kotlin/thedarkcolour/kotlinforforge/proxy/Proxies.kt20
-rw-r--r--src/example/kotlin/thedarkcolour/kotlinforforge/proxy/package.md12
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/AutoKotlinEventBusSubscriber.kt60
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/KotlinForForge.kt14
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/KotlinLanguageProvider.kt38
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModContainer.kt62
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModLoadingContext.kt16
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt46
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/package.md2
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/forge/Forge.kt169
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/forge/package.md11
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/kotlin/Kotlin.kt85
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/kotlin/package.md6
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/package.md2
-rw-r--r--src/main/kotlin/thedarkcolour/kotlinforforge/webgenerator/WebGenerator.kt68
-rw-r--r--src/main/resources/META-INF/mods.toml6
-rw-r--r--src/main/resources/pack.mcmeta5
19 files changed, 526 insertions, 183 deletions
diff --git a/src/example/kotlin/thedarkcolour/kotlinforforge/ExampleMod.kt b/src/example/kotlin/thedarkcolour/kotlinforforge/ExampleMod.kt
new file mode 100644
index 0000000..af01493
--- /dev/null
+++ b/src/example/kotlin/thedarkcolour/kotlinforforge/ExampleMod.kt
@@ -0,0 +1,80 @@
+package thedarkcolour.kotlinforforge
+
+import net.minecraft.block.Block
+import net.minecraftforge.event.RegistryEvent
+import net.minecraftforge.eventbus.api.SubscribeEvent
+import net.minecraftforge.fml.common.Mod
+import net.minecraftforge.fml.event.server.FMLServerStartingEvent
+import thedarkcolour.kotlinforforge.forge.MOD_BUS
+import thedarkcolour.kotlinforforge.forge.lazySidedDelegate
+import thedarkcolour.kotlinforforge.proxy.ClientProxy
+import thedarkcolour.kotlinforforge.proxy.IProxy
+import thedarkcolour.kotlinforforge.proxy.ServerProxy
+
+/**
+ * Example mod for anyone who'd like to see
+ * how a mod would be made with Kotlin for Forge.
+ *
+ * This mod has a modid of "examplemod", listens
+ * for the ``RegistryEvent.Register<Block>`` and
+ * for the ``FMLServerStartingEvent``.
+ *
+ * It registers event listeners by adding event listeners
+ * directly to the event buses KFF provides and
+ * by using the ``@EventBusSubscriber`` annotation.
+ */
+@Mod(ExampleMod.ID)
+object ExampleMod {
+ /**
+ * Your mod's ID
+ */
+ const val ID = "examplemod"
+
+ /**
+ * The sided proxy. Since we use a lazy sided delegate,
+ * the supplier parameters are invoked only once.
+ */
+ private val proxy by lazySidedDelegate(::ClientProxy, ::ServerProxy)
+
+ /**
+ * Example of using the KotlinEventBus
+ * to register a function reference.
+ *
+ * Event classes with a generic type
+ * should be registered using ``addGenericListener``
+ * instead of ``addListener``.
+ */
+ init {
+ MOD_BUS.addGenericListener(::registerBlocks)
+
+ proxy.modConstruction()
+ }
+
+ /**
+ * Handle block registry here.
+ */
+ private fun registerBlocks(event: RegistryEvent.Register<Block>) {
+ // ...
+ }
+
+ /**
+ * Example of an object class using the
+ * ``@Mod.EventBusSubscriber`` annotation
+ * to automatically subscribe functions
+ * to the forge event bus.
+ *
+ * Even though the ``Bus.FORGE`` event bus
+ * is default, I think that it's still
+ * a good practice to specify the bus explicitly.
+ */
+ @Mod.EventBusSubscriber(modid = ExampleMod.ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
+ object EventHandler {
+ /**
+ * Handles things like registering commands.
+ */
+ @SubscribeEvent
+ fun onServerStarting(event: FMLServerStartingEvent) {
+ // ...
+ }
+ }
+} \ No newline at end of file
diff --git a/src/example/kotlin/thedarkcolour/kotlinforforge/package.md b/src/example/kotlin/thedarkcolour/kotlinforforge/package.md
new file mode 100644
index 0000000..c9c1caa
--- /dev/null
+++ b/src/example/kotlin/thedarkcolour/kotlinforforge/package.md
@@ -0,0 +1,7 @@
+# thedarkcolour.kotlinforforge
+This package contains an example main mod class
+for a mod using Kotlin for Forge.
+
+## ExampleMod
+Your main mod class should be an object declaration.
+It must be annotated with the @Mod annotation. \ No newline at end of file
diff --git a/src/example/kotlin/thedarkcolour/kotlinforforge/proxy/Proxies.kt b/src/example/kotlin/thedarkcolour/kotlinforforge/proxy/Proxies.kt
new file mode 100644
index 0000000..24c51b7
--- /dev/null
+++ b/src/example/kotlin/thedarkcolour/kotlinforforge/proxy/Proxies.kt
@@ -0,0 +1,20 @@
+package thedarkcolour.kotlinforforge.proxy
+
+/**
+ * Common inheritor of both proxies.
+ */
+interface IProxy {
+ fun modConstruction()
+}
+
+class ClientProxy : IProxy {
+ override fun modConstruction() {
+ // run client code
+ }
+}
+
+class ServerProxy : IProxy {
+ override fun modConstruction() {
+ // run server code
+ }
+} \ No newline at end of file
diff --git a/src/example/kotlin/thedarkcolour/kotlinforforge/proxy/package.md b/src/example/kotlin/thedarkcolour/kotlinforforge/proxy/package.md
new file mode 100644
index 0000000..bb57203
--- /dev/null
+++ b/src/example/kotlin/thedarkcolour/kotlinforforge/proxy/package.md
@@ -0,0 +1,12 @@
+# thedarkcolour.kotlinforforge.proxy
+This package has example proxy classes.
+Proxies are used to provide common declarations with sided implementations.
+
+Forge no longer supports the proxy pattern.
+The ``@SidedProxy`` annotation was removed in 1.13+.
+This example shows a use case for the ``lazySidedDelegate``.
+It is recommended to use the ``runWhenOn`` and ``callWhenOn`` functions
+instead of proxies whenever possible.
+
+In this example, a proxy is instantiated lazily in the ``ExampleMod`` class.
+Proxies are not the only use for sided delegates. \ No newline at end of file
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/AutoKotlinEventBusSubscriber.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/AutoKotlinEventBusSubscriber.kt
index 547918f..01a10c0 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/AutoKotlinEventBusSubscriber.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/AutoKotlinEventBusSubscriber.kt
@@ -4,56 +4,60 @@ import net.minecraftforge.api.distmarker.Dist
import net.minecraftforge.fml.Logging
import net.minecraftforge.fml.ModContainer
import net.minecraftforge.fml.common.Mod
-import net.minecraftforge.fml.loading.FMLEnvironment
import net.minecraftforge.fml.loading.moddiscovery.ModAnnotation
import net.minecraftforge.forgespi.language.ModFileScanData
import org.objectweb.asm.Type
+import thedarkcolour.kotlinforforge.forge.DIST
import thedarkcolour.kotlinforforge.forge.FORGE_BUS
import thedarkcolour.kotlinforforge.forge.MOD_BUS
-import java.util.*
-import java.util.stream.Collectors
+import thedarkcolour.kotlinforforge.kotlin.enumSet
/**
- * Handles [net.minecraftforge.fml.common.Mod.EventBusSubscriber] annotations for object declarations.
+ * Automatically registers `object` classes to
+ * Kotlin for Forge's event buses.
+ *
+ * @see MOD_BUS
+ * @see FORGE_BUS
*/
-public object AutoKotlinEventBusSubscriber {
+object AutoKotlinEventBusSubscriber {
private val EVENT_BUS_SUBSCRIBER: Type = Type.getType(Mod.EventBusSubscriber::class.java)
+ private val DIST_ENUM_HOLDERS = listOf(
+ ModAnnotation.EnumHolder(null, "CLIENT"),
+ ModAnnotation.EnumHolder(null, "DEDICATED_SERVER")
+ )
/**
- * Registers Kotlin objects and companion objects that are annotated with [Mod.EventBusSubscriber]
- * This allows you to declare an object that subscribes to the event bus
- * without making all the [net.minecraftforge.eventbus.api.SubscribeEvent] annotated with [JvmStatic]
+ * Allows the Mod.EventBusSubscriber annotation
+ * to target member functions of an `object` class.
*
- * Example Usage:
+ * You **must** be using an `object` class, or the
+ * EventBusSubscriber annotation will ignore it.
*
- * @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
- * object ExampleSubscriber {
- * @SubscribeEvent
- * public fun onItemRegistry(event: RegistryEvent.Register<Item>) {
- * println("Look! We're in items!")
- * }
- * }
+ * Personally, I am against using [Mod.EventBusSubscriber]
+ * because it makes
+ *
+ * @sample thedarkcolour.kotlinforforge.ExampleMod
*/
- public fun inject(mod: ModContainer, scanData: ModFileScanData, classLoader: ClassLoader) {
+ fun inject(mod: ModContainer, scanData: ModFileScanData, classLoader: ClassLoader) {
LOGGER.debug(Logging.LOADING, "Attempting to inject @EventBusSubscriber kotlin objects in to the event bus for ${mod.modId}")
- val data: ArrayList<ModFileScanData.AnnotationData> = scanData.annotations.stream()
- .filter { annotationData ->
- EVENT_BUS_SUBSCRIBER == annotationData.annotationType
- }
- .collect(Collectors.toList()) as ArrayList<ModFileScanData.AnnotationData>
+
+ val data = scanData.annotations.filter { annotationData ->
+ EVENT_BUS_SUBSCRIBER == annotationData.annotationType
+ }
+
data.forEach { annotationData ->
- val sidesValue: List<ModAnnotation.EnumHolder> = annotationData.annotationData.getOrDefault("value", listOf(ModAnnotation.EnumHolder(null, "CLIENT"), ModAnnotation.EnumHolder(null, "DEDICATED_SERVER"))) as List<ModAnnotation.EnumHolder>
- val sides: EnumSet<Dist> = sidesValue.stream().map { eh -> Dist.valueOf(eh.value) }
- .collect(Collectors.toCollection { EnumSet.noneOf(Dist::class.java) })
+ val sidesValue = annotationData.annotationData.getOrDefault("value", DIST_ENUM_HOLDERS) as List<ModAnnotation.EnumHolder>
+ val sides = enumSet<Dist>().plus(sidesValue.map { eh -> Dist.valueOf(eh.value) })
val modid = annotationData.annotationData.getOrDefault("modid", mod.modId)
- val busTargetHolder: ModAnnotation.EnumHolder = annotationData.annotationData.getOrDefault("bus", ModAnnotation.EnumHolder(null, "FORGE")) as ModAnnotation.EnumHolder
+ val busTargetHolder = annotationData.annotationData.getOrDefault("bus", ModAnnotation.EnumHolder(null, "FORGE")) as ModAnnotation.EnumHolder
val busTarget = Mod.EventBusSubscriber.Bus.valueOf(busTargetHolder.value)
val ktObject = Class.forName(annotationData.classType.className, true, classLoader).kotlin.objectInstance
- if (ktObject != null && mod.modId == modid && sides.contains(FMLEnvironment.dist)) {
+
+ if (ktObject != null && mod.modId == modid && DIST in sides) {
try {
LOGGER.debug(Logging.LOADING, "Auto-subscribing kotlin object ${annotationData.classType.className} to $busTarget")
+
if (busTarget == Mod.EventBusSubscriber.Bus.MOD) {
- // Gets the correct mod loading context
MOD_BUS.register(ktObject)
} else {
FORGE_BUS.register(ktObject)
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinForForge.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinForForge.kt
index 51840d9..6a60e4b 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinForForge.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinForForge.kt
@@ -1,11 +1,7 @@
package thedarkcolour.kotlinforforge
-import net.minecraft.block.Block
-import net.minecraft.block.material.Material
-import net.minecraftforge.event.RegistryEvent
import net.minecraftforge.fml.common.Mod
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext
-import thedarkcolour.kotlinforforge.forge.MOD_BUS
/**
* Set 'modLoader' in mods.toml to "kotlinforforge" and loaderVersion to "[1,)".
@@ -13,12 +9,4 @@ import thedarkcolour.kotlinforforge.forge.MOD_BUS
* Make sure to use [KotlinModLoadingContext] instead of [FMLJavaModLoadingContext].
*/
@Mod("kotlinforforge")
-object KotlinForForge {
- init {
- MOD_BUS.addGenericListener(::registerBlocks)
- }
-
- private fun registerBlocks(event: RegistryEvent.Register<Block>) {
- event.registry.register(Block(Block.Properties.create(Material.ROCK)).setRegistryName("bruh"))
- }
-} \ No newline at end of file
+object KotlinForForge \ No newline at end of file
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinLanguageProvider.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinLanguageProvider.kt
index d393cbd..a06c9a3 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinLanguageProvider.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinLanguageProvider.kt
@@ -2,35 +2,39 @@ package thedarkcolour.kotlinforforge
import net.minecraftforge.fml.Logging
import net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider
-import net.minecraftforge.forgespi.language.ILifecycleEvent
import net.minecraftforge.forgespi.language.IModInfo
import net.minecraftforge.forgespi.language.IModLanguageProvider
import net.minecraftforge.forgespi.language.ModFileScanData
import java.util.function.Consumer
-import java.util.function.Supplier
-import java.util.stream.Collectors
-public class KotlinLanguageProvider : FMLJavaModLanguageProvider() {
+/**
+ * Reuse a bit of code from FMLJavaModLanguageProvider
+ */
+class KotlinLanguageProvider : FMLJavaModLanguageProvider() {
+ override fun name() = "kotlinforforge"
+
override fun getFileVisitor(): Consumer<ModFileScanData> {
- return Consumer { scanResult ->
- val target = scanResult.annotations.stream()
- .filter { data -> data.annotationType == MODANNOTATION }
- .peek { data -> LOGGER.debug(Logging.SCAN, "Found @Mod class ${data.classType.className} with id ${data.annotationData["value"]}") }
- .map { data -> KotlinModTarget(data.classType.className, data.annotationData["value"] as String) }
- .collect(Collectors.toMap({ target: KotlinModTarget -> target.modId }, { it }, { a, _ -> a }))
- scanResult.addLanguageLoader(target)
+ return Consumer { scanData ->
+ val id2TargetMap = scanData.annotations.filter { data ->
+ data.annotationType == MODANNOTATION
+ }.map { data ->
+ val modid = data.annotationData["value"] as String
+ val modClass = data.classType.className
+ LOGGER.debug(Logging.SCAN, "Found @Mod class $modClass with mod id $modid")
+ modid to KotlinModTarget(modClass)
+ }.toMap()
+
+ scanData.addLanguageLoader(id2TargetMap)
}
}
- override fun <R : ILifecycleEvent<R>?> consumeLifecycleEvent(consumeEvent: Supplier<R>?) {}
-
- override fun name(): String = "kotlinforforge"
-
- public class KotlinModTarget constructor(private val className: String, val modId: String) : IModLanguageProvider.IModLanguageLoader {
+ class KotlinModTarget constructor(private val className: String) : IModLanguageProvider.IModLanguageLoader {
override fun <T> loadMod(info: IModInfo, modClassLoader: ClassLoader, modFileScanResults: ModFileScanData): T {
val ktContainer = Class.forName("thedarkcolour.kotlinforforge.KotlinModContainer", true, Thread.currentThread().contextClassLoader)
+ val constructor = ktContainer.declaredConstructors[0]
+
LOGGER.debug(Logging.LOADING, "Loading KotlinModContainer from classloader ${Thread.currentThread().contextClassLoader} - got ${ktContainer.classLoader}}")
- val constructor = ktContainer.declaredConstructors[0]//(IModInfo::class.java, String::class.java, ClassLoader::class.java, ModFileScanData::class.java)!!
+
return constructor.newInstance(info, className, modClassLoader, modFileScanResults) as T
}
}
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModContainer.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModContainer.kt
index 9802d90..e86f66a 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModContainer.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModContainer.kt
@@ -5,13 +5,19 @@ import net.minecraftforge.eventbus.api.BusBuilder
import net.minecraftforge.eventbus.api.Event
import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.eventbus.api.IEventListener
-import net.minecraftforge.fml.*
+import net.minecraftforge.fml.LifecycleEventProvider.LifecycleEvent
+import net.minecraftforge.fml.Logging
+import net.minecraftforge.fml.ModContainer
+import net.minecraftforge.fml.ModLoadingException
+import net.minecraftforge.fml.ModLoadingStage
import net.minecraftforge.fml.config.ModConfig
import net.minecraftforge.forgespi.language.IModInfo
import net.minecraftforge.forgespi.language.ModFileScanData
import thedarkcolour.kotlinforforge.eventbus.KotlinEventBus
+import thedarkcolour.kotlinforforge.kotlin.supply
import java.util.function.Consumer
-import java.util.function.Supplier
+
+typealias LifeCycleEventListener = (LifecycleEvent) -> Unit
/**
* Functions as [net.minecraftforge.fml.javafmlmod.FMLModContainer] for Kotlin
@@ -22,27 +28,34 @@ class KotlinModContainer(private val info: IModInfo, private val className: Stri
init {
LOGGER.debug(Logging.LOADING, "Creating KotlinModContainer instance for {} with classLoader {} & {}", className, classLoader, javaClass.classLoader)
- triggerMap[ModLoadingStage.CONSTRUCT] = dummy().andThen(::constructMod).andThen(::afterEvent)
- triggerMap[ModLoadingStage.CREATE_REGISTRIES] = dummy().andThen(::fireEvent).andThen(::afterEvent)
- triggerMap[ModLoadingStage.LOAD_REGISTRIES] = dummy().andThen(::fireEvent).andThen(::afterEvent)
- triggerMap[ModLoadingStage.COMMON_SETUP] = dummy().andThen(::fireEvent).andThen(::afterEvent)
- triggerMap[ModLoadingStage.SIDED_SETUP] = dummy().andThen(::fireEvent).andThen(::afterEvent)
- triggerMap[ModLoadingStage.ENQUEUE_IMC] = dummy().andThen(::fireEvent).andThen(::afterEvent)
- triggerMap[ModLoadingStage.PROCESS_IMC] = dummy().andThen(::fireEvent).andThen(::afterEvent)
- triggerMap[ModLoadingStage.COMPLETE] = dummy().andThen(::fireEvent).andThen(::afterEvent)
- triggerMap[ModLoadingStage.GATHERDATA] = dummy().andThen(::fireEvent).andThen(::afterEvent)
+ triggerMap[ModLoadingStage.CONSTRUCT] = createTrigger(::constructMod, ::afterEvent)
+ triggerMap[ModLoadingStage.CREATE_REGISTRIES] = createTrigger(::fireEvent, ::afterEvent)
+ triggerMap[ModLoadingStage.LOAD_REGISTRIES] = createTrigger(::fireEvent, ::afterEvent)
+ triggerMap[ModLoadingStage.COMMON_SETUP] = createTrigger(::fireEvent, ::afterEvent)
+ triggerMap[ModLoadingStage.SIDED_SETUP] = createTrigger(::fireEvent, ::afterEvent)
+ triggerMap[ModLoadingStage.ENQUEUE_IMC] = createTrigger(::fireEvent, ::afterEvent)
+ triggerMap[ModLoadingStage.PROCESS_IMC] = createTrigger(::fireEvent, ::afterEvent)
+ triggerMap[ModLoadingStage.COMPLETE] = createTrigger(::fireEvent, ::afterEvent)
+ triggerMap[ModLoadingStage.GATHERDATA] = createTrigger(::fireEvent, ::afterEvent)
eventBus = KotlinEventBus(BusBuilder.builder().setExceptionHandler(::onEventFailed).setTrackPhases(false))
- val ctx = KotlinModLoadingContext(this)
- contextExtension = Supplier { ctx }
+ contextExtension = supply(KotlinModLoadingContext(this))
}
- private fun dummy(): Consumer<LifecycleEventProvider.LifecycleEvent> = Consumer {}
+ private inline fun createTrigger(
+ crossinline consumerA: LifeCycleEventListener,
+ crossinline consumerB: LifeCycleEventListener,
+ ): Consumer<LifecycleEvent> {
+ return Consumer { event ->
+ consumerA(event)
+ consumerB(event)
+ }
+ }
private fun onEventFailed(iEventBus: IEventBus, event: Event, iEventListeners: Array<IEventListener>, i: Int, throwable: Throwable) {
LOGGER.error(EventBusErrorMessage(event, i, iEventListeners, throwable))
}
- private fun fireEvent(lifecycleEvent: LifecycleEventProvider.LifecycleEvent) {
+ private fun fireEvent(lifecycleEvent: LifecycleEvent) {
val event = lifecycleEvent.getOrBuildEvent(this)
LOGGER.debug(Logging.LOADING, "Firing event for modid $modId : $event")
@@ -55,16 +68,16 @@ class KotlinModContainer(private val info: IModInfo, private val className: Stri
}
}
- private fun afterEvent(lifecycleEvent: LifecycleEventProvider.LifecycleEvent) {
+ private fun afterEvent(lifecycleEvent: LifecycleEvent) {
if (currentState == ModLoadingStage.ERROR) {
LOGGER.error(Logging.LOADING, "An error occurred while dispatching event ${lifecycleEvent.fromStage()} to $modId")
}
}
- private fun constructMod(lifecycleEvent: LifecycleEventProvider.LifecycleEvent) {
+ private fun constructMod(lifecycleEvent: LifecycleEvent) {
val modClass: Class<*>
try {
- modClass = Class.forName(className, true, classLoader)
+ modClass = Class.forName(className, false, classLoader)
LOGGER.debug(Logging.LOADING, "Loaded kotlin modclass ${modClass.name} with ${modClass.classLoader}")
} catch (throwable: Throwable) {
LOGGER.error(Logging.LOADING, "Failed to load kotlin class $className", throwable)
@@ -91,14 +104,17 @@ class KotlinModContainer(private val info: IModInfo, private val className: Stri
}
}
- override fun matches(mod: Any?) = mod == modInstance
+ override fun dispatchConfigEvent(event: ModConfig.ModConfigEvent) {
+ eventBus.post(event)
+ }
+
+ override fun matches(mod: Any?): Boolean {
+ return mod == modInstance
+ }
+
override fun getMod() = modInstance
override fun acceptEvent(e: Event) {
eventBus.post(e)
}
-
- override fun dispatchConfigEvent(event: ModConfig.ModConfigEvent) {
- eventBus.post(event)
- }
} \ No newline at end of file
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModLoadingContext.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModLoadingContext.kt
index 78aaef7..8d0029f 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModLoadingContext.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/KotlinModLoadingContext.kt
@@ -5,10 +5,10 @@ import thedarkcolour.kotlinforforge.eventbus.KotlinEventBus
import thedarkcolour.kotlinforforge.forge.LOADING_CONTEXT
/**
- * Functions as [net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext] for Kotlin
+ * Mod loading context for mods made with Kotlin for Forge.
*/
class KotlinModLoadingContext constructor(private val container: KotlinModContainer) {
- /**
+ /** @since 1.2.1
* @see thedarkcolour.kotlinforforge.forge.MOD_BUS
*/
fun getKEventBus(): KotlinEventBus {
@@ -16,15 +16,23 @@ class KotlinModLoadingContext constructor(private val container: KotlinModContai
}
/** @since 1.2.1
- * @see thedarkcolour.kotlinforforge.forge.MOD_BUS
- *
* Required to make mods that use an older version of KFF work.
+ *
+ * @see thedarkcolour.kotlinforforge.forge.MOD_BUS
*/
+ @Deprecated(
+ message = "Use the KotlinEventBus version. This will be an error in Kotlin for Forge 1.3",
+ replaceWith = ReplaceWith("getKEventBus()"),
+ level = DeprecationLevel.WARNING,
+ )
fun getEventBus(): IEventBus {
return container.eventBus
}
companion object {
+ /**
+ * Returns the [KotlinModLoadingContext] for the current mod
+ */
fun get(): KotlinModLoadingContext {
return LOADING_CONTEXT.extension()
}
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt
index 7323f23..7bf575e 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/KotlinEventBus.kt
@@ -17,7 +17,7 @@ import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Consumer
/** @since 1.2.0
- * Fixes [IEventBus.addListener] for Kotlin SAM interfaces.
+ * Fixes [addListener] and [addGenericListener] for Kotlin KCallable.
*/
open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEventBus, IEventExceptionHandler {
@Suppress("LeakingThis")
@@ -45,7 +45,7 @@ open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEv
}
}
- protected fun registerClass(clazz: Class<*>) {
+ private fun registerClass(clazz: Class<*>) {
for (method in clazz.methods) {
if (Modifier.isStatic(method.modifiers) && method.isAnnotationPresent(SubscribeEvent::class.java)) {
registerListener(clazz, method, method)
@@ -53,7 +53,7 @@ open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEv
}
}
- protected fun registerObject(target: Any) {
+ private fun registerObject(target: Any) {
val classes = HashSet<Class<*>>()
typesFor(target.javaClass, classes)
Arrays.stream(target.javaClass.methods).filter { m ->
@@ -194,6 +194,8 @@ open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEv
/**
* Add a consumer listener for a [GenericEvent] subclass with generic type [F].
+ * Despite being a new addition in Kotlin for Forge 1.2.x,
+ * this function is backwards compatible with Kotlin for Forge 1.1.x and 1.0.x.
*
* @param consumer Callback to invoke when a matching event is received
* @param T The [GenericEvent] subclass to listen for
@@ -247,6 +249,7 @@ open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEv
addListener(priority, passGenericCancelled(genericClassFilter, receiveCancelled), consumer)
}
+ @Suppress("UNCHECKED_CAST")
private fun <T : Event> addListener(priority: EventPriority, filter: (T) -> Boolean, consumer: Consumer<T>) {
val eventType = reflectKotlinSAM(consumer) as Class<T>?
@@ -272,22 +275,26 @@ open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEv
private fun reflectKotlinSAM(consumer: Consumer<*>): Class<*>? {
val clazz = consumer.javaClass
- if (clazz.simpleName.contains("$\$Lambda$")) {
- return TypeResolver.resolveRawArgument(Consumer::class.java, consumer.javaClass)
- } else 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
+ 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
+ }
}
- } else return null
+ else -> return null
+ }
}
private fun <T : GenericEvent<out F>, F> passGenericCancelled(genericClassFilter: Class<F>, receiveCancelled: Boolean): (T) -> Boolean = { event ->
@@ -314,6 +321,9 @@ open class KotlinEventBus(builder: BusBuilder, synthetic: Boolean = false) : IEv
addListener(priority, passGenericCancelled(genericClassFilter, receiveCancelled), eventType, consumer)
}
+ /**
+ * Removes the specified
+ */
override fun unregister(any: Any?) {
val list = listeners.remove(any) ?: return
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/package.md b/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/package.md
new file mode 100644
index 0000000..a9f209d
--- /dev/null
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/eventbus/package.md
@@ -0,0 +1,2 @@
+# thedarkcolour.kotlinforforge.eventbus
+This package contains classes related to the Kotlin for Forge EventBus. \ No newline at end of file
diff --git a/src/main/kotlin/thedarkcolour/kotlinforforge/forge/Forge.kt b/src/main/kotlin/thedarkcolour/kotlinforforge/forge/Forge.kt
index fe8ee42..a792863 100644
--- a/src/main/kotlin/thedarkcolour/kotlinforforge/forge/Forge.kt
+++ b/src/main/kotlin/thedarkcolour/kotlinforforge/forge/Forge.kt
@@ -1,22 +1,32 @@
package thedarkcolour.kotlinforforge.forge
+import net.minecraft.client.Minecraft
+import net.minecraft.util.ResourceLocation
import net.minecraftforge.api.distmarker.Dist
+import net.minecraftforge.api.distmarker.OnlyIn
import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.eventbus.EventBus
import net.minecraftforge.fml.ModLoadingContext
import net.minecraftforge.fml.config.ModConfig
import net.minecraftforge.fml.loading.FMLEnvironment
+import net.minecraftforge.registries.*
import thedarkcolour.kotlinforforge.KotlinModLoadingContext
+import thedarkcolour.kotlinforforge.LOGGER
import thedarkcolour.kotlinforforge.eventbus.KotlinEventBus
import thedarkcolour.kotlinforforge.eventbus.KotlinEventBusWrapper
+import java.util.function.Consumer
+import java.util.function.Predicate
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
/** @since 1.0.0
- * The forge EventBus wrapped in a [KEventBus].
- * Many events that occur during the game are fired on this bus.
+ * The Forge [EventBus].
+ * Many game events are fired on this bus.
*
* @since 1.2.0
* This event bus supports [EventBus.addListener]
+ * and [EventBus.addGenericListener]
* for Kotlin SAM interfaces.
*
* Examples:
@@ -27,11 +37,12 @@ import thedarkcolour.kotlinforforge.eventbus.KotlinEventBusWrapper
val FORGE_BUS = KotlinEventBusWrapper(MinecraftForge.EVENT_BUS as EventBus)
/** @since 1.0.0
- * The mod-specific EventBus.
+ * The mod-specific [EventBus].
* Setup events are typically fired on this bus.
*
* @since 1.2.0
* This event bus supports [EventBus.addListener]
+ * and [EventBus.addGenericListener]
* for Kotlin SAM interfaces.
*
* Examples:
@@ -58,6 +69,14 @@ val LOADING_CONTEXT: ModLoadingContext
*/
val DIST: Dist = FMLEnvironment.dist
+/** @since 1.2.2
+ * The instance of Minecraft.
+ * Make sure to only call this on the client side.
+ */
+val MINECRAFT: Minecraft
+ @OnlyIn(Dist.CLIENT)
+ inline get() = Minecraft.getInstance()
+
/** @since 1.0.0
* An alternative to [net.minecraftforge.fml.DistExecutor.callWhenOn]
* that inlines the callable.
@@ -76,7 +95,7 @@ inline fun <T> callWhenOn(dist: Dist, toRun: () -> T): T? {
/** @since 1.0.0
* An alternative to [net.minecraftforge.fml.DistExecutor.runWhenOn]
- * that uses Kotlin functions instead of Java functional interfaces.
+ * that inlines the runnable.
*/
inline fun runWhenOn(dist: Dist, toRun: () -> Unit) {
if (DIST == dist) {
@@ -86,7 +105,7 @@ inline fun runWhenOn(dist: Dist, toRun: () -> Unit) {
/** @since 1.0.0
* An alternative to [net.minecraftforge.fml.DistExecutor.runForDist]
- * that inlines the method call.
+ * that inlines the function call.
*/
inline fun <T> runForDist(clientTarget: () -> T, serverTarget: () -> T): T {
return when (DIST) {
@@