From d76ee4edd74d98db274578922ebbf023c1d6d522 Mon Sep 17 00:00:00 2001 From: cpw Date: Sun, 30 Aug 2020 21:39:06 -0400 Subject: [PATCH] Redo event dispatch, removes a bunch of nonsense, and tries to integrate with the vanilla CF system where possible Signed-off-by: cpw --- .../fml/loading/moddiscovery/ModFile.java | 2 +- .../minecraftforge/event/RegistryEvent.java | 1 + .../minecraftforge/fml/DeferredWorkQueue.java | 187 ++++-------------- .../fml/LifecycleEventProvider.java | 154 --------------- .../net/minecraftforge/fml/ModContainer.java | 48 +++-- .../java/net/minecraftforge/fml/ModList.java | 94 +++++---- .../net/minecraftforge/fml/ModLoader.java | 169 +++++++++------- .../minecraftforge/fml/ModLoadingStage.java | 146 ++++++++++++-- .../minecraftforge/fml/ModWorkManager.java | 103 ++++++++++ .../net/minecraftforge/fml/SidedProvider.java | 7 - .../fml/client/ClientModLoader.java | 64 +++--- .../event/lifecycle/FMLClientSetupEvent.java | 6 +- .../event/lifecycle/FMLCommonSetupEvent.java | 2 +- .../event/lifecycle/FMLConstructModEvent.java | 9 + .../FMLDedicatedServerSetupEvent.java | 2 +- .../event/lifecycle/FMLLoadCompleteEvent.java | 2 +- .../event/lifecycle/InterModEnqueueEvent.java | 2 +- .../event/lifecycle/InterModProcessEvent.java | 2 +- .../event/lifecycle/ModLifecycleEvent.java | 4 + .../lifecycle/ParallelDispatchEvent.java | 26 +++ .../fml/javafmlmod/FMLModContainer.java | 73 ++----- .../fml/server/ServerModLoader.java | 7 +- .../minecraftforge/registries/GameData.java | 75 +++---- .../client/rendering/StencilEnableTest.java | 2 +- 24 files changed, 570 insertions(+), 617 deletions(-) delete mode 100644 src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java create mode 100644 src/main/java/net/minecraftforge/fml/ModWorkManager.java create mode 100644 src/main/java/net/minecraftforge/fml/event/lifecycle/FMLConstructModEvent.java create mode 100644 src/main/java/net/minecraftforge/fml/event/lifecycle/ParallelDispatchEvent.java diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java index b8de3fe0b..3dd73a937 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java @@ -79,7 +79,7 @@ public class ModFile implements IModFile { this.locator = locator; this.filePath = file; this.parser = parser; - manifest = locator.findManifest(file).orElse(DEFAULTMANIFEST); + manifest = Optional.ofNullable(locator).flatMap(l->l.findManifest(file)).orElse(DEFAULTMANIFEST); if (manifest != DEFAULTMANIFEST) LOGGER.debug(SCAN,"Mod file {} has a manifest", file); else LOGGER.debug(SCAN,"Mod file {} is missing a manifest", file); final Optional value = Optional.ofNullable(manifest.getMainAttributes().getValue(TYPE)); diff --git a/src/main/java/net/minecraftforge/event/RegistryEvent.java b/src/main/java/net/minecraftforge/event/RegistryEvent.java index b02303270..264aae202 100644 --- a/src/main/java/net/minecraftforge/event/RegistryEvent.java +++ b/src/main/java/net/minecraftforge/event/RegistryEvent.java @@ -46,6 +46,7 @@ public class RegistryEvent> extends GenericEven */ public static class NewRegistry extends net.minecraftforge.eventbus.api.Event implements IModBusEvent { + public NewRegistry(ModContainer mc) {} @Override public String toString() { return "RegistryEvent.NewRegistry"; diff --git a/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java b/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java index 75febfa7a..8174ca8af 100644 --- a/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java +++ b/src/main/java/net/minecraftforge/fml/DeferredWorkQueue.java @@ -22,22 +22,21 @@ package net.minecraftforge.fml; import static net.minecraftforge.fml.Logging.LOADING; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; -import java.util.concurrent.Callable; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; import java.util.function.Supplier; -import java.util.stream.Collectors; +import net.minecraftforge.fml.event.lifecycle.ParallelDispatchEvent; import org.apache.commons.lang3.time.StopWatch; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.google.common.base.Function; import com.google.common.base.Stopwatch; import net.minecraftforge.forgespi.language.IModInfo; @@ -52,166 +51,60 @@ import net.minecraftforge.forgespi.language.IModInfo; *

* Exceptions from tasks will be handled gracefully, causing a mod loading * error. Tasks that take egregiously long times to run will be logged. - * - * This is being deprecated in favour of a new interface on loading events, to remove confusion about how it operates. #TODO */ -@Deprecated public class DeferredWorkQueue { - private static class TaskInfo - { - public final IModInfo owner; - public final Runnable task; - - TaskInfo(IModInfo owner, Runnable task) { - this.owner = owner; - this.task = task; - } - } - - /** - * {@link Runnable} except it allows throwing checked exceptions. - * - * Is to {@link Runnable} as {@link Callable} is to {@link Supplier}. - */ - @FunctionalInterface - public interface CheckedRunnable - { - void run() throws Exception; - } - private static final Logger LOGGER = LogManager.getLogger(); - private static ThreadLocal currentOwner = new ThreadLocal<>(); - private static List raisedExceptions = new ArrayList<>(); + private static Map, DeferredWorkQueue> workQueues = new HashMap<>(); - private static final ConcurrentLinkedDeque taskQueue = new ConcurrentLinkedDeque<>(); - private static final Executor deferredExecutor = r -> taskQueue.add(new TaskInfo(currentOwner.get().getModInfo(), r)); + private final ModLoadingStage modLoadingStage; + private final ConcurrentLinkedDeque tasks = new ConcurrentLinkedDeque<>(); - private static Function handleException() { - final ModContainer owner = currentOwner.get(); - return t -> { - LogManager.getLogger(DeferredWorkQueue.class).error("Encountered exception executing deferred work", t); - raisedExceptions.add(new ModLoadingException(owner.getModInfo(), owner.getCurrentState(), "fml.modloading.failedtoprocesswork", t)); - return null; - }; + public DeferredWorkQueue(final ModLoadingStage modLoadingStage, Class eventClass) { + this.modLoadingStage = modLoadingStage; + workQueues.put(eventClass, this); } - /** - * Run a task on the loading thread at the next available opportunity, i.e. - * after the current lifecycle event has completed. - *

- * If the task must throw a checked exception, use - * {@link #runLaterChecked(CheckedRunnable)}. - *

- * If the task has a result, use {@link #getLater(Supplier)} or - * {@link #getLaterChecked(Callable)}. - * - * @param workToEnqueue A {@link Runnable} to execute later, on the loading - * thread - * @return A {@link CompletableFuture} that completes at said time - */ - public static CompletableFuture runLater(Runnable workToEnqueue) { - currentOwner.set(ModLoadingContext.get().getActiveContainer()); - return CompletableFuture.runAsync(workToEnqueue, deferredExecutor).exceptionally(DeferredWorkQueue.handleException()); + public static Optional lookup(Optional> parallelClass) { + return Optional.ofNullable(workQueues.get(parallelClass.orElse(null))); } - /** - * Run a task on the loading thread at the next available opportunity, i.e. - * after the current lifecycle event has completed. This variant allows the task - * to throw a checked exception. - *

- * If the task does not throw a checked exception, use - * {@link #runLater(Runnable)}. - *

- * If the task has a result, use {@link #getLater(Supplier)} or - * {@link #getLaterChecked(Callable)}. - * - * @param workToEnqueue A {@link CheckedRunnable} to execute later, on the - * loading thread - * @return A {@link CompletableFuture} that completes at said time - */ - public static CompletableFuture runLaterChecked(CheckedRunnable workToEnqueue) { - return runLater(() -> { - try { - workToEnqueue.run(); - } catch (Throwable t) { - throw new CompletionException(t); - } - }); - } - - /** - * Run a task computing a result on the loading thread at the next available - * opportunity, i.e. after the current lifecycle event has completed. - *

- * If the task throws a checked exception, use - * {@link #getLaterChecked(Callable)}. - *

- * If the task does not have a result, use {@link #runLater(Runnable)} or - * {@link #runLaterChecked(CheckedRunnable)}. - * - * @param The result type of the task - * @param workToEnqueue A {@link Supplier} to execute later, on the loading - * thread - * @return A {@link CompletableFuture} that completes at said time - */ - public static CompletableFuture getLater(Supplier workToEnqueue) { - currentOwner.set(ModLoadingContext.get().getActiveContainer()); - return CompletableFuture.supplyAsync(workToEnqueue, deferredExecutor).exceptionally(DeferredWorkQueue.handleException()); - } - - /** - * Run a task computing a result on the loading thread at the next available - * opportunity, i.e. after the current lifecycle event has completed. This - * variant allows the task to throw a checked exception. - *

- * If the task does not throw a checked exception, use - * {@link #getLater(Callable)}. - *

- * If the task does not have a result, use {@link #runLater(Runnable)} or - * {@link #runLaterChecked(CheckedRunnable)}. - * - * @param The result type of the task - * @param workToEnqueue A {@link Supplier} to execute later, on the loading - * thread - * @return A {@link CompletableFuture} that completes at said time - */ - public static CompletableFuture getLaterChecked(Callable workToEnqueue) { - return getLater(() -> { - try { - return workToEnqueue.call(); - } catch (Throwable t) { - throw new CompletionException(t); - } - }); - } - - static void clear() { - taskQueue.clear(); - } - - static Executor workExecutor = Runnable::run; - - static void runTasks(ModLoadingStage fromStage, Consumer> errorHandler) { - raisedExceptions.clear(); - if (taskQueue.isEmpty()) return; // Don't log unnecessarily - LOGGER.info(LOADING, "Dispatching synchronous work after {}: {} jobs", fromStage, taskQueue.size()); + void runTasks() { + if (tasks.isEmpty()) return; + LOGGER.debug(LOADING, "Dispatching synchronous work after {}: {} jobs", modLoadingStage, tasks.size()); StopWatch globalTimer = StopWatch.createStarted(); - final CompletableFuture tasks = CompletableFuture.allOf(taskQueue.stream().map(ti -> makeRunnable(ti, workExecutor)).toArray(CompletableFuture[]::new)); - tasks.join(); - LOGGER.info(LOADING, "Synchronous work queue completed in {}", globalTimer); - errorHandler.accept(raisedExceptions); + tasks.forEach(t->makeRunnable(t, Runnable::run)); + LOGGER.debug(LOADING, "Synchronous work queue completed in {}", globalTimer); } - private static CompletableFuture makeRunnable(TaskInfo ti, Executor executor) { - return CompletableFuture.runAsync(() -> { + private static void makeRunnable(TaskInfo ti, Executor executor) { + executor.execute(() -> { Stopwatch timer = Stopwatch.createStarted(); ti.task.run(); timer.stop(); if (timer.elapsed(TimeUnit.SECONDS) >= 1) { LOGGER.warn(LOADING, "Mod '{}' took {} to run a deferred task.", ti.owner.getModId(), timer); } - }, executor); + }); } -} + + public CompletableFuture enqueueWork(final IModInfo modInfo, final Runnable work) { + return CompletableFuture.runAsync(work, r->tasks.add(new TaskInfo(modInfo, r))); + } + + public CompletableFuture enqueueWork(final IModInfo modInfo, final Supplier work) { + return CompletableFuture.supplyAsync(work, r->tasks.add(new TaskInfo(modInfo, r))); + } + + static class TaskInfo + { + public final IModInfo owner; + public final Runnable task; + + private TaskInfo(IModInfo owner, Runnable task) { + this.owner = owner; + this.task = task; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java b/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java deleted file mode 100644 index 87654b8c1..000000000 --- a/src/main/java/net/minecraftforge/fml/LifecycleEventProvider.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2020. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.fml; - -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.forgespi.language.ILifecycleEvent; - -import java.util.List; -import java.util.concurrent.Executor; -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)), - GATHERDATA(()->new GatherDataLifecycleEvent(ModLoadingStage.GATHERDATA), ModList.inlineDispatcher); - - private final Supplier event; - private final EventHandler>,Executor, Runnable> eventDispatcher; - private Supplier customEventSupplier; - private LifecycleEvent.Progression progression = LifecycleEvent.Progression.NEXT; - - LifecycleEventProvider(Supplier e) - { - this(e, ModList.parallelDispatcher); - } - - LifecycleEventProvider(Supplier e, EventHandler>,Executor, Runnable> 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 Executor executor, final Runnable ticker) { - final LifecycleEvent lifecycleEvent = this.event.get(); - lifecycleEvent.setCustomEventSupplier(this.customEventSupplier); - lifecycleEvent.changeProgression(this.progression); - this.eventDispatcher.dispatchEvent(lifecycleEvent, errorHandler, executor, ticker); - } - - - public static class LifecycleEvent implements ILifecycleEvent { - private final 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() - { - return this.stage; - } - - public ModLoadingStage toStage() - { - return progression.apply(this.stage); - } - - public void setCustomEventSupplier(Supplier customEventSupplier) { - this.customEventSupplier = customEventSupplier; - } - - public void changeProgression(Progression p) { - this.progression = p; - } - - public Event getOrBuildEvent(ModContainer modContainer) - { - if (customEventSupplier!=null) return customEventSupplier.get(); - - return stage.getModEvent(modContainer); - } - - @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); - } - } - } - - private static class GatherDataLifecycleEvent extends LifecycleEvent { - GatherDataLifecycleEvent(final ModLoadingStage stage) { - super(stage); - } - - @Override - public ModLoadingStage fromStage() { - return ModLoadingStage.COMMON_SETUP; - } - - @Override - public ModLoadingStage toStage() { - return ModLoadingStage.DONE; - } - } - - public interface EventHandler>, V extends Executor, R extends Runnable> { - void dispatchEvent(T event, U exceptionHandler, V executor, R ticker); - } -} diff --git a/src/main/java/net/minecraftforge/fml/ModContainer.java b/src/main/java/net/minecraftforge/fml/ModContainer.java index daeb247b0..ad685edd3 100644 --- a/src/main/java/net/minecraftforge/fml/ModContainer.java +++ b/src/main/java/net/minecraftforge/fml/ModContainer.java @@ -21,17 +21,19 @@ package net.minecraftforge.fml; import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.config.ModConfig; +import net.minecraftforge.fml.event.lifecycle.IModBusEvent; import net.minecraftforge.forgespi.language.IModInfo; import org.apache.commons.lang3.tuple.Pair; -import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.IdentityHashMap; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Supplier; @@ -55,7 +57,7 @@ public abstract class ModContainer protected final IModInfo modInfo; protected ModLoadingStage modLoadingStage; protected Supplier contextExtension; - protected final Map> triggerMap; + protected final Map activityMap = new HashMap<>(); protected final Map> extensionPoints = new IdentityHashMap<>(); protected final EnumMap configs = new EnumMap<>(ModConfig.Type.class); @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @@ -67,7 +69,6 @@ public abstract class ModContainer // TODO: Currently not reading namespace from configuration.. this.namespace = this.modId; this.modInfo = info; - this.triggerMap = new HashMap<>(); this.modLoadingStage = ModLoadingStage.CONSTRUCT; // default displaytest extension checks for version string match registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(()->this.modInfo.getVersion().toString(), @@ -98,27 +99,22 @@ public abstract class ModContainer return modLoadingStage; } - /** - * Transition the mod to this event if possible. - * @param event to transition to - */ - public final void transitionState(LifecycleEventProvider.LifecycleEvent event, Consumer> errorHandler) - { - if (modLoadingStage == event.fromStage()) - { - try - { - ModLoadingContext.get().setActiveContainer(this, contextExtension.get()); - triggerMap.getOrDefault(modLoadingStage, e->{}).accept(event); - modLoadingStage = event.toStage(); - ModLoadingContext.get().setActiveContainer(null, null); - } - catch (ModLoadingException e) - { - modLoadingStage = ModLoadingStage.ERROR; - errorHandler.accept(Collections.singletonList(e)); - } - } + public static CompletableFuture buildTransitionHandler( + final ModContainer target, + final ModLoadingStage.EventGenerator eventGenerator, + final ModLoadingStage.EventDispatcher eventDispatcher, + final BiFunction stateChangeHandler, + final Executor executor) { + return CompletableFuture + .runAsync(() -> { + ModLoadingContext.get().setActiveContainer(target, target.contextExtension.get()); + target.activityMap.getOrDefault(target.modLoadingStage, ()->{}).run(); + eventDispatcher.apply(target::acceptEvent).accept(eventGenerator.apply(target)); + }, executor) + .whenComplete((mc, exception) -> { + target.modLoadingStage = stateChangeHandler.apply(target.modLoadingStage, exception); + ModLoadingContext.get().setActiveContainer(null, null); + }); } /** @@ -164,5 +160,5 @@ public abstract class ModContainer * Accept an arbitrary event for processing by the mod. Probably posted to an event bus in the lower level container. * @param e Event to accept */ - protected void acceptEvent(Event e) {} + protected void acceptEvent(T e) {} } diff --git a/src/main/java/net/minecraftforge/fml/ModList.java b/src/main/java/net/minecraftforge/fml/ModList.java index c587048fd..e4d8e7da0 100644 --- a/src/main/java/net/minecraftforge/fml/ModList.java +++ b/src/main/java/net/minecraftforge/fml/ModList.java @@ -19,30 +19,37 @@ package net.minecraftforge.fml; -import net.minecraftforge.fml.loading.FMLConfig; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.lifecycle.IModBusEvent; import net.minecraftforge.forgespi.language.ModFileScanData; -import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -import static net.minecraftforge.fml.Logging.LOADING; - /** * Master list of all mods - game-side version. This is classloaded in the game scope and * can dispatch game level events as a result. @@ -56,7 +63,6 @@ public class ModList private final Map fileById; private List mods; private Map indexedMods; - private ForkJoinPool modLoadingThreadPool; private List modFileScanData; private ModList(final List modFiles, final List sortedList) @@ -68,9 +74,6 @@ public class ModList this.fileById = this.modFiles.stream().map(ModFileInfo::getMods).flatMap(Collection::stream). map(ModInfo.class::cast). collect(Collectors.toMap(ModInfo::getModId, ModInfo::getOwningFile)); - final int loadingThreadCount = FMLConfig.loadingThreadCount(); - LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount); - modLoadingThreadPool = new ForkJoinPool(loadingThreadCount, ModList::newForkJoinWorkerThread, null, false); CrashReportExtender.registerCrashCallable("Mod List", this::crashReport); } @@ -93,10 +96,6 @@ public class ModList return INSTANCE; } - static LifecycleEventProvider.EventHandler>, Executor, Runnable> inlineDispatcher = (event, errors, executor, ticker) -> ModList.get().dispatchSynchronousEvent(event, errors, executor, ticker); - - static LifecycleEventProvider.EventHandler>, Executor, Runnable> parallelDispatcher = (event, errors, executor, ticker) -> ModList.get().dispatchParallelEvent(event, errors, executor, ticker); - public static ModList get() { return INSTANCE; } @@ -119,38 +118,47 @@ public class ModList return this.fileById.get(modid); } - private void dispatchSynchronousEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler, final Executor executor, final Runnable ticker) { - LOGGER.debug(LOADING, "Dispatching synchronous event {}", lifecycleEvent); - executor.execute(ticker); - FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); - this.mods.stream().forEach(m->m.transitionState(lifecycleEvent, errorHandler)); - FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); + Function>> futureVisitor( + final ModLoadingStage.EventGenerator eventGenerator, + final ModLoadingStage.EventDispatcher eventManager, + final BiFunction stateChange) { + return executor -> gather( + this.mods.stream() + .map(m -> ModContainer.buildTransitionHandler(m, eventGenerator, eventManager, stateChange, executor)) + .collect(Collectors.toList())) + .thenComposeAsync(ModList::completableFutureFromExceptionList, executor); } - private void dispatchParallelEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer> errorHandler, final Executor executor, final Runnable ticker) { - LOGGER.debug(LOADING, "Dispatching parallel event {}", lifecycleEvent); - FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); - DeferredWorkQueue.clear(); - try - { - final ForkJoinTask parallelTask = modLoadingThreadPool.submit(() -> this.mods.parallelStream().forEach(m -> m.transitionState(lifecycleEvent, errorHandler))); - while (ticker != null && !parallelTask.isDone()) { - executor.execute(ticker); - } - parallelTask.get(); + static CompletionStage> completableFutureFromExceptionList(List> t) { + if (t.stream().noneMatch(e->e.getValue()!=null)) { + return CompletableFuture.completedFuture(Collections.emptyList()); + } else { + final List throwables = t.stream().filter(e -> e.getValue() != null).map(Map.Entry::getValue).collect(Collectors.toList()); + CompletableFuture> cf = new CompletableFuture<>(); + final RuntimeException accumulator = new RuntimeException(); + cf.completeExceptionally(accumulator); + throwables.forEach(exception -> { + if (exception instanceof CompletionException) { + exception = exception.getCause(); + } + if (exception.getSuppressed().length!=0) { + Arrays.stream(exception.getSuppressed()).forEach(accumulator::addSuppressed); + } else { + accumulator.addSuppressed(exception); + } + }); + return cf; } - catch (InterruptedException | ExecutionException e) - { - LOGGER.error(LOADING, "Encountered an exception during parallel processing - sleeping 10 seconds to wait for jobs to finish", e); - errorHandler.accept(Collections.singletonList(new UncaughtModLoadingException(lifecycleEvent.fromStage(), e))); - modLoadingThreadPool.awaitQuiescence(10, TimeUnit.SECONDS); - if (!modLoadingThreadPool.isQuiescent()) { - LOGGER.fatal(LOADING, "The parallel pool has failed to quiesce correctly, forcing a shutdown. There is something really wrong here"); - modLoadingThreadPool.shutdownNow(); - throw new RuntimeException("Forge played \"STOP IT NOW MODS!\" - it was \"NOT VERY EFFECTIVE\""); - } - } - DeferredWorkQueue.runTasks(lifecycleEvent.fromStage(), errorHandler); - FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent)); + } + + static CompletableFuture>> gather(List> futures) { + List> list = new ArrayList<>(futures.size()); + CompletableFuture[] results = new CompletableFuture[futures.size()]; + futures.forEach(future -> { + int i = list.size(); + list.add(null); + results[i] = future.whenComplete((result, exception) -> list.set(i, new AbstractMap.SimpleImmutableEntry<>(result, exception))); + }); + return CompletableFuture.allOf(results).handle((r, th)->null).thenApply(res -> list); } void setLoadedMods(final List modContainers) diff --git a/src/main/java/net/minecraftforge/fml/ModLoader.java b/src/main/java/net/minecraftforge/fml/ModLoader.java index 7c1b25796..c1c7c2b43 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -29,6 +29,7 @@ import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.config.ConfigTracker; import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; +import net.minecraftforge.fml.event.lifecycle.IModBusEvent; import net.minecraftforge.fml.event.lifecycle.ModLifecycleEvent; import net.minecraftforge.fml.loading.FMLLoader; import net.minecraftforge.fml.loading.FMLPaths; @@ -48,12 +49,19 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.nio.file.Path; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Collectors; import static net.minecraftforge.fml.Logging.CORE; @@ -147,37 +155,22 @@ public class ModLoader return INSTANCE == null ? INSTANCE = new ModLoader() : INSTANCE; } - public void loadMods(Executor mainThreadExecutor, Consumer>> preSidedRunnable, Consumer>> postSidedRunnable) { - DeferredWorkQueue.workExecutor = mainThreadExecutor; - statusConsumer.ifPresent(c->c.accept("Loading mod config")); - DistExecutor.runWhenOn(Dist.CLIENT, ()->()-> ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.CLIENT, FMLPaths.CONFIGDIR.get())); - ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.COMMON, FMLPaths.CONFIGDIR.get()); - statusConsumer.ifPresent(c->c.accept("Mod setup: SETUP")); - dispatchAndHandleError(LifecycleEventProvider.SETUP, mainThreadExecutor, null); - statusConsumer.ifPresent(c->c.accept("Mod setup: SIDED SETUP")); - mainThreadExecutor.execute(()->preSidedRunnable.accept(c->ModList.get().forEachModContainer((mi,mc)->mc.acceptEvent(c.get())))); - dispatchAndHandleError(LifecycleEventProvider.SIDED_SETUP, mainThreadExecutor, null); - mainThreadExecutor.execute(()->postSidedRunnable.accept(c->ModList.get().forEachModContainer((mi,mc)->mc.acceptEvent(c.get())))); - statusConsumer.ifPresent(c->c.accept("Mod setup complete")); - } - - private static class SpacedRunnable implements Executor { - private static final long FIFTYMILLIS = TimeUnit.MILLISECONDS.toNanos(50); - private long next = System.nanoTime() + FIFTYMILLIS; - @Override - public void execute(final Runnable command) { - final long time = System.nanoTime(); - if (next < time) { - command.run(); - next = time + FIFTYMILLIS; - } - } - } - public void gatherAndInitializeMods(final Runnable ticker) { + /** + * Run on the primary starting thread by ClientModLoader and ServerModLoader + * @param syncExecutor An executor to run tasks on the main thread + * @param parallelExecutor An executor to run tasks on a parallel loading thread pool + * @param periodicTask Optional periodic task to perform on the main thread while other activities run + */ + public void gatherAndInitializeMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) { statusConsumer.ifPresent(c->c.accept("Waiting for scan to complete")); - FMLLoader.backgroundScanHandler.waitForScanToComplete(ticker); + FMLLoader.backgroundScanHandler.waitForScanToComplete(periodicTask); statusConsumer.ifPresent(c->c.accept("Loading mods")); - final ModList modList = ModList.of(loadingModList.getModFiles().stream().map(ModFileInfo::getFile).collect(Collectors.toList()), loadingModList.getMods()); + final ModList modList = ModList.of(loadingModList + .getModFiles() + .stream() + .map(ModFileInfo::getFile) + .collect(Collectors.toList()), + loadingModList.getMods()); if (!this.loadingExceptions.isEmpty()) { LOGGER.fatal(CORE, "Error during pre-loading phase", loadingExceptions.get(0)); modList.setLoadedMods(Collections.emptyList()); @@ -195,31 +188,74 @@ public class ModLoader throw new LoadingFailedException(loadingExceptions); } modList.setLoadedMods(modContainers); - SpacedRunnable sr = new SpacedRunnable(); statusConsumer.ifPresent(c->c.accept(String.format("Constructing %d mods", modList.size()))); - dispatchAndHandleError(LifecycleEventProvider.CONSTRUCT, sr, ticker); + dispatchAndHandleError(ModLoadingStage.CONSTRUCT, syncExecutor, parallelExecutor, periodicTask); statusConsumer.ifPresent(c->c.accept("Creating registries")); - GameData.fireCreateRegistryEvents(LifecycleEventProvider.CREATE_REGISTRIES, event -> dispatchAndHandleError(event, sr, ticker)); + dispatchAndHandleError(ModLoadingStage.CREATE_REGISTRIES, syncExecutor, parallelExecutor, periodicTask); ObjectHolderRegistry.findObjectHolders(); CapabilityManager.INSTANCE.injectCapabilities(modList.getAllScanData()); statusConsumer.ifPresent(c->c.accept("Populating registries")); - GameData.fireRegistryEvents(rl->true, LifecycleEventProvider.LOAD_REGISTRIES, event -> dispatchAndHandleError(event, sr, ticker)); + dispatchAndHandleError(ModLoadingStage.LOAD_REGISTRIES, syncExecutor, parallelExecutor, periodicTask); statusConsumer.ifPresent(c->c.accept("Early mod loading complete")); } - private void dispatchAndHandleError(LifecycleEventProvider event, Executor executor, final Runnable ticker) { - if (!loadingExceptions.isEmpty()) { - LOGGER.error(LOADING,"Skipping lifecycle event {}, {} errors found.", event, loadingExceptions.size()); - } else { - event.dispatch(this::accumulateErrors, executor, ticker); - } - if (!loadingExceptions.isEmpty()) { - LOGGER.fatal(LOADING,"Failed to complete lifecycle event {}, {} errors found", event, loadingExceptions.size()); - throw new LoadingFailedException(loadingExceptions); - } + public void loadMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Function> beforeSidedEvent, final Function> afterSidedEvent, final Runnable periodicTask) { + statusConsumer.ifPresent(c->c.accept("Loading mod config")); + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, ()->()-> ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.CLIENT, FMLPaths.CONFIGDIR.get())); + ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.COMMON, FMLPaths.CONFIGDIR.get()); + statusConsumer.ifPresent(c->c.accept("Mod setup: SETUP")); + dispatchAndHandleError(ModLoadingStage.COMMON_SETUP, syncExecutor, parallelExecutor, periodicTask); + statusConsumer.ifPresent(c->c.accept("Mod setup: SIDED SETUP")); + dispatchAndHandleError(ModLoadingStage.SIDED_SETUP, syncExecutor, parallelExecutor, periodicTask, beforeSidedEvent, afterSidedEvent); + statusConsumer.ifPresent(c->c.accept("Mod setup complete")); } - private void accumulateErrors(List errors) { - loadingExceptions.addAll(errors); + + public void finishMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) + { + statusConsumer.ifPresent(c->c.accept("Mod setup: ENQUEUE IMC")); + dispatchAndHandleError(ModLoadingStage.ENQUEUE_IMC, syncExecutor, parallelExecutor, periodicTask); + statusConsumer.ifPresent(c->c.accept("Mod setup: PROCESS IMC")); + dispatchAndHandleError(ModLoadingStage.PROCESS_IMC, syncExecutor, parallelExecutor, periodicTask); + statusConsumer.ifPresent(c->c.accept("Mod setup: Final completion")); + dispatchAndHandleError(ModLoadingStage.COMPLETE, syncExecutor, parallelExecutor, periodicTask); + statusConsumer.ifPresent(c->c.accept("Freezing data")); + GameData.freezeData(); + NetworkRegistry.lock(); + statusConsumer.ifPresent(c->c.accept(String.format("Mod loading complete - %d mods loaded", ModList.get().size()))); + } + + private void dispatchAndHandleError(ModLoadingStage state, ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor, final Runnable ticker) { + waitForTransition(state, syncExecutor, ticker, state.buildTransition(syncExecutor, parallelExecutor)); + } + + private void dispatchAndHandleError(ModLoadingStage state, ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor, final Runnable ticker, Function> preSyncTask, Function> postSyncTask) { + waitForTransition(state, syncExecutor, ticker, state.buildTransition(syncExecutor, parallelExecutor, preSyncTask, postSyncTask)); + } + + private void waitForTransition(final ModLoadingStage state, final ModWorkManager.DrivenExecutor syncExecutor, final Runnable ticker, final CompletableFuture> transition) { + while (!transition.isDone()) { + ticker.run(); + syncExecutor.drive(); + } + try { + transition.join(); + } catch (CompletionException e) { + Throwable t = e.getCause(); + final List notModLoading = Arrays.stream(t.getSuppressed()) + .filter(obj -> !(obj instanceof ModLoadingException)) + .collect(Collectors.toList()); + if (!notModLoading.isEmpty()) { + LOGGER.fatal("Encountered non-modloading exceptions!", e); + throw e; + } + + final List modLoadingExceptions = Arrays.stream(t.getSuppressed()) + .filter(ModLoadingException.class::isInstance) + .map(ModLoadingException.class::cast) + .collect(Collectors.toList()); + LOGGER.fatal(LOADING,"Failed to complete lifecycle event {}, {} errors found", state, modLoadingExceptions.size()); + throw new LoadingFailedException(modLoadingExceptions); + } } private List buildMods(final ModFile modFile, final TransformingClassLoader modClassLoader) @@ -229,7 +265,7 @@ public class ModLoader LOGGER.debug(LOADING, "ModContainer is {}", ModContainer.class.getClassLoader()); final List containers = modFile.getScanResult().getTargets().entrySet().stream(). map(e -> buildModContainerFromTOML(modFile, modClassLoader, modInfoMap, e)) - .filter(e -> e != null) + .filter(Objects::nonNull) .collect(Collectors.toList()); if (containers.size() != modInfoMap.size()) { LOGGER.fatal(LOADING,"File {} constructed {} mods: {}, but had {} mods specified: {}", @@ -256,25 +292,10 @@ public class ModLoader } } - public void postEvent(Event e) { + public void postEvent(T e) { ModList.get().forEachModContainer((id, mc) -> mc.acceptEvent(e)); } - public void finishMods(Executor mainThreadExecutor) - { - DeferredWorkQueue.workExecutor = mainThreadExecutor; - statusConsumer.ifPresent(c->c.accept("Mod setup: ENQUEUE IMC")); - dispatchAndHandleError(LifecycleEventProvider.ENQUEUE_IMC, mainThreadExecutor, null); - statusConsumer.ifPresent(c->c.accept("Mod setup: PROCESS IMC")); - dispatchAndHandleError(LifecycleEventProvider.PROCESS_IMC, mainThreadExecutor, null); - statusConsumer.ifPresent(c->c.accept("Mod setup: Final completion")); - dispatchAndHandleError(LifecycleEventProvider.COMPLETE, mainThreadExecutor, null); - statusConsumer.ifPresent(c->c.accept("Freezing data")); - GameData.freezeData(); - NetworkRegistry.lock(); - statusConsumer.ifPresent(c->c.accept(String.format("Mod loading complete - %d mods loaded", ModList.get().size()))); - } - public List getWarnings() { return ImmutableList.copyOf(this.loadingWarnings); @@ -292,15 +313,15 @@ public class ModLoader } public void runDataGenerator(final Set mods, final Path path, final Collection inputs, Collection existingPacks, final boolean serverGenerators, final boolean clientGenerators, final boolean devToolGenerators, final boolean reportsGenerator, final boolean structureValidator, final boolean flat) { - if (mods.contains("minecraft") && mods.size() == 1) return; - LOGGER.info("Initializing Data Gatherer for mods {}", mods); - runningDataGen = true; - Bootstrap.register(); - dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat); - existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator); - gatherAndInitializeMods(() -> {}); - dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, () -> {}); - dataGeneratorConfig.runAll(); +// if (mods.contains("minecraft") && mods.size() == 1) return; +// LOGGER.info("Initializing Data Gatherer for mods {}", mods); +// runningDataGen = true; +// Bootstrap.register(); +// dataGeneratorConfig = new GatherDataEvent.DataGeneratorConfig(mods, path, inputs, serverGenerators, clientGenerators, devToolGenerators, reportsGenerator, structureValidator, flat); +// existingFileHelper = new ExistingFileHelper(existingPacks, structureValidator); +// gatherAndInitializeMods(() -> {}); +// dispatchAndHandleError(LifecycleEventProvider.GATHERDATA, Runnable::run, () -> {}); +// dataGeneratorConfig.runAll(); } public Function getDataGeneratorEvent() { diff --git a/src/main/java/net/minecraftforge/fml/ModLoadingStage.java b/src/main/java/net/minecraftforge/fml/ModLoadingStage.java index b59a776a3..157cdb7d8 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoadingStage.java +++ b/src/main/java/net/minecraftforge/fml/ModLoadingStage.java @@ -19,34 +19,140 @@ package net.minecraftforge.fml; -import net.minecraftforge.fml.event.lifecycle.*; +import cpw.mods.modlauncher.api.LamdbaExceptionUtils; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; +import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; +import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; +import net.minecraftforge.fml.event.lifecycle.IModBusEvent; +import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; +import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; +import net.minecraftforge.fml.event.lifecycle.ParallelDispatchEvent; +import net.minecraftforge.registries.GameData; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Stream; 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), - GATHERDATA(ModLoader.get()::getDataGeneratorEvent); - private final Supplier> modLifecycleEventFunction; + ERROR(), + VALIDATE(), + CONSTRUCT(FMLConstructModEvent.class), + CREATE_REGISTRIES(()->Stream.of(RegistryEvent.NewRegistry::new), EventDispatcher.identity()), + LOAD_REGISTRIES(GameData::generateRegistryEvents, GameData.buildRegistryEventDispatch()), + COMMON_SETUP(FMLCommonSetupEvent.class), + SIDED_SETUP(DistExecutor.unsafeRunForDist(()->()->FMLClientSetupEvent.class, ()->()->FMLDedicatedServerSetupEvent.class)), + ENQUEUE_IMC(InterModEnqueueEvent.class), + PROCESS_IMC(InterModProcessEvent.class), + COMPLETE(FMLLoadCompleteEvent.class), + DONE(), + GATHERDATA(); - ModLoadingStage(Supplier> modLifecycleEventFunction) - { - this.modLifecycleEventFunction = modLifecycleEventFunction; + private final Supplier>> eventFunctionStream; + private final EventDispatcher eventManager; + private final Optional> parallelEventClass; + private final ThreadSelector threadSelector; + private final BiFunction>, CompletableFuture>> finalActivityGenerator; + + ModLoadingStage(Class parallelClass) { + final EventGenerator event = EventGenerator.fromFunction(LamdbaExceptionUtils.rethrowFunction((ModContainer mc) -> parallelClass.getConstructor(ModContainer.class).newInstance(mc))); + this.eventFunctionStream = ()->Stream.of(event); + this.threadSelector = ThreadSelector.PARALLEL; + this.eventManager = EventDispatcher.identity(); + this.parallelEventClass = Optional.of(parallelClass); + final DeferredWorkQueue deferredWorkQueue = new DeferredWorkQueue(this, parallelClass); + this.finalActivityGenerator = (e, prev) -> prev.thenApplyAsync((List t) -> { + deferredWorkQueue.runTasks(); + return t; + }, e); } - public ModLifecycleEvent getModEvent(ModContainer modContainer) - { - return modLifecycleEventFunction.get().apply(modContainer); + @SuppressWarnings("unchecked") + ModLoadingStage(Supplier>> eventStream, EventDispatcher eventManager) { + this.eventFunctionStream = eventStream; + this.parallelEventClass = Optional.empty(); + this.eventManager = eventManager; + this.threadSelector = ThreadSelector.SYNC; + this.finalActivityGenerator = (e, prev) ->prev.thenApplyAsync(Function.identity(), e); + } + + ModLoadingStage() { + this(ParallelDispatchEvent.class); + } + + public CompletableFuture> buildTransition(final Executor syncExecutor, final Executor parallelExecutor) { + return buildTransition(syncExecutor, parallelExecutor, e->CompletableFuture.runAsync(()->{}, e), e->CompletableFuture.runAsync(()->{}, e)); + } + @SuppressWarnings("unchecked") + public CompletableFuture> buildTransition(final Executor syncExecutor, final Executor parallelExecutor, Function> preSyncTask, Function> postSyncTask) { + List>> cfs = new ArrayList<>(); + EventDispatcher em = (EventDispatcher) this.eventManager; + eventFunctionStream.get() + .map(f->(EventGenerator)f) + .reduce((head, tail)->{ + cfs.add(ModList.get() + .futureVisitor(head, em, ModLoadingStage::currentState) + .apply(threadSelector.apply(syncExecutor, parallelExecutor)) + ); + return tail; + }) + .ifPresent(last->cfs.add(ModList.get().futureVisitor(last, em, ModLoadingStage::nextState).apply(threadSelector.apply(syncExecutor, parallelExecutor)))); + final CompletableFuture preSyncTaskCF = preSyncTask.apply(syncExecutor); + final CompletableFuture> eventDispatchCF = ModList.gather(cfs).thenCompose(ModList::completableFutureFromExceptionList); + final CompletableFuture> postEventDispatchCF = preSyncTaskCF.thenComposeAsync(n -> eventDispatchCF, parallelExecutor).thenApply(r -> { + postSyncTask.apply(syncExecutor); + return r; + }); + return this.finalActivityGenerator.apply(syncExecutor, postEventDispatchCF); + } + + ModLoadingStage nextState(Throwable exception) { + return exception != null ? ERROR : values()[this.ordinal()+1]; + } + + ModLoadingStage currentState(Throwable exception) { + return exception != null ? ERROR : this; + } + + public Optional> getParallelEventClass() { + return parallelEventClass; + } + enum ThreadSelector implements BinaryOperator { + SYNC((sync, parallel)->sync), + PARALLEL((sync, parallel)->parallel); + + private final BinaryOperator selector; + + ThreadSelector(final BinaryOperator selector) { + this.selector = selector; + } + + @Override + public Executor apply(final Executor sync, final Executor parallel) { + return this.selector.apply(sync, parallel); + } + } + + public interface EventGenerator extends Function { + static EventGenerator fromFunction(Function fn) { + return fn::apply; + } + } + public interface EventDispatcher extends Function, Consumer> { + static EventDispatcher identity() { + return consumer -> consumer; + } } } diff --git a/src/main/java/net/minecraftforge/fml/ModWorkManager.java b/src/main/java/net/minecraftforge/fml/ModWorkManager.java new file mode 100644 index 000000000..1a0765f78 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/ModWorkManager.java @@ -0,0 +1,103 @@ +package net.minecraftforge.fml; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; + +import static net.minecraftforge.fml.Logging.LOADING; + +public class ModWorkManager { + private static final Logger LOGGER = LogManager.getLogger(); + + public interface DrivenExecutor extends Executor { + boolean selfDriven(); + boolean driveOne(); + + default void drive() { + if (!selfDriven()) { + while (driveOne()) + ; + } + } + } + private static class SyncExecutor implements DrivenExecutor { + private ConcurrentLinkedDeque tasks = new ConcurrentLinkedDeque<>(); + + @Override + public boolean driveOne() { + final Runnable task = tasks.pollFirst(); + if (task != null) { + task.run(); + } + return task != null; + } + + @Override + public boolean selfDriven() { + return false; + } + + @Override + public void execute(final Runnable command) { + tasks.addLast(command); + } + } + + private static class WrappingExecutor implements DrivenExecutor { + private final Executor wrapped; + + public WrappingExecutor(final Executor executor) { + this.wrapped = executor; + } + + @Override + public boolean selfDriven() { + return true; + } + + @Override + public boolean driveOne() { + return false; + } + + @Override + public void execute(final Runnable command) { + wrapped.execute(command); + } + } + + private static SyncExecutor syncExecutor; + + public static DrivenExecutor syncExecutor() { + if (syncExecutor == null) + syncExecutor = new SyncExecutor(); + return syncExecutor; + } + + public static DrivenExecutor wrappedExecutor(Executor executor) { + return new WrappingExecutor(executor); + } + + private static ForkJoinPool parallelThreadPool; + public static Executor parallelExecutor() { + if (parallelThreadPool == null) { + final int loadingThreadCount = 2; + LOGGER.debug(LOADING, "Using {} threads for parallel mod-loading", loadingThreadCount); + parallelThreadPool = new ForkJoinPool(loadingThreadCount, ModWorkManager::newForkJoinWorkerThread, null, false); + } + return parallelThreadPool; + } + + private static ForkJoinWorkerThread newForkJoinWorkerThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setName("modloading-worker-" + thread.getPoolIndex()); + // The default sets it to the SystemClassloader, so copy the current one. + thread.setContextClassLoader(Thread.currentThread().getContextClassLoader()); + return thread; + } + +} diff --git a/src/main/java/net/minecraftforge/fml/SidedProvider.java b/src/main/java/net/minecraftforge/fml/SidedProvider.java index 7026bb9a1..230c17978 100644 --- a/src/main/java/net/minecraftforge/fml/SidedProvider.java +++ b/src/main/java/net/minecraftforge/fml/SidedProvider.java @@ -22,10 +22,7 @@ package net.minecraftforge.fml; import net.minecraft.client.Minecraft; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.client.ClientHooks; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; import net.minecraftforge.fml.loading.FMLEnvironment; import java.util.function.Function; @@ -38,10 +35,6 @@ public enum SidedProvider c->c.get().getDataFixer(), s->s.get().getDataFixer(), ()-> { throw new UnsupportedOperationException(); }), - SIDED_SETUP_EVENT( - (Function, Function>)c-> mc->new FMLClientSetupEvent(c, mc), - s-> mc->new FMLDedicatedServerSetupEvent(mc), - ()-> { throw new UnsupportedOperationException(); }), STRIPCHARS( (Function, Function>)c-> ClientHooks::stripSpecialChars, s-> str->str, diff --git a/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java index 65304c93a..2eb0f9416 100644 --- a/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java +++ b/src/main/java/net/minecraftforge/fml/client/ClientModLoader.java @@ -32,9 +32,22 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.Consumer; -import java.util.function.Supplier; -import net.minecraft.resources.*; +import net.minecraft.resources.IFutureReloadListener; +import net.minecraft.resources.IPackNameDecorator; +import net.minecraft.resources.IReloadableResourceManager; +import net.minecraft.resources.IResourceManager; +import net.minecraft.resources.ResourcePackInfo; +import net.minecraft.resources.ResourcePackList; +import net.minecraftforge.fml.BrandingControl; +import net.minecraftforge.fml.LoadingFailedException; +import net.minecraftforge.fml.LogicalSidedProvider; +import net.minecraftforge.fml.ModLoader; +import net.minecraftforge.fml.ModLoadingStage; +import net.minecraftforge.fml.ModLoadingWarning; +import net.minecraftforge.fml.ModWorkManager; +import net.minecraftforge.fml.SidedProvider; +import net.minecraftforge.fml.VersionChecker; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -48,19 +61,9 @@ import net.minecraft.resources.data.PackMetadataSection; import net.minecraft.util.text.TranslationTextComponent; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.client.event.ModelRegistryEvent; import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.common.ForgeConfig; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.fml.BrandingControl; -import net.minecraftforge.fml.LoadingFailedException; -import net.minecraftforge.fml.LogicalSidedProvider; -import net.minecraftforge.fml.ModLoader; -import net.minecraftforge.fml.ModLoadingStage; -import net.minecraftforge.fml.ModLoadingWarning; -import net.minecraftforge.fml.SidedProvider; -import net.minecraftforge.fml.VersionChecker; import net.minecraftforge.fml.client.gui.screen.LoadingErrorScreen; import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.loading.moddiscovery.ModFile; @@ -90,17 +93,19 @@ public class ClientModLoader LogicalSidedProvider.setClient(()->minecraft); LanguageHook.loadForgeAndMCLangs(); earlyLoaderGUI = new EarlyLoaderGUI(minecraft.getMainWindow()); - createRunnableWithCatch(() -> ModLoader.get().gatherAndInitializeMods(earlyLoaderGUI::renderTick)).run(); - ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder); - mcResourceManager.addReloadListener(ClientModLoader::onreload); - mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener()); - ModelLoaderRegistry.init(); + createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), earlyLoaderGUI::renderTick)).run(); + if (error == null) { + ResourcePackLoader.loadResourcePacks(defaultResourcePacks, ClientModLoader::buildPackFinder); + mcResourceManager.addReloadListener(ClientModLoader::onResourceReload); + mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener()); + ModelLoaderRegistry.init(); + } } - private static CompletableFuture onreload(final IFutureReloadListener.IStage stage, final IResourceManager resourceManager, final IProfiler prepareProfiler, final IProfiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) { - return CompletableFuture.runAsync(createRunnableWithCatch(() -> startModLoading(syncExecutor)), asyncExecutor). - thenCompose(stage::markCompleteAwaitingOthers). - thenRunAsync(() -> finishModLoading(syncExecutor), asyncExecutor); + private static CompletableFuture onResourceReload(final IFutureReloadListener.IStage stage, final IResourceManager resourceManager, final IProfiler prepareProfiler, final IProfiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) { + return CompletableFuture.runAsync(createRunnableWithCatch(() -> startModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor)), asyncExecutor) + .thenCompose(stage::markCompleteAwaitingOthers) + .thenRunAsync(() -> finishModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor), asyncExecutor); } private static Runnable createRunnableWithCatch(Runnable r) { @@ -114,26 +119,27 @@ public class ClientModLoader }; } - private static void startModLoading(Executor executor) { + private static void startModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) { earlyLoaderGUI.handleElsewhere(); - createRunnableWithCatch(() -> ModLoader.get().loadMods(executor, ClientModLoader::preSidedRunnable, ClientModLoader::postSidedRunnable)).run(); + createRunnableWithCatch(() -> ModLoader.get().loadMods(syncExecutor, parallelExecutor, executor -> CompletableFuture.runAsync(ClientModLoader::preSidedRunnable, executor), executor -> CompletableFuture.runAsync(ClientModLoader::postSidedRunnable, executor), ()->{})).run(); } - private static void postSidedRunnable(Consumer> perModContainerEventProcessor) { + private static void postSidedRunnable() { + LOGGER.debug(LOADING, "Running post client event work"); RenderingRegistry.loadEntityRenderers(mc.getRenderManager()); ModelLoaderRegistry.initComplete(); } - private static void preSidedRunnable(Consumer> perModContainerEventProcessor) { - perModContainerEventProcessor.accept(ModelRegistryEvent::new); + private static void preSidedRunnable() { + LOGGER.debug(LOADING, "Running pre client event work"); } - private static void finishModLoading(Executor executor) + private static void finishModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) { - createRunnableWithCatch(() -> ModLoader.get().finishMods(executor)).run(); + createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, ()->{})).run(); loading = false; // reload game settings on main thread - executor.execute(()->mc.gameSettings.loadOptions()); + syncExecutor.execute(()->mc.gameSettings.loadOptions()); } public static VersionChecker.Status checkForUpdates() diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLClientSetupEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLClientSetupEvent.java index 17a21e695..a9ba525a6 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLClientSetupEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLClientSetupEvent.java @@ -38,14 +38,14 @@ import java.util.function.Supplier; * * This is a parallel dispatch event. */ -public class FMLClientSetupEvent extends ModLifecycleEvent +public class FMLClientSetupEvent extends ParallelDispatchEvent { private final Supplier minecraftSupplier; - public FMLClientSetupEvent(Supplier mc, ModContainer container) + public FMLClientSetupEvent(ModContainer container) { super(container); - this.minecraftSupplier = mc; + minecraftSupplier = Minecraft::getInstance; } public Supplier getMinecraftSupplier() diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLCommonSetupEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLCommonSetupEvent.java index 36d65ca2b..82788839f 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLCommonSetupEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLCommonSetupEvent.java @@ -40,7 +40,7 @@ import java.util.function.Consumer; * @see net.minecraftforge.fml.DeferredWorkQueue to enqueue work to run on the main game thread after this event has * completed dispatch */ -public class FMLCommonSetupEvent extends ModLifecycleEvent +public class FMLCommonSetupEvent extends ParallelDispatchEvent { public FMLCommonSetupEvent(final ModContainer container) { diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLConstructModEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLConstructModEvent.java new file mode 100644 index 000000000..4ea9bda24 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLConstructModEvent.java @@ -0,0 +1,9 @@ +package net.minecraftforge.fml.event.lifecycle; + +import net.minecraftforge.fml.ModContainer; + +public class FMLConstructModEvent extends ParallelDispatchEvent { + public FMLConstructModEvent(final ModContainer container) { + super(container); + } +} diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java index dc5812137..888a3f3b9 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java @@ -42,7 +42,7 @@ import java.util.function.Supplier; * * This is a parallel dispatch event. */ -public class FMLDedicatedServerSetupEvent extends ModLifecycleEvent +public class FMLDedicatedServerSetupEvent extends ParallelDispatchEvent { public FMLDedicatedServerSetupEvent(ModContainer container) { diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLLoadCompleteEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLLoadCompleteEvent.java index 68c81fddc..712780e67 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLLoadCompleteEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/FMLLoadCompleteEvent.java @@ -27,7 +27,7 @@ import net.minecraftforge.fml.ModContainer; * * @author cpw */ -public class FMLLoadCompleteEvent extends ModLifecycleEvent +public class FMLLoadCompleteEvent extends ParallelDispatchEvent { public FMLLoadCompleteEvent(final ModContainer container) { diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModEnqueueEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModEnqueueEvent.java index 880bf5d4d..89f9d0491 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModEnqueueEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModEnqueueEvent.java @@ -32,7 +32,7 @@ import net.minecraftforge.fml.ModContainer; * * This is a parallel dispatch event. */ -public class InterModEnqueueEvent extends ModLifecycleEvent +public class InterModEnqueueEvent extends ParallelDispatchEvent { public InterModEnqueueEvent(final ModContainer container) diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModProcessEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModProcessEvent.java index 65225e021..a5ce93ed2 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModProcessEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/InterModProcessEvent.java @@ -36,7 +36,7 @@ import java.util.function.Predicate; * @see #getIMCStream() * @see #getIMCStream(Predicate) */ -public class InterModProcessEvent extends ModLifecycleEvent +public class InterModProcessEvent extends ParallelDispatchEvent { public InterModProcessEvent(final ModContainer container) { diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/ModLifecycleEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/ModLifecycleEvent.java index f52dcada8..430f0cde2 100644 --- a/src/main/java/net/minecraftforge/fml/event/lifecycle/ModLifecycleEvent.java +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/ModLifecycleEvent.java @@ -54,6 +54,10 @@ public class ModLifecycleEvent extends Event implements IModBusEvent return InterModComms.getMessages(this.container.getModId(), methodFilter); } + ModContainer getContainer() { + return this.container; + } + @Override public String toString() { return description(); diff --git a/src/main/java/net/minecraftforge/fml/event/lifecycle/ParallelDispatchEvent.java b/src/main/java/net/minecraftforge/fml/event/lifecycle/ParallelDispatchEvent.java new file mode 100644 index 000000000..597ebe8eb --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/event/lifecycle/ParallelDispatchEvent.java @@ -0,0 +1,26 @@ +package net.minecraftforge.fml.event.lifecycle; + +import net.minecraftforge.fml.DeferredWorkQueue; +import net.minecraftforge.fml.ModContainer; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class ParallelDispatchEvent extends ModLifecycleEvent { + public ParallelDispatchEvent(final ModContainer container) { + super(container); + } + + private Optional getQueue() { + return DeferredWorkQueue.lookup(Optional.of(getClass())); + } + + public CompletableFuture enqueueWork(Runnable work) { + return getQueue().map(q->q.enqueueWork(getContainer().getModInfo(), work)).orElseThrow(()->new RuntimeException("No work queue found!")); + } + + public CompletableFuture enqueueWork(Supplier work) { + return getQueue().map(q->q.enqueueWork(getContainer().getModInfo(), work)).orElseThrow(()->new RuntimeException("No work queue found!")); + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java index 906f921b8..4acd64ad8 100644 --- a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java +++ b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLModContainer.java @@ -25,20 +25,16 @@ import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.IEventListener; import net.minecraftforge.fml.AutomaticEventSubscriber; -import net.minecraftforge.fml.LifecycleEventProvider; import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModLoadingException; import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.event.lifecycle.IModBusEvent; -import net.minecraftforge.fml.event.lifecycle.ModLifecycleEvent; import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.forgespi.language.ModFileScanData; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.Optional; -import java.util.function.Consumer; import static net.minecraftforge.fml.Logging.LOADING; @@ -55,17 +51,9 @@ public class FMLModContainer extends ModContainer super(info); LOGGER.debug(LOADING,"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)); - triggerMap.put(ModLoadingStage.PROCESS_IMC, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent)); - triggerMap.put(ModLoadingStage.COMPLETE, dummy().andThen(this::beforeEvent).andThen(this::completeLoading).andThen(this::fireEvent).andThen(this::afterEvent)); - triggerMap.put(ModLoadingStage.GATHERDATA, dummy().andThen(this::beforeEvent).andThen(this::fireEvent).andThen(this::afterEvent)); + activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod); this.eventBus = BusBuilder.builder().setExceptionHandler(this::onEventFailed).setTrackPhases(false).markerType(IModBusEvent.class).build(); - this.configHandler = Optional.of(event -> this.eventBus.post(event)); + this.configHandler = Optional.of(this.eventBus::post); final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this); this.contextExtension = () -> contextExtension; try @@ -80,52 +68,12 @@ public class FMLModContainer extends ModContainer } } - private void completeLoading(LifecycleEventProvider.LifecycleEvent lifecycleEvent) - { - - } - - private void initMod(LifecycleEventProvider.LifecycleEvent lifecycleEvent) - { - - } - - private Consumer dummy() { return (s) -> {}; } - private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) { LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable)); } - private void beforeEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) { - } - - private void fireEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) { - final Event event = lifecycleEvent.getOrBuildEvent(this); - LOGGER.debug(LOADING, "Firing event for modid {} : {}", this.getModId(), event); - try - { - eventBus.post(event); - LOGGER.debug(LOADING, "Fired event for modid {} : {}", this.getModId(), event); - } - catch (Throwable e) - { - LOGGER.error(LOADING,"Caught exception during event {} dispatch for modid {}", event, this.getModId(), e); - throw new ModLoadingException(modInfo, lifecycleEvent.fromStage(), "fml.modloading.errorduringevent", e); - } - } - - private void afterEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) { - if (getCurrentState() == ModLoadingStage.ERROR) { - LOGGER.error(LOADING,"An error occurred while dispatching event {} to {}", lifecycleEvent.fromStage(), getModId()); - } - } - - private void preinitMod(LifecycleEventProvider.LifecycleEvent lifecycleEvent) - { - } - - private void constructMod(LifecycleEventProvider.LifecycleEvent event) + private void constructMod() { try { @@ -136,7 +84,7 @@ public class FMLModContainer extends ModContainer catch (Throwable e) { LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e); - throw new ModLoadingException(modInfo, event.fromStage(), "fml.modloading.failedtoloadmod", e, modClass); + throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass); } try { LOGGER.debug(LOADING, "Injecting Automatic event subscribers for {}", getModId()); @@ -144,7 +92,7 @@ public class FMLModContainer extends ModContainer LOGGER.debug(LOADING, "Completed Automatic event subscribers for {}", getModId()); } catch (Throwable e) { LOGGER.error(LOADING,"Failed to register automatic subscribers. ModID: {}, class {}", getModId(), modClass.getName(), e); - throw new ModLoadingException(modInfo, event.fromStage(), "fml.modloading.failedtoloadmod", e, modClass); + throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass); } } @@ -166,7 +114,14 @@ public class FMLModContainer extends ModContainer } @Override - protected void acceptEvent(final Event e) { - this.eventBus.post(e); + protected void acceptEvent(final T e) { + try { + LOGGER.debug(LOADING, "Firing event for modid {} : {}", this.getModId(), e); + this.eventBus.post(e); + LOGGER.debug(LOADING, "Fired event for modid {} : {}", this.getModId(), e); + } catch (Throwable t) { + LOGGER.error(LOADING,"Caught exception during event {} dispatch for modid {}", e, this.getModId(), t); + throw new ModLoadingException(modInfo, modLoadingStage, "fml.modloading.errorduringevent", t); + } } } diff --git a/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java b/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java index 84055fb46..8e17b9b55 100644 --- a/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java +++ b/src/main/java/net/minecraftforge/fml/server/ServerModLoader.java @@ -24,6 +24,7 @@ import net.minecraftforge.fml.LoadingFailedException; import net.minecraftforge.fml.LogicalSidedProvider; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoadingWarning; +import net.minecraftforge.fml.ModWorkManager; import net.minecraftforge.fml.SidedProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -46,9 +47,9 @@ public class ServerModLoader }); LanguageHook.loadForgeAndMCLangs(); try { - ModLoader.get().gatherAndInitializeMods(() -> {}); - ModLoader.get().loadMods(Runnable::run, (a)->{}, (a)->{}); - ModLoader.get().finishMods(Runnable::run); + ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{}); + ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), null, null, ()->{}); + ModLoader.get().finishMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{}); } catch (LoadingFailedException e) { ServerModLoader.hasErrors = true; throw e; diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 84008d453..b7779f810 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -51,7 +51,6 @@ import net.minecraft.util.ObjectIntIdentityMap; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; import net.minecraft.util.registry.DefaultedRegistry; -import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.SimpleRegistry; import net.minecraft.village.PointOfInterestType; import net.minecraft.world.biome.Biome; @@ -69,8 +68,8 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.loot.GlobalLootModifierSerializer; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.event.RegistryEvent.MissingMappings; -import net.minecraftforge.fml.LifecycleEventProvider; import net.minecraftforge.fml.ModLoadingContext; +import net.minecraftforge.fml.ModLoadingStage; import net.minecraftforge.fml.common.EnhancedRuntimeException; import net.minecraftforge.fml.common.thread.EffectiveSide; import net.minecraftforge.fml.event.lifecycle.FMLModIdMappingEvent; @@ -91,9 +90,8 @@ 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; +import java.util.stream.Stream; import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES; @@ -367,6 +365,34 @@ public class GameData LOGGER.debug(REGISTRIES, "Reverting complete"); } + public static Stream> generateRegistryEvents() { + List keys = Lists.newArrayList(RegistryManager.ACTIVE.registries.keySet()); + keys.sort((o1, o2) -> String.valueOf(o1).compareToIgnoreCase(String.valueOf(o2))); + + //Move Blocks to first, and Items to second. + keys.remove(BLOCKS); + keys.remove(ITEMS); + + keys.add(0, BLOCKS); + keys.add(1, ITEMS); + + return keys.stream().map(rl -> mc -> RegistryManager.ACTIVE.getRegistry(rl).getRegisterEvent(rl)); + } + + public static ModLoadingStage.EventDispatcher> buildRegistryEventDispatch() { + return eventConsumer -> eventToSend -> { + final ResourceLocation rl = eventToSend.getName(); + ForgeRegistry fr = (ForgeRegistry) eventToSend.getRegistry(); + StartupMessageManager.modLoaderConsumer().ifPresent(s->s.accept("REGISTERING "+rl)); + fr.unfreeze(); + eventConsumer.accept(eventToSend); + fr.freeze(); + LOGGER.debug(REGISTRIES,"Applying holder lookups: {}", rl.toString()); + ObjectHolderRegistry.applyObjectHolders(rl::equals); + LOGGER.debug(REGISTRIES,"Holder lookups applied: {}", rl.toString()); + }; + } + //Lets us clear the map so we can rebuild it. private static class ClearableObjectIntIdentityMap extends ObjectIntIdentityMap { @@ -832,47 +858,6 @@ public class GameData newRegistry.loadIds(_new, frozen.getOverrideOwners(), Maps.newLinkedHashMap(), remaps, frozen, name); } - public static void fireCreateRegistryEvents() - { - MinecraftForge.EVENT_BUS.post(new RegistryEvent.NewRegistry()); - } - - 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, final LifecycleEventProvider lifecycleEventProvider, final Consumer eventDispatcher) - { - List keys = Lists.newArrayList(RegistryManager.ACTIVE.registries.keySet()); - keys.sort((o1, o2) -> String.valueOf(o1).compareToIgnoreCase(String.valueOf(o2))); - - //Move Blocks to first, and Items to second. - keys.remove(BLOCKS); - keys.remove(ITEMS); - - keys.add(0, BLOCKS); - keys.add(1, ITEMS); - 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(); - StartupMessageManager.modLoaderConsumer().ifPresent(s->s.accept("REGISTERING "+rl)); - final RegistryEvent.Register registerEvent = reg.getRegisterEvent(rl); - lifecycleEventProvider.setCustomEventSupplier(() -> registerEvent); - lifecycleEventProvider.changeProgression(LifecycleEventProvider.LifecycleEvent.Progression.STAY); - if (i==keysSize-1) lifecycleEventProvider.changeProgression(LifecycleEventProvider.LifecycleEvent.Progression.NEXT); - eventDispatcher.accept(lifecycleEventProvider); - reg.freeze(); - LOGGER.debug(REGISTRIES,"Applying holder lookups: {}", rl.toString()); - ObjectHolderRegistry.applyObjectHolders(rl::equals); - LOGGER.debug(REGISTRIES,"Holder lookups applied: {}", rl.toString()); - } - } - /** * Check a name for a domain prefix, and if not present infer it from the * current active mod container. diff --git a/src/test/java/net/minecraftforge/debug/client/rendering/StencilEnableTest.java b/src/test/java/net/minecraftforge/debug/client/rendering/StencilEnableTest.java index 103cfb8f4..ca5312c28 100644 --- a/src/test/java/net/minecraftforge/debug/client/rendering/StencilEnableTest.java +++ b/src/test/java/net/minecraftforge/debug/client/rendering/StencilEnableTest.java @@ -36,6 +36,6 @@ public class StencilEnableTest { private void clientSetup(FMLClientSetupEvent event) { if (ENABLED) - DeferredWorkQueue.runLater(() -> Minecraft.getInstance().getFramebuffer().enableStencil()); + event.enqueueWork(() -> Minecraft.getInstance().getFramebuffer().enableStencil()); } }