diff --git a/build.gradle b/build.gradle index 5d02a3323..6c2288689 100644 --- a/build.gradle +++ b/build.gradle @@ -260,7 +260,7 @@ project(':forge') { installer 'org.ow2.asm:asm-tree:6.2' installer 'cpw.mods:modlauncher:0.6.0' installer 'net.minecraftforge:accesstransformers:0.14.+:shadowed' - installer 'net.minecraftforge:eventbus:0.3.+:service' + installer 'net.minecraftforge:eventbus:0.6.+:service' installer 'net.minecraftforge:forgespi:0.2.+' installer 'net.minecraftforge:coremods:0.2.+' installer 'com.electronwill.night-config:core:3.4.2' diff --git a/src/main/java/net/minecraftforge/fml/AutomaticEventSubscriber.java b/src/main/java/net/minecraftforge/fml/AutomaticEventSubscriber.java index bbb8dcb22..31aa8a5c2 100644 --- a/src/main/java/net/minecraftforge/fml/AutomaticEventSubscriber.java +++ b/src/main/java/net/minecraftforge/fml/AutomaticEventSubscriber.java @@ -39,7 +39,8 @@ import static net.minecraftforge.fml.Logging.LOADING; /** * Automatic eventbus subscriber - reads {@link net.minecraftforge.fml.common.Mod.EventBusSubscriber} - * annotations and passes the class instances to the {@link net.minecraftforge.common.MinecraftForge.EVENT_BUS} + * annotations and passes the class instances to the {@link net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus} + * defined by the annotation. Defaults to {@link MinecraftForge#EVENT_BUS} */ public class AutomaticEventSubscriber { @@ -60,10 +61,11 @@ public class AutomaticEventSubscriber final EnumSet sides = sidesValue.stream().map(eh -> Dist.valueOf(eh.getValue())). collect(Collectors.toCollection(() -> EnumSet.noneOf(Dist.class))); final String modId = (String)ad.getAnnotationData().getOrDefault("modId", mod.getModId()); + final Mod.EventBusSubscriber.Bus busTarget = (Mod.EventBusSubscriber.Bus)ad.getAnnotationData().getOrDefault("bus", Mod.EventBusSubscriber.Bus.FORGE); if (Objects.equals(mod.getModId(), modId) && sides.contains(FMLEnvironment.dist)) { try { - MinecraftForge.EVENT_BUS.register(Class.forName(ad.getClassType().getClassName(), true, loader)); + busTarget.bus().get().register(Class.forName(ad.getClassType().getClassName(), true, loader)); } catch (ClassNotFoundException e) { diff --git a/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java b/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java index e6bea2caf..fdedfc697 100644 --- a/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java +++ b/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java @@ -19,40 +19,72 @@ package net.minecraftforge.fml; +import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.event.lifecycle.ModLifecycleEvent; import net.minecraftforge.fml.javafmlmod.FMLModContainer; import net.minecraftforge.forgespi.language.ILifecycleEvent; import java.util.List; +import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; public enum LifecycleEventProvider { CONSTRUCT(()->new LifecycleEvent(ModLoadingStage.CONSTRUCT)), + CREATE_REGISTRIES(()->new LifecycleEvent(ModLoadingStage.CREATE_REGISTRIES), ModList.inlineDispatcher), + LOAD_REGISTRIES(()->new LifecycleEvent(ModLoadingStage.LOAD_REGISTRIES, LifecycleEvent.Progression.STAY), ModList.inlineDispatcher), SETUP(()->new LifecycleEvent(ModLoadingStage.COMMON_SETUP)), SIDED_SETUP(()->new LifecycleEvent(ModLoadingStage.SIDED_SETUP)), ENQUEUE_IMC(()->new LifecycleEvent(ModLoadingStage.ENQUEUE_IMC)), PROCESS_IMC(()->new LifecycleEvent(ModLoadingStage.PROCESS_IMC)), COMPLETE(()->new LifecycleEvent(ModLoadingStage.COMPLETE)); - public void dispatch(Consumer> errorHandler) { - ModList.get().dispatchLifeCycleEvent(this.event.get(), errorHandler); - } private final Supplier event; + private final BiConsumer>> eventDispatcher; + private Supplier customEventSupplier; + private LifecycleEvent.Progression progression = LifecycleEvent.Progression.NEXT; LifecycleEventProvider(Supplier e) { - event = e; + this(e, ModList.parallelDispatcher); + } + + LifecycleEventProvider(Supplier e, BiConsumer>> eventDispatcher) + { + this.event = e; + this.eventDispatcher = eventDispatcher; + } + + public void setCustomEventSupplier(Supplier eventSupplier) { + this.customEventSupplier = eventSupplier; + } + + public void changeProgression(LifecycleEvent.Progression progression) { + this.progression = progression; + } + + public void dispatch(Consumer> errorHandler) { + final LifecycleEvent lifecycleEvent = this.event.get(); + lifecycleEvent.setCustomEventSupplier(this.customEventSupplier); + lifecycleEvent.changeProgression(this.progression); + this.eventDispatcher.accept(lifecycleEvent, errorHandler); } public static class LifecycleEvent implements ILifecycleEvent { private final ModLoadingStage stage; - - public LifecycleEvent(ModLoadingStage stage) + private Supplier customEventSupplier; + private Progression progression; + LifecycleEvent(final ModLoadingStage stage) { + this(stage, Progression.NEXT); + } + + LifecycleEvent(final ModLoadingStage stage, final Progression progression) { this.stage = stage; + this.progression = progression; } public ModLoadingStage fromStage() @@ -62,12 +94,42 @@ public enum LifecycleEventProvider public ModLoadingStage toStage() { - return ModLoadingStage.values()[this.stage.ordinal()+1]; + return progression.apply(this.stage); } - public ModLifecycleEvent buildModEvent(FMLModContainer fmlModContainer) + public void setCustomEventSupplier(Supplier customEventSupplier) { + this.customEventSupplier = customEventSupplier; + } + + public void changeProgression(Progression p) { + this.progression = p; + } + + public Event getOrBuildEvent(FMLModContainer fmlModContainer) { + if (customEventSupplier!=null) return customEventSupplier.get(); + return stage.getModEvent(fmlModContainer); } + + @Override + public String toString() { + return "LifecycleEvent:"+stage; + } + + public enum Progression { + NEXT((current)-> ModLoadingStage.values()[current.ordinal()+1]), + STAY(Function.identity()); + + private final Function edge; + + Progression(Function edge) { + this.edge = edge; + } + + public ModLoadingStage apply(ModLoadingStage in) { + return this.edge.apply(in); + } + } } } diff --git a/src/main/java/net/minecraftforge/fml/ModList.java b/src/main/java/net/minecraftforge/fml/ModList.java index 6af85bc90..ff5f04ae6 100644 --- a/src/main/java/net/minecraftforge/fml/ModList.java +++ b/src/main/java/net/minecraftforge/fml/ModList.java @@ -35,6 +35,7 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.FutureTask; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -74,6 +75,10 @@ public class ModList return INSTANCE; } + static BiConsumer>> inlineDispatcher = (event, errors) -> ModList.get().dispatchSynchronousEvent(event, errors); + + static BiConsumer>> parallelDispatcher = (event, errors) -> ModList.get().dispatchParallelEvent(event, errors); + public static ModList get() { return INSTANCE; } @@ -83,13 +88,19 @@ public class ModList return modFiles; } - public ModFileInfo getModFileById(String modid) { return this.fileById.get(modid); } - public void dispatchLifeCycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler) { + private void dispatchSynchronousEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler) { + LOGGER.info(LOADING, "Dispatching synchronous event {}", lifecycleEvent); + FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); + this.mods.forEach(m->m.transitionState(lifecycleEvent, errorHandler)); + FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); + } + private void dispatchParallelEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler) { + LOGGER.info(LOADING, "Dispatching parallel event {}", lifecycleEvent); FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); DeferredWorkQueue.clear(); try @@ -104,7 +115,7 @@ public class ModList FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); } - public void setLoadedMods(final List modContainers) + void setLoadedMods(final List modContainers) { this.mods = modContainers; this.indexedMods = modContainers.stream().collect(Collectors.toMap(ModContainer::getModId, Function.identity())); diff --git a/src/main/java/net/minecraftforge/fml/ModLoader.java b/src/main/java/net/minecraftforge/fml/ModLoader.java index 662c2451e..8818b1a98 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -123,10 +123,10 @@ public class ModLoader } modList.setLoadedMods(modContainerStream.collect(Collectors.toList())); dispatchAndHandleError(LifecycleEventProvider.CONSTRUCT); - GameData.fireCreateRegistryEvents(); + GameData.fireCreateRegistryEvents(LifecycleEventProvider.CREATE_REGISTRIES, this::dispatchAndHandleError); ObjectHolderRegistry.findObjectHolders(); CapabilityManager.INSTANCE.injectCapabilities(modList.getAllScanData()); - GameData.fireRegistryEvents(); + GameData.fireRegistryEvents(rl->true, LifecycleEventProvider.LOAD_REGISTRIES, this::dispatchAndHandleError); dispatchAndHandleError(LifecycleEventProvider.SETUP); DistExecutor.runWhenOn(Dist.CLIENT, ModLoader::fireClientEvents); dispatchAndHandleError(LifecycleEventProvider.SIDED_SETUP); diff --git a/src/main/java/net/minecraftforge/fml/ModLoadingStage.java b/src/main/java/net/minecraftforge/fml/ModLoadingStage.java index 06dae5598..f05575270 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoadingStage.java +++ b/src/main/java/net/minecraftforge/fml/ModLoadingStage.java @@ -29,13 +29,14 @@ public enum ModLoadingStage ERROR(null), VALIDATE(null), CONSTRUCT(null), + CREATE_REGISTRIES(null), + LOAD_REGISTRIES(null), COMMON_SETUP(()-> FMLCommonSetupEvent::new), SIDED_SETUP(SidedProvider.SIDED_SETUP_EVENT::get), ENQUEUE_IMC(()-> InterModEnqueueEvent::new), PROCESS_IMC(()-> InterModProcessEvent::new), COMPLETE(()-> FMLLoadCompleteEvent::new), DONE(null); - private final Supplier> modLifecycleEventFunction; ModLoadingStage(Supplier> modLifecycleEventFunction) diff --git a/src/main/java/net/minecraftforge/fml/common/Mod.java b/src/main/java/net/minecraftforge/fml/common/Mod.java index 37cfbdca6..150a5909d 100644 --- a/src/main/java/net/minecraftforge/fml/common/Mod.java +++ b/src/main/java/net/minecraftforge/fml/common/Mod.java @@ -23,26 +23,19 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.function.Supplier; import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.fml.event.lifecycle.FMLFingerprintViolationEvent; -import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; -import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; -import net.minecraftforge.fml.event.server.FMLServerStartedEvent; -import net.minecraftforge.fml.event.server.FMLServerStartingEvent; -import net.minecraftforge.fml.event.server.FMLServerStoppedEvent; -import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.event.lifecycle.ModLifecycleEvent; -import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; -import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.fml.javafmlmod.FMLModLoadingContext; /** * This defines a Mod to FML. * Any class found with this annotation applied will be loaded as a Mod. The instance that is loaded will * represent the mod to other Mods in the system. It will be sent various subclasses of {@link ModLifecycleEvent} - * at pre-defined times during the loading of the game, based on where you have applied the {@link EventHandler} - * annotation. + * at pre-defined times during the loading of the game. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @@ -56,63 +49,60 @@ public @interface Mod * By default, you will have a resource domain that matches the modid. All these uses require that constraints are imposed on the format of the modid. */ String value(); - /** - * Marks the associated method as handling an FML lifecycle event. - * The method must have a single parameter, one of the following types. This annotation - * replaces the multiple different annotations that previously were used. - * - * Current event classes. This first section is standard lifecycle events. They are dispatched - * at various phases as the game starts. Each event should have information useful to that - * phase of the lifecycle. They are fired in this order. - * - * These suggestions are mostly just suggestions on what to do in each event. - *
    - *
  • {@link FMLCommonSetupEvent} : Run before anything else. Read your config, create blocks, - * items, etc, and register them with the {@link GameRegistry}.
  • - *
  • {@link InterModEnqueueEvent} : Do your mod setup. Build whatever data structures you care about. Register recipes, - * send {@link FMLInterModComms} messages to other mods.
  • - *
  • {@link InterModProcessEvent} : Handle interaction with other mods, complete your setup based on this.
  • - *
- *

These are the server lifecycle events. They are fired whenever a server is running, or about to run. Each time a server - * starts they will be fired in this sequence. - *

    - *
  • {@link FMLServerAboutToStartEvent} : Use if you need to handle something before the server has even been created.
  • - *
  • {@link FMLServerStartingEvent} : Do stuff you need to do to set up the server. register commands, tweak the server.
  • - *
  • {@link FMLServerStartedEvent} : Do what you need to with the running server.
  • - *
  • {@link FMLServerStoppingEvent} : Do what you need to before the server has started it's shutdown sequence.
  • - *
  • {@link FMLServerStoppedEvent} : Do whatever cleanup you need once the server has shutdown. Generally only useful - * on the integrated server.
  • - *
- * The second set of events are more specialized, for receiving notification of specific - * information. - *
    - *
  • {@link FMLFingerprintViolationEvent} : Sent just before {@link FMLCommonSetupEvent} - * if something is wrong with your mod signature
  • - *
  • {@link IMCEvent} : Sent just after {@link InterModEnqueueEvent} if you have IMC messages waiting - * from other mods
  • - *
- * - * @author cpw - * - */ - @Deprecated - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.METHOD) - @interface EventHandler{} /** - * A class which will be subscribed to {@link net.minecraftforge.common.MinecraftForge.EVENT_BUS} at mod construction time. + * Annotate a class which will be subscribed to an Event Bus at mod construction time. + * Defaults to subscribing the current modid to the {@link MinecraftForge#EVENT_BUS} + * on both sides. + * + * @see Bus */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface EventBusSubscriber { + /** + * Specify targets to load this event subscriber on. Can be used to avoid loading Client specific events + * on a dedicated server, for example. + * + * @return an array of Dist to load this event subscriber on + */ Dist[] value() default { Dist.CLIENT, Dist.DEDICATED_SERVER }; /** - * Optional value, only nessasary if tis annotation is not on the same class that has a @Mod annotation. + * Optional value, only necessary if this annotation is not on the same class that has a @Mod annotation. * Needed to prevent early classloading of classes not owned by your mod. - * @return + * @return a modid */ String modid() default ""; + + /** + * Specify an alternative bus to listen to + * + * @return the bus you wish to listen to + */ + Bus bus() default Bus.FORGE; + + enum Bus { + /** + * The main Forge Event Bus. + * @see MinecraftForge#EVENT_BUS + */ + FORGE(()-> MinecraftForge.EVENT_BUS), + /** + * The mod specific Event bus. + * @see FMLModLoadingContext#getModEventBus() + */ + MOD(()-> FMLModLoadingContext.get().getModEventBus()); + + private final Supplier busSupplier; + + Bus(final Supplier eventBusSupplier) { + this.busSupplier = eventBusSupplier; + } + + public Supplier bus() { + return busSupplier; + } + } } } diff --git a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java index 5b431beb8..2630fb8b7 100644 --- a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java +++ b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java @@ -29,7 +29,6 @@ import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModLoadingException; import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.ModThreadContext; -import net.minecraftforge.fml.event.lifecycle.ModLifecycleEvent; import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.language.ModFileScanData; @@ -54,6 +53,8 @@ public class FMLModContainer extends ModContainer LOGGER.debug("Creating FMLModContainer instance for {} with classLoader {} & {}", className, modClassLoader, getClass().getClassLoader()); this.scanResults = modFileScanResults; triggerMap.put(ModLoadingStage.CONSTRUCT, dummy().andThen(this::beforeEvent).andThen(this::constructMod).andThen(this::afterEvent)); + triggerMap.put(ModLoadingStage.CREATE_REGISTRIES, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent)); + triggerMap.put(ModLoadingStage.LOAD_REGISTRIES, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent)); triggerMap.put(ModLoadingStage.COMMON_SETUP, dummy().andThen(this::beforeEvent).andThen(this::preinitMod).andThen(this::fireEvent).andThen(this::afterEvent)); triggerMap.put(ModLoadingStage.SIDED_SETUP, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent)); triggerMap.put(ModLoadingStage.ENQUEUE_IMC, dummy().andThen(this::beforeEvent).andThen(this::initMod).andThen(this::fireEvent).andThen(this::afterEvent)); @@ -96,8 +97,8 @@ public class FMLModContainer extends ModContainer } private void fireEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) { - final ModLifecycleEvent event = lifecycleEvent.buildModEvent(this); - LOGGER.debug(LOADING, "Firing event for modid {} : {}", this.getModId(), event.getClass().getName()); + final Event event = lifecycleEvent.getOrBuildEvent(this); + LOGGER.info(LOADING, "Firing event for modid {} : {}", this.getModId(), event.getClass().getName()); try { eventBus.post(event); diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 4f641fc54..8da094b1f 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -41,6 +41,7 @@ import net.minecraft.world.biome.Biome; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.event.RegistryEvent.MissingMappings; +import net.minecraftforge.fml.LifecycleEventProvider; import net.minecraftforge.fml.ModThreadContext; import net.minecraftforge.fml.StartupQuery; import net.minecraftforge.fml.common.EnhancedRuntimeException; @@ -61,6 +62,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -779,14 +781,17 @@ public class GameData MinecraftForge.EVENT_BUS.post(new RegistryEvent.NewRegistry()); } - public static void fireRegistryEvents() - { - fireRegistryEvents(rl -> true); + public static void fireCreateRegistryEvents(final LifecycleEventProvider lifecycleEventProvider, final Consumer eventDispatcher) { + final RegistryEvent.NewRegistry newRegistryEvent = new RegistryEvent.NewRegistry(); + lifecycleEventProvider.setCustomEventSupplier(()->newRegistryEvent); + eventDispatcher.accept(lifecycleEventProvider); } - public static void fireRegistryEvents(Predicate filter) + + + public static void fireRegistryEvents(Predicate filter, final LifecycleEventProvider lifecycleEventProvider, final Consumer eventDispatcher) { List keys = Lists.newArrayList(RegistryManager.ACTIVE.registries.keySet()); - Collections.sort(keys, (o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString())); + keys.sort((o1, o2) -> String.valueOf(o1).compareToIgnoreCase(String.valueOf(o2))); //Move Blocks to first, and Items to second. keys.remove(BLOCKS); @@ -794,16 +799,18 @@ public class GameData keys.add(0, BLOCKS); keys.add(1, ITEMS); - - for (ResourceLocation rl : keys) - { + for (int i = 0, keysSize = keys.size(); i < keysSize; i++) { + final ResourceLocation rl = keys.get(i); if (!filter.test(rl)) continue; ForgeRegistry reg = RegistryManager.ACTIVE.getRegistry(rl); reg.unfreeze(); - MinecraftForge.EVENT_BUS.post(reg.getRegisterEvent(rl)); + final RegistryEvent.Register registerEvent = reg.getRegisterEvent(rl); + lifecycleEventProvider.setCustomEventSupplier(() -> registerEvent); + if (i== keysSize-1) lifecycleEventProvider.changeProgression(LifecycleEventProvider.LifecycleEvent.Progression.NEXT); + eventDispatcher.accept(lifecycleEventProvider); reg.freeze(); LOGGER.info("{} Applying holder lookups", rl.toString()); - ObjectHolderRegistry.applyObjectHolders(key -> rl.equals(key)); + ObjectHolderRegistry.applyObjectHolders(rl::equals); LOGGER.info("{} Holder lookups applied", rl.toString()); } }