From 1ab90f9cc3c006344482b90136879d45dead208c Mon Sep 17 00:00:00 2001 From: autaut03 Date: Tue, 12 Feb 2019 22:15:08 +0200 Subject: [PATCH] Automatic event subscriber fixed --- .../forgelin/FMLKotlinModContainer.kt | 3 +- .../ForgelinAutomaticEventSubscriber.kt | 139 ++++++------------ .../net/shadowfacts/forgelin/ForgelinTest.kt | 24 ++- 3 files changed, 51 insertions(+), 115 deletions(-) diff --git a/src/main/kotlin/net/shadowfacts/forgelin/FMLKotlinModContainer.kt b/src/main/kotlin/net/shadowfacts/forgelin/FMLKotlinModContainer.kt index e958e31..2faa513 100644 --- a/src/main/kotlin/net/shadowfacts/forgelin/FMLKotlinModContainer.kt +++ b/src/main/kotlin/net/shadowfacts/forgelin/FMLKotlinModContainer.kt @@ -97,8 +97,7 @@ class FMLKotlinModContainer( } log.debug(LOADING, "Injecting Automatic event subscribers for {}", getModId()) - AutomaticEventSubscriber.inject(this, this.scanResults, this.modClass.classLoader) - //ForgelinAutomaticEventSubscriber.subscribeAutomatic(FMLKotlinModLoadingContext.get().activeContainer, event.asmData, FMLCommonHandler.instance().side) + ForgelinAutomaticEventSubscriber.inject(this, this.scanResults, this.modClass.classLoader) log.debug(LOADING, "Completed Automatic event subscribers for {}", getModId()) } diff --git a/src/main/kotlin/net/shadowfacts/forgelin/ForgelinAutomaticEventSubscriber.kt b/src/main/kotlin/net/shadowfacts/forgelin/ForgelinAutomaticEventSubscriber.kt index 4fce18a..66899cc 100644 --- a/src/main/kotlin/net/shadowfacts/forgelin/ForgelinAutomaticEventSubscriber.kt +++ b/src/main/kotlin/net/shadowfacts/forgelin/ForgelinAutomaticEventSubscriber.kt @@ -1,112 +1,55 @@ package net.shadowfacts.forgelin -import net.minecraftforge.common.MinecraftForge -/*import net.minecraftforge.fml.common.Loader -import net.minecraftforge.fml.common.LoaderException +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.common.ModContainer -import net.minecraftforge.fml.common.discovery.ASMDataTable -import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData -import net.minecraftforge.fml.common.discovery.asm.ModAnnotation.EnumHolder -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import net.minecraftforge.fml.relauncher.Side*/ +import net.minecraftforge.fml.loading.FMLEnvironment +import net.minecraftforge.fml.loading.moddiscovery.ModAnnotation +import net.minecraftforge.forgespi.language.ModFileScanData import org.apache.logging.log4j.LogManager -import java.lang.reflect.Modifier -import java.util.EnumSet -import kotlin.reflect.full.companionObjectInstance +import org.objectweb.asm.Type +import java.util.* +import java.util.stream.Collectors object ForgelinAutomaticEventSubscriber { - /*private val DEFAULT_SUBSCRIPTION_SIDES = EnumSet.allOf(Side::class.java) - private val LOGGER = LogManager.getLogger(ForgelinAutomaticEventSubscriber::class.java) + private val AUTO_SUBSCRIBER = Type.getType(Mod.EventBusSubscriber::class.java) - private val unregistered = mutableSetOf>() - private val registered = mutableSetOf() + private val logger = LogManager.getLogger() - fun subscribeAutomatic(mod: ModContainer, asm: ASMDataTable, currentSide: Side) { - val modAnnotations = asm.getAnnotationsFor(mod) ?: return + fun inject(mod: ModContainer, scanData: ModFileScanData?, loader: ClassLoader) { + if(scanData == null) { + return + } - val containedMods = modAnnotations.get(Mod::class.java.name) - val subscribers = modAnnotations.get(Mod.EventBusSubscriber::class.java.name) - .filter { parseTargetSides(it).contains(currentSide) } + logger.debug(Logging.LOADING, "Attempting to inject @EventBusSubscriber classes into the eventbus for {}", mod.modId) + val targets = scanData.annotations.stream() + .filter { annotationData -> annotationData.annotationType == AUTO_SUBSCRIBER } + .filter { annotationData -> shouldBeRegistered(mod.modId, annotationData) } + .collect(Collectors.toList()) - val loader = Loader.instance().modClassLoader + targets.forEach { ad -> + val busTargetHolder = ad.annotationData.getOrDefault("bus", ModAnnotation.EnumHolder(null, "FORGE")) as ModAnnotation.EnumHolder + val busTarget = Mod.EventBusSubscriber.Bus.valueOf(busTargetHolder.value) + try { + logger.debug(Logging.LOADING, "Auto-subscribing {} to {}", ad.classType.className, busTarget) + val className = Class.forName(ad.classType.className, true, loader) + busTarget.bus().get().register(className.kotlin.objectInstance ?: className) + } catch (e: ClassNotFoundException) { + logger.fatal(Logging.LOADING, "Failed to load mod class {} for @EventBusSubscriber annotation", ad.classType, e) + throw e + } + } + } - for (containedMod in containedMods) { - val containedModId = containedMod.annotationInfo["modid"] as String - if (containedMod.annotationInfo["modLanguageAdapter"] != FMLKotlinModLanguageProvider::class.qualifiedName) { - LOGGER.debug("Skipping @EventBusSubscriber injection for {} since it does not use FMLKotlinModLanguageProvider", containedModId) - continue - } + private fun shouldBeRegistered(modId: String, ad: ModFileScanData.AnnotationData): Boolean { + val sidesValue = ad.annotationData.getOrDefault("value", Arrays.asList(ModAnnotation.EnumHolder(null, "CLIENT"), ModAnnotation.EnumHolder(null, "DEDICATED_SERVER"))) as List + val sides = sidesValue.stream() + .map { eh -> Dist.valueOf(eh.value) } + .collect(Collectors.toCollection { EnumSet.noneOf(Dist::class.java) }) as EnumSet + val annotationModId = ad.annotationData.getOrDefault("modid", modId) as String - LOGGER.debug("Attempting to register Kotlin @EventBusSubscriber objects for {}", containedModId) - - for (subscriber in subscribers) { - try { - val ownerModId = parseModId(containedMods, subscriber) - if (ownerModId.isNullOrEmpty()) { - LOGGER.debug("Could not determine owning mod for @EventBusSubscriber on {} for mod {}", subscriber.className, mod.modId) - continue - } - - if (containedModId != ownerModId) { - LOGGER.debug("Skipping @EventBusSubscriber injection for {} since it is not for mod {}", subscriber.className, containedModId) - continue - } - - val subscriberClass = Class.forName(subscriber.className, false, loader) ?: continue - val kotlinClass = subscriberClass.kotlin - val objectInstance = kotlinClass.objectInstance ?: kotlinClass.companionObjectInstance ?: continue - - if (!hasStaticEventHandlers(subscriberClass) && subscriberClass !in unregistered) { - MinecraftForge.EVENT_BUS.unregister(subscriberClass) - unregistered += subscriberClass - LOGGER.debug("Unregistered static @EventBusSubscriber class {}", subscriber.className) - } - if (hasObjectEventHandlers(objectInstance) && objectInstance !in registered) { - MinecraftForge.EVENT_BUS.register(objectInstance) - registered += objectInstance - LOGGER.debug("Registered @EventBusSubscriber object instance {}", subscriber.className) - } - - } catch (e: Throwable) { - LOGGER.error("An error occurred trying to load an @EventBusSubscriber object {} for modid {}", mod.modId, e) - throw LoaderException(e) - } - } - } - } - - private fun hasObjectEventHandlers(objectInstance: Any): Boolean { - return objectInstance.javaClass.methods.any { - !Modifier.isStatic(it.modifiers) && it.isAnnotationPresent(SubscribeEvent::class.java) - } - } - - private fun hasStaticEventHandlers(clazz: Class<*>): Boolean { - return clazz.methods.any { - Modifier.isStatic(it.modifiers) && it.isAnnotationPresent(SubscribeEvent::class.java) - } - } - - private fun parseModId(containedMods: MutableSet, subscriber: ASMData): String? { - val parsedModId: String? = subscriber.annotationInfo["modid"] as? String - if (parsedModId.isNullOrEmpty()) { - return parsedModId - } - - return ASMDataTable.getOwnerModID(containedMods, subscriber) - } - - private fun parseTargetSides(subscriber: ASMData): EnumSet { - val parsedSides: List? = subscriber.annotationInfo["value"] as? List - if (parsedSides != null) { - val targetSides = EnumSet.noneOf(Side::class.java) - for (parsed in parsedSides) { - targetSides.add(Side.valueOf(parsed.value)) - } - return targetSides - } - return DEFAULT_SUBSCRIPTION_SIDES - }*/ + return modId == annotationModId && sides.contains(FMLEnvironment.dist) + } } diff --git a/src/test/kotlin/net/shadowfacts/forgelin/ForgelinTest.kt b/src/test/kotlin/net/shadowfacts/forgelin/ForgelinTest.kt index 5b3d4bc..be843c7 100644 --- a/src/test/kotlin/net/shadowfacts/forgelin/ForgelinTest.kt +++ b/src/test/kotlin/net/shadowfacts/forgelin/ForgelinTest.kt @@ -20,6 +20,15 @@ object ForgelinTest { FMLKotlinModLoadingContext.get().modEventBus.register(this) } + // You can also use EventBusSubscriber as usual + @Mod.EventBusSubscriber + object EventSubscriber { + @SubscribeEvent + fun testNonStatic(event: EntityJoinWorldEvent) { + logger.info("HELLO from testNonStatic") + } + } + fun setup(event: FMLCommonSetupEvent) { logger.info("HELLO from setup") } @@ -32,19 +41,4 @@ object ForgelinTest { fun setup3(event: FMLCommonSetupEvent) { logger.info("HELLO from setup3") } - - @Mod.EventBusSubscriber - object EventSubscriber { - // doesn't work - @SubscribeEvent - fun testNonStatic(event: EntityJoinWorldEvent) { - logger.info("HELLO from testNonStatic") - } - - @JvmStatic - @SubscribeEvent - fun testStatic(event: EntityJoinWorldEvent) { - logger.info("HELLO from testStatic") - } - } }