diff --git a/earlydisplay/src/main/java/net/neoforged/fml/earlydisplay/RenderElement.java b/earlydisplay/src/main/java/net/neoforged/fml/earlydisplay/RenderElement.java index 2d660c4cc..897d9bb4f 100644 --- a/earlydisplay/src/main/java/net/neoforged/fml/earlydisplay/RenderElement.java +++ b/earlydisplay/src/main/java/net/neoforged/fml/earlydisplay/RenderElement.java @@ -168,7 +168,6 @@ public static void startupProgressBars(SimpleFont font, final SimpleBufferBuilde final ProgressMeter pm = currentProgress.get(i); Renderer barRenderer = barRenderer(i, alpha, font, pm, context); acc = barRenderer.then(acc); - alpha >>= 1; } if (acc != null) acc.accept(buffer, context, frameNumber); diff --git a/loader/src/main/java/net/neoforged/fml/DeferredWorkQueue.java b/loader/src/main/java/net/neoforged/fml/DeferredWorkQueue.java index 47a17cffb..c36639af5 100644 --- a/loader/src/main/java/net/neoforged/fml/DeferredWorkQueue.java +++ b/loader/src/main/java/net/neoforged/fml/DeferredWorkQueue.java @@ -8,9 +8,6 @@ import static net.neoforged.fml.Logging.LOADING; import com.google.common.base.Stopwatch; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentLinkedDeque; @@ -35,24 +32,16 @@ public class DeferredWorkQueue { private static final Logger LOGGER = LogManager.getLogger(); - private static final Map workQueues = new HashMap<>(); - private final ConcurrentLinkedDeque tasks = new ConcurrentLinkedDeque<>(); - private final ModLoadingStage modLoadingStage; - - public DeferredWorkQueue(ModLoadingStage modLoadingStage) { - this.modLoadingStage = modLoadingStage; - workQueues.put(modLoadingStage, this); - } + private final String name; - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public static Optional lookup(Optional parallelClass) { - return Optional.ofNullable(workQueues.get(parallelClass.orElse(null))); + public DeferredWorkQueue(String name) { + this.name = name; } public void runTasks() { if (tasks.isEmpty()) return; - LOGGER.debug(LOADING, "Dispatching synchronous work for work queue {}: {} jobs", modLoadingStage, tasks.size()); + LOGGER.debug(LOADING, "Dispatching synchronous work for work queue {}: {} jobs", name, tasks.size()); RuntimeException aggregate = new RuntimeException(); Stopwatch timer = Stopwatch.createStarted(); tasks.forEach(t -> makeRunnable(t, Runnable::run, aggregate)); diff --git a/loader/src/main/java/net/neoforged/fml/IModLoadingState.java b/loader/src/main/java/net/neoforged/fml/IModLoadingState.java deleted file mode 100644 index d8483e050..000000000 --- a/loader/src/main/java/net/neoforged/fml/IModLoadingState.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.ToIntFunction; -import net.neoforged.bus.api.Event; -import net.neoforged.fml.event.IModBusEvent; -import net.neoforged.fml.loading.progress.ProgressMeter; - -/** - * A mod loading state. During mod loading, the mod loader transitions between states in a defined sorted list of states, - * grouped into various {@link ModLoadingPhase phases}. - * - * @see IModStateProvider - */ -public interface IModLoadingState { - /** - * {@return the name of this state} - */ - String name(); - - /** - * {@return the name of the state immediately previous to this state} This may be a blank name, which indicates this - * is either the first mod loading state or an exceptional mod loading state (such as a situation where errors - * prevent the loading process from continuing normally). - */ - String previous(); - - /** - * {@return the mod loading phase this state belongs to} For exceptional mod loading states, this should be - * {@link ModLoadingPhase#ERROR}. - */ - ModLoadingPhase phase(); - - /** - * {@return a function returning a human-friendly message for this state} - */ - Function message(); - - /** - * @return a function that computes the size of this transition based on the size of the modlist. - * Used to compute progress. - */ - ToIntFunction size(); - - /** - * {@return an optional runnable, which runs before starting the transition from this state to the next} - * - * @see #buildTransition(Executor, Executor, ProgressMeter, Function, Function) - */ - Optional> inlineRunnable(); - - /** - * Builds the transition task for this state with a blank pre-sync and post-sync task. - * - * @param a type of event fired on the mod-specific event bus - * @param syncExecutor a synchronous executor - * @param parallelExecutor a parallel executor - * @param progressBar a progress meter for tracking progress - * @return a transition task for this state - * @see #buildTransition(Executor, Executor, ProgressMeter, Function, Function) - */ - default Optional> buildTransition(final Executor syncExecutor, - final Executor parallelExecutor, - final ProgressMeter progressBar) { - return buildTransition(syncExecutor, parallelExecutor, progressBar, - e -> CompletableFuture.runAsync(() -> {}, e), - e -> CompletableFuture.runAsync(() -> {}, e)); - } - - /** - * Builds the transition task for this state. The pre-sync and post-sync task functions allow the transition builder - * to run these tasks on the same executor as the actual event dispatch and pre/post hooks. - * - * @param a type of event fired on the mod-specific event bus - * @param syncExecutor a synchronous executor - * @param parallelExecutor a parallel executor - * @param progressBar a progress meter for tracking progress - * @param preSyncTask a function which returns a task to run before event pre-dispatch hook - * @param postSyncTask a function which returns a task to run after event post-dispatch hook - * @return a transition task for this state - */ - Optional> buildTransition(final Executor syncExecutor, - final Executor parallelExecutor, - final ProgressMeter progressBar, - final Function> preSyncTask, - final Function> postSyncTask); -} diff --git a/loader/src/main/java/net/neoforged/fml/IModStateProvider.java b/loader/src/main/java/net/neoforged/fml/IModStateProvider.java deleted file mode 100644 index 73c2b5b42..000000000 --- a/loader/src/main/java/net/neoforged/fml/IModStateProvider.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -import java.util.List; - -/** - * Provides a list of mod loading states which the mod loader may transition between. - * - *

There may be multiple mod state providers in a single application, where all states from each provider is - * combined into a single list and ordered.

- */ -public interface IModStateProvider { - /** - * {@return the list of mod loading states known to this provider} - */ - List getAllStates(); -} diff --git a/loader/src/main/java/net/neoforged/fml/IModStateTransition.java b/loader/src/main/java/net/neoforged/fml/IModStateTransition.java deleted file mode 100644 index fe9583bbb..000000000 --- a/loader/src/main/java/net/neoforged/fml/IModStateTransition.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Stream; -import net.neoforged.bus.api.Event; -import net.neoforged.fml.event.IModBusEvent; -import net.neoforged.fml.loading.progress.ProgressMeter; - -@SuppressWarnings("unchecked") -public interface IModStateTransition { - static IModStateTransition buildNoopTransition() { - return new NoopTransition(); - } - - default CompletableFuture build(final String name, - final Executor syncExecutor, - final Executor parallelExecutor, - final ProgressMeter progressBar, - final Function> preSyncTask, - final Function> postSyncTask) { - List> futures = new ArrayList<>(); - - this.eventFunctionStream().get() - .map(f -> (EventGenerator) f) - .reduce((head, tail) -> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, futures, progressBar, head, ModLoadingStage::currentState, tail)) - .ifPresent(last -> addCompletableFutureTaskForModDispatch(syncExecutor, parallelExecutor, futures, progressBar, last, nextModLoadingStage(), null)); - - final CompletableFuture preSyncTaskCF = preSyncTask.apply(syncExecutor); - final CompletableFuture eventDispatchCF = ModList.gather(futures).thenCompose(ModList::completableFutureFromExceptionList); - final CompletableFuture postEventDispatchCF = preSyncTaskCF - .thenApplyAsync(n -> { - progressBar.label(progressBar.name() + ": dispatching " + name); - return null; - }, parallelExecutor) - .thenComposeAsync(n -> eventDispatchCF, parallelExecutor) - .thenApply(r -> { - postSyncTask.apply(syncExecutor); - return null; - }); - return this.finalActivityGenerator().apply(syncExecutor, postEventDispatchCF); - } - - default BiFunction nextModLoadingStage() { - return ModLoadingStage::nextState; - } - - private EventGenerator addCompletableFutureTaskForModDispatch(final Executor syncExecutor, - final Executor parallelExecutor, - final List> completableFutures, - final ProgressMeter progressBar, - final EventGenerator eventGenerator, - final BiFunction nextState, - final EventGenerator nextGenerator) { - final Executor selectedExecutor = threadSelector().apply(syncExecutor, parallelExecutor); - - var preDispatchHook = (BiFunction, CompletableFuture>) preDispatchHook(); - completableFutures.add(preDispatchHook.apply(selectedExecutor, eventGenerator)); - - completableFutures.add(ModList.get().futureVisitor(eventGenerator, progressBar, nextState).apply(threadSelector().apply(syncExecutor, parallelExecutor))); - - var postDispatchHook = (BiFunction, CompletableFuture>) postDispatchHook(); - completableFutures.add(postDispatchHook.apply(selectedExecutor, eventGenerator)); - - return nextGenerator; - } - - Supplier>> eventFunctionStream(); - - ThreadSelector threadSelector(); - - BiFunction, CompletableFuture> finalActivityGenerator(); - - BiFunction, CompletableFuture> preDispatchHook(); - - BiFunction, CompletableFuture> postDispatchHook(); - - interface EventGenerator extends Function { - static EventGenerator fromFunction(Function fn) { - return fn::apply; - } - } -} - -record NoopTransition() implements IModStateTransition { - @Override - public Supplier>> eventFunctionStream() { - return Stream::of; - } - - @Override - public ThreadSelector threadSelector() { - return ThreadSelector.SYNC; - } - - @Override - public BiFunction, CompletableFuture> finalActivityGenerator() { - return (e, t) -> t.thenApplyAsync(Function.identity(), e); - } - - @Override - public BiFunction, CompletableFuture> preDispatchHook() { - return (t, f) -> CompletableFuture.completedFuture(null); - } - - @Override - public BiFunction, CompletableFuture> postDispatchHook() { - return (t, f) -> CompletableFuture.completedFuture(null); - } -} diff --git a/loader/src/main/java/net/neoforged/fml/ModContainer.java b/loader/src/main/java/net/neoforged/fml/ModContainer.java index 3b0a8782b..5293e5b92 100644 --- a/loader/src/main/java/net/neoforged/fml/ModContainer.java +++ b/loader/src/main/java/net/neoforged/fml/ModContainer.java @@ -8,13 +8,9 @@ import static net.neoforged.fml.Logging.LOADING; import java.util.EnumMap; -import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BiFunction; import java.util.function.Supplier; import net.neoforged.bus.api.BusBuilder; import net.neoforged.bus.api.Event; @@ -23,7 +19,7 @@ import net.neoforged.fml.config.IConfigSpec; import net.neoforged.fml.config.ModConfig; import net.neoforged.fml.event.IModBusEvent; -import net.neoforged.fml.loading.progress.ProgressMeter; +import net.neoforged.fml.event.lifecycle.FMLConstructModEvent; import net.neoforged.neoforgespi.language.IModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -48,9 +44,7 @@ public abstract class ModContainer { protected final String modId; protected final String namespace; protected final IModInfo modInfo; - protected ModLoadingStage modLoadingStage; protected Supplier contextExtension; - protected final Map activityMap = new HashMap<>(); protected final Map, Supplier> extensionPoints = new IdentityHashMap<>(); protected final EnumMap configs = new EnumMap<>(ModConfig.Type.class); @@ -59,14 +53,12 @@ public ModContainer(IModInfo info) { // TODO: Currently not reading namespace from configuration.. this.namespace = this.modId; this.modInfo = info; - this.modLoadingStage = ModLoadingStage.CONSTRUCT; } /** * Errored container state, used for filtering. Does nothing. */ ModContainer() { - this.modLoadingStage = ModLoadingStage.ERROR; modId = "BROKEN"; namespace = "BROKEN"; modInfo = null; @@ -86,32 +78,6 @@ public final String getNamespace() { return namespace; } - /** - * @return The current loading stage for this mod - */ - public ModLoadingStage getCurrentState() { - return modLoadingStage; - } - - public static CompletableFuture buildTransitionHandler( - final ModContainer target, - final IModStateTransition.EventGenerator eventGenerator, - final ProgressMeter progressBar, - final BiFunction stateChangeHandler, - final Executor executor) { - return CompletableFuture - .runAsync(() -> { - ModLoadingContext.get().setActiveContainer(target); - target.activityMap.getOrDefault(target.modLoadingStage, () -> {}).run(); - target.acceptEvent(eventGenerator.apply(target)); - }, executor) - .whenComplete((mc, exception) -> { - target.modLoadingStage = stateChangeHandler.apply(target.modLoadingStage, exception); - progressBar.increment(); - ModLoadingContext.get().setActiveContainer(null); - }); - } - public IModInfo getModInfo() { return modInfo; } @@ -174,6 +140,12 @@ public void registerConfig(ModConfig.Type type, IConfigSpec configSpec, Strin addConfig(new ModConfig(type, configSpec, this, fileName)); } + /** + * Function invoked by FML to construct the mod, + * right before the dispatch of {@link FMLConstructModEvent}. + */ + protected void constructMod() {} + /** * Does this mod match the supplied mod? * @@ -203,7 +175,7 @@ public void registerConfig(ModConfig.Type type, IConfigSpec configSpec, Strin * * @param e Event to accept */ - protected final void acceptEvent(T e) { + public final void acceptEvent(T e) { IEventBus bus = getEventBus(); if (bus == null) return; @@ -213,7 +185,7 @@ protected final void acceptEvent(T e) { LOGGER.trace(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); + throw new ModLoadingException(modInfo, "fml.modloading.errorduringevent", t, e.getClass().getName()); } } @@ -222,7 +194,7 @@ protected final void acceptEvent(T e) { * * @param e Event to accept */ - protected final void acceptEvent(EventPriority phase, T e) { + public final void acceptEvent(EventPriority phase, T e) { IEventBus bus = getEventBus(); if (bus == null) return; @@ -232,7 +204,7 @@ protected final void acceptEvent(EventPriority LOGGER.trace(LOADING, "Fired event for phase {} for modid {} : {}", phase, 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); + throw new ModLoadingException(modInfo, "fml.modloading.errorduringevent", t, e.getClass().getName()); } } } diff --git a/loader/src/main/java/net/neoforged/fml/ModList.java b/loader/src/main/java/net/neoforged/fml/ModList.java index 603e99848..09c2b7ab5 100644 --- a/loader/src/main/java/net/neoforged/fml/ModList.java +++ b/loader/src/main/java/net/neoforged/fml/ModList.java @@ -18,21 +18,16 @@ 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 net.neoforged.bus.api.Event; -import net.neoforged.fml.event.IModBusEvent; import net.neoforged.fml.loading.moddiscovery.ModFile; import net.neoforged.fml.loading.moddiscovery.ModFileInfo; import net.neoforged.fml.loading.moddiscovery.ModInfo; -import net.neoforged.fml.loading.progress.ProgressMeter; import net.neoforged.neoforgespi.language.IModFileInfo; import net.neoforged.neoforgespi.language.IModInfo; import net.neoforged.neoforgespi.language.ModFileScanData; @@ -62,16 +57,11 @@ private ModList(final List modFiles, final List sortedList) { CrashReportCallables.registerCrashCallable("Mod List", this::crashReport); } - private String getModContainerState(String modId) { - return getModContainerById(modId).map(ModContainer::getCurrentState).map(Object::toString).orElse("NONE"); - } - private String fileToLine(IModFile mf) { - return String.format(Locale.ENGLISH, "%-50.50s|%-30.30s|%-30.30s|%-20.20s|%-10.10s|Manifest: %s", mf.getFileName(), + return String.format(Locale.ENGLISH, "%-50.50s|%-30.30s|%-30.30s|%-20.20s|Manifest: %s", mf.getFileName(), mf.getModInfos().get(0).getDisplayName(), mf.getModInfos().get(0).getModId(), mf.getModInfos().get(0).getVersion(), - getModContainerState(mf.getModInfos().get(0).getModId()), ((ModFileInfo) mf.getModFileInfo()).getCodeSigningFingerprint().orElse("NOSIGNATURE")); } @@ -104,17 +94,6 @@ public IModFileInfo getModFileById(String modid) { return this.fileById.get(modid); } - Function> futureVisitor( - final IModStateTransition.EventGenerator eventGenerator, - final ProgressMeter progressBar, - final BiFunction stateChange) { - return executor -> gather( - this.mods.stream() - .map(mod -> ModContainer.buildTransitionHandler(mod, eventGenerator, progressBar, stateChange, executor)) - .collect(Collectors.toList())) - .thenComposeAsync(ModList::completableFutureFromExceptionList, executor); - } - static CompletionStage completableFutureFromExceptionList(List> t) { if (t.stream().noneMatch(e -> e.getValue() != null)) { return CompletableFuture.completedFuture(null); @@ -209,10 +188,4 @@ public void forEachModInOrder(Consumer containerConsumer) { public Stream applyForEachModContainer(Function function) { return indexedMods.values().stream().map(function); } - - private static class UncaughtModLoadingException extends ModLoadingException { - public UncaughtModLoadingException(ModLoadingStage stage, Throwable originalException) { - super(null, stage, "fml.modloading.uncaughterror", originalException); - } - } } diff --git a/loader/src/main/java/net/neoforged/fml/ModLoader.java b/loader/src/main/java/net/neoforged/fml/ModLoader.java index 80f6981ce..1a7539252 100644 --- a/loader/src/main/java/net/neoforged/fml/ModLoader.java +++ b/loader/src/main/java/net/neoforged/fml/ModLoader.java @@ -13,16 +13,16 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; 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.ExecutionException; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; 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; @@ -30,6 +30,8 @@ import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.event.IModBusEvent; +import net.neoforged.fml.event.lifecycle.FMLConstructModEvent; +import net.neoforged.fml.event.lifecycle.ParallelDispatchEvent; import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.ImmediateWindowHandler; @@ -38,7 +40,6 @@ import net.neoforged.fml.loading.moddiscovery.InvalidModIdentifier; import net.neoforged.fml.loading.moddiscovery.ModFileInfo; import net.neoforged.fml.loading.moddiscovery.ModInfo; -import net.neoforged.fml.loading.progress.ProgressMeter; import net.neoforged.fml.loading.progress.StartupNotificationManager; import net.neoforged.neoforgespi.language.IModInfo; import net.neoforged.neoforgespi.language.IModLanguageProvider; @@ -91,11 +92,9 @@ public class ModLoader { private final List loadingExceptions; private final List loadingWarnings; - private final ModStateManager stateManager; private boolean loadingStateValid; @SuppressWarnings("OptionalUsedAsFieldOrParameterType") private final Optional> statusConsumer = StartupNotificationManager.modLoaderConsumer(); - private final Set completedStates = new HashSet<>(); private ModList modList; private ModLoader() { @@ -105,7 +104,7 @@ private ModLoader() { .flatMap(ModLoadingException::fromEarlyException) .collect(Collectors.toList()); this.loadingWarnings = FMLLoader.getLoadingModList().getBrokenFiles().stream() - .map(file -> new ModLoadingWarning(null, ModLoadingStage.VALIDATE, InvalidModIdentifier.identifyJarProblem(file.getFilePath()).orElse("fml.modloading.brokenfile"), file.getFileName())) + .map(file -> new ModLoadingWarning(null, InvalidModIdentifier.identifyJarProblem(file.getFilePath()).orElse("fml.modloading.brokenfile"), file.getFileName())) .collect(Collectors.toList()); FMLLoader.getLoadingModList().getWarnings().stream() @@ -115,9 +114,8 @@ private ModLoader() { FMLLoader.getLoadingModList().getModFiles().stream() .filter(ModFileInfo::missingLicense) .filter(modFileInfo -> modFileInfo.getMods().stream().noneMatch(thisModInfo -> this.loadingExceptions.stream().map(ModLoadingException::getModInfo).anyMatch(otherInfo -> otherInfo == thisModInfo))) //Ignore files where any other mod already encountered an error - .map(modFileInfo -> new ModLoadingException(null, ModLoadingStage.VALIDATE, "fml.modloading.missinglicense", null, modFileInfo.getFile())) + .map(modFileInfo -> new ModLoadingException(null, "fml.modloading.missinglicense", null, modFileInfo.getFile())) .forEach(this.loadingExceptions::add); - this.stateManager = new ModStateManager(); CrashReportCallables.registerCrashCallable("ModLauncher", FMLLoader::getLauncherInfo); CrashReportCallables.registerCrashCallable("ModLauncher launch target", FMLLoader::launcherHandlerName); CrashReportCallables.registerCrashCallable("ModLauncher services", this::computeModLauncherServiceList); @@ -147,7 +145,7 @@ public static ModLoader get() { * @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) { + public void gatherAndInitializeMods(final Executor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) { ForgeFeature.registerFeature("javaVersion", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.BOTH, System.getProperty("java.version"))); ForgeFeature.registerFeature("openGLVersion", ForgeFeature.VersionFeatureTest.forVersionString(IModInfo.DependencySide.CLIENT, ImmediateWindowHandler.getGLVersion())); loadingStateValid = true; @@ -173,7 +171,7 @@ public void gatherAndInitializeMods(final ModWorkManager.DrivenExecutor syncExec modList.setLoadedMods(Collections.emptyList()); loadingStateValid = false; throw new LoadingFailedException(failedBounds.stream() - .map(fb -> new ModLoadingException(fb.modInfo(), ModLoadingStage.CONSTRUCT, "fml.modloading.feature.missing", null, fb, ForgeFeature.featureValue(fb))) + .map(fb -> new ModLoadingException(fb.modInfo(), "fml.modloading.feature.missing", null, fb, ForgeFeature.featureValue(fb))) .toList()); } @@ -191,73 +189,101 @@ public void gatherAndInitializeMods(final ModWorkManager.DrivenExecutor syncExec } modList.setLoadedMods(modContainers); this.modList = modList; - var stateList = stateManager.getStates(ModLoadingPhase.GATHER); - var progress = StartupMessageManager.addProgressBar("Mod Gather", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum()); - stateList.forEach(mls -> dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress)); - progress.complete(); - } - public void loadMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) { - var stateList = stateManager.getStates(ModLoadingPhase.LOAD); - var progress = StartupMessageManager.addProgressBar("Mod Loading", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum()); - stateList.forEach(mls -> dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress)); - progress.complete(); + constructMods(syncExecutor, parallelExecutor, periodicTask); } - public void finishMods(final ModWorkManager.DrivenExecutor syncExecutor, final Executor parallelExecutor, final Runnable periodicTask) { - var stateList = stateManager.getStates(ModLoadingPhase.COMPLETE); - var progress = StartupMessageManager.addProgressBar("Mod Complete", stateList.stream().mapToInt(mls -> mls.size().applyAsInt(modList)).sum()); - stateList.forEach(mls -> dispatchAndHandleError(mls, syncExecutor, parallelExecutor, periodicTask, progress)); - statusConsumer.ifPresent(c -> c.accept(String.format("Mod loading complete - %d mods loaded", this.modList.size()))); - progress.complete(); + private void constructMods(Executor syncExecutor, Executor parallelExecutor, Runnable periodicTask) { + var workQueue = new DeferredWorkQueue("Mod Construction"); + dispatchParallelTask("Mod Construction", parallelExecutor, periodicTask, modContainer -> { + modContainer.constructMod(); + modContainer.acceptEvent(new FMLConstructModEvent(modContainer, workQueue)); + }); + waitForTask("Mod Construction: Deferred Queue", periodicTask, CompletableFuture.runAsync(workQueue::runTasks, syncExecutor)); } - private void dispatchAndHandleError(IModLoadingState state, ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor, final Runnable ticker, final ProgressMeter progressBar) { - if (!isLoadingStateValid()) { - LOGGER.error("Cowardly refusing to process mod state change request from {}", state); - return; - } - progressBar.label(progressBar.name() + " working"); - syncExecutor.drive(ticker); - state.inlineRunnable().ifPresent(a -> this.handleInlineTransition(a, state, syncExecutor, ticker)); - state.buildTransition(syncExecutor, parallelExecutor, progressBar).ifPresent(t -> waitForTransition(state, syncExecutor, ticker, t)); - completedStates.add(state); + /** + * Runs a single task on the {@code syncExecutor}, while ticking the loading screen. + */ + public void runInitTask(String name, Executor syncExecutor, Runnable periodicTask, Runnable initTask) { + waitForTask(name, periodicTask, CompletableFuture.runAsync(initTask, syncExecutor)); } - private void handleInlineTransition(final Consumer transition, final IModLoadingState state, final ModWorkManager.DrivenExecutor syncExecutor, final Runnable ticker) { - var pb = StartupMessageManager.addProgressBar("State transition " + state.name() + " running", 0); - syncExecutor.drive(ticker); - transition.accept(this.modList); - syncExecutor.drive(ticker); - pb.complete(); - syncExecutor.drive(ticker); + /** + * Dispatches a parallel event across all mod containers, with progress displayed on the loading screen. + */ + public void dispatchParallelEvent(String name, Executor syncExecutor, Executor parallelExecutor, Runnable periodicTask, BiFunction eventConstructor) { + var workQueue = new DeferredWorkQueue(name); + dispatchParallelTask(name, parallelExecutor, periodicTask, modContainer -> { + modContainer.acceptEvent(eventConstructor.apply(modContainer, workQueue)); + }); + runInitTask(name + ": Deferred Queue", syncExecutor, periodicTask, workQueue::runTasks); } - private void waitForTransition(final IModLoadingState state, final ModWorkManager.DrivenExecutor syncExecutor, final Runnable ticker, final CompletableFuture transition) { - while (!transition.isDone()) { - syncExecutor.drive(ticker); + /** + * Waits for a task to complete, displaying the name of the task on the loading screen. + */ + public void waitForTask(String name, Runnable periodicTask, CompletableFuture future) { + var progress = StartupMessageManager.addProgressBar(name, 0); + try { + waitForFuture(name, periodicTask, future); + } finally { + progress.complete(); } + } + + /** + * Dispatches a task across all mod containers in parallel, with progress displayed on the loading screen. + */ + public void dispatchParallelTask(String name, Executor parallelExecutor, Runnable periodicTask, Consumer task) { + var progress = StartupMessageManager.addProgressBar(name, modList.size()); try { - transition.join(); - } catch (CompletionException e) { - loadingStateValid = false; - 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); - statusConsumer.ifPresent(c -> c.accept("ERROR DURING MOD LOADING")); - throw e; - } + periodicTask.run(); + var futureList = modList.getSortedMods().stream() + .map(modContainer -> { + return CompletableFuture.runAsync(() -> { + ModLoadingContext.get().setActiveContainer(modContainer); + task.accept(modContainer); + }, parallelExecutor).whenComplete((result, exception) -> { + progress.increment(); + ModLoadingContext.get().setActiveContainer(null); + }); + }) + .toList(); + var singleFuture = ModList.gather(futureList) + .thenCompose(ModList::completableFutureFromExceptionList); + waitForFuture(name, periodicTask, singleFuture); + } finally { + progress.complete(); + } + } - 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.name(), modLoadingExceptions.size()); - statusConsumer.ifPresent(c -> c.accept("ERROR DURING MOD LOADING")); - throw new LoadingFailedException(modLoadingExceptions); + private void waitForFuture(String name, Runnable periodicTask, CompletableFuture future) { + while (true) { + periodicTask.run(); + try { + future.get(50, TimeUnit.MILLISECONDS); + return; + } catch (ExecutionException e) { + loadingStateValid = false; + 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); + statusConsumer.ifPresent(c -> c.accept("ERROR DURING MOD LOADING")); + throw new RuntimeException("Encountered non-modloading exception in future " + name, e); + } + + final List modLoadingExceptions = Arrays.stream(t.getSuppressed()) + .filter(ModLoadingException.class::isInstance) + .map(ModLoadingException.class::cast) + .collect(Collectors.toList()); + LOGGER.fatal(LOADING, "Failed to wait for future {}, {} errors found", name, modLoadingExceptions.size()); + statusConsumer.ifPresent(c -> c.accept("ERROR DURING MOD LOADING")); + throw new LoadingFailedException(modLoadingExceptions); + } catch (Exception ignored) {} } } @@ -288,10 +314,10 @@ private List buildMods(final IModFile modFile) { missingMods.removeAll(modIds); LOGGER.fatal(LOADING, "The following mods are missing, but have classes in the jar: {}", missingMods); - loadingExceptions.add(new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingclasses", null, modFile.getFilePath())); + loadingExceptions.add(new ModLoadingException(null, "fml.modloading.missingclasses", null, modFile.getFilePath())); } // remove errored mod containers - return containers.stream().filter(mc -> mc.modLoadingStage != ModLoadingStage.ERROR).collect(Collectors.toList()); + return containers.stream().filter(mc -> !(mc instanceof ErroredModContainer)).collect(Collectors.toList()); } private ModContainer buildModContainerFromTOML(final IModFile modFile, final Map modInfoMap, final Map.Entry idToProviderEntry) { @@ -300,7 +326,7 @@ private ModContainer buildModContainerFromTOML(final IModFile modFile, final Map final IModLanguageProvider.IModLanguageLoader languageLoader = idToProviderEntry.getValue(); IModInfo info = Optional.ofNullable(modInfoMap.get(modId)). // throw a missing metadata error if there is no matching modid in the modInfoMap from the mods.toml file - orElseThrow(() -> new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingmetadata", null, modId)); + orElseThrow(() -> new ModLoadingException(null, "fml.modloading.missingmetadata", null, modId)); return languageLoader.loadMod(info, modFile.getScanResult(), FMLLoader.getGameLayer()); } catch (ModLoadingException mle) { // exceptions are caught and added to the error list for later handling @@ -318,11 +344,6 @@ public static boolean isLoadingStateValid() { return get().loadingStateValid; } - public boolean hasCompletedState(final String stateName) { - IModLoadingState state = stateManager.findState(stateName); - return completedStates.contains(state); - } - public void runEventGenerator(Function generator) { if (!loadingStateValid) { LOGGER.error("Cowardly refusing to send event generator to a broken mod state"); diff --git a/loader/src/main/java/net/neoforged/fml/ModLoadingContext.java b/loader/src/main/java/net/neoforged/fml/ModLoadingContext.java index 2af1885c6..8c1e49ff2 100644 --- a/loader/src/main/java/net/neoforged/fml/ModLoadingContext.java +++ b/loader/src/main/java/net/neoforged/fml/ModLoadingContext.java @@ -15,7 +15,6 @@ public class ModLoadingContext { private static final Logger LOGGER = LogUtils.getLogger(); private static final ThreadLocal context = ThreadLocal.withInitial(ModLoadingContext::new); private Object languageExtension; - private ModLoadingStage stage; public static ModLoadingContext get() { return context.get(); diff --git a/loader/src/main/java/net/neoforged/fml/ModLoadingException.java b/loader/src/main/java/net/neoforged/fml/ModLoadingException.java index c6c9312df..2c7e688e3 100644 --- a/loader/src/main/java/net/neoforged/fml/ModLoadingException.java +++ b/loader/src/main/java/net/neoforged/fml/ModLoadingException.java @@ -21,10 +21,6 @@ public class ModLoadingException extends RuntimeException { * Mod Info for mod with issue */ private final IModInfo modInfo; - /** - * The stage where this error was encountered - */ - private final ModLoadingStage errorStage; /** * I18N message to use for display @@ -36,16 +32,15 @@ public class ModLoadingException extends RuntimeException { */ private final List context; - public ModLoadingException(final IModInfo modInfo, final ModLoadingStage errorStage, final String i18nMessage, final Throwable originalException, Object... context) { + public ModLoadingException(final IModInfo modInfo, final String i18nMessage, final Throwable originalException, Object... context) { super("Mod Loading Exception", originalException); this.modInfo = modInfo; - this.errorStage = errorStage; this.i18nMessage = i18nMessage; this.context = Arrays.asList(context); } static Stream fromEarlyException(final EarlyLoadingException e) { - return e.getAllData().stream().map(ed -> new ModLoadingException(ed.getModInfo(), ModLoadingStage.VALIDATE, ed.getI18message(), e.getCause(), ed.getArgs())); + return e.getAllData().stream().map(ed -> new ModLoadingException(ed.getModInfo(), ed.getI18message(), e.getCause(), ed.getArgs())); } public String getI18NMessage() { @@ -57,7 +52,8 @@ public Object[] getContext() { } public String formatToString() { - return Bindings.getMessageParser().get().parseMessage(i18nMessage, Streams.concat(Stream.of(modInfo, errorStage, getCause()), context.stream()).toArray()); + // TODO: cleanup null here - this requires moving all indices in the translations + return Bindings.getMessageParser().get().parseMessage(i18nMessage, Streams.concat(Stream.of(modInfo, null, getCause()), context.stream()).toArray()); } @Override diff --git a/loader/src/main/java/net/neoforged/fml/ModLoadingPhase.java b/loader/src/main/java/net/neoforged/fml/ModLoadingPhase.java deleted file mode 100644 index 227f8d60a..000000000 --- a/loader/src/main/java/net/neoforged/fml/ModLoadingPhase.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -/** - * Phases of mod loading, for grouping mod loading states. - */ -public enum ModLoadingPhase { - /** - * Special phase for exceptional situations and error handling, where mod loading cannot continue normally. - * - *

There is conventionally only one state with this phase.

- */ - ERROR, // Special state for error handling - /** - * Phase for discovering and gathering mods for loading. - */ - GATHER, - /** - * Phase for the loading of mods found from mod discovery. - * - * @see #GATHER - */ - LOAD, - /** - * Phase after mod loading has completed, for post-loading tasks. - * - * @see #LOAD - */ - COMPLETE, - /** - * Marker phase for the last state in the full mod loading process. - */ - DONE -} diff --git a/loader/src/main/java/net/neoforged/fml/ModLoadingStage.java b/loader/src/main/java/net/neoforged/fml/ModLoadingStage.java deleted file mode 100644 index 4ebd02599..000000000 --- a/loader/src/main/java/net/neoforged/fml/ModLoadingStage.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -/** - * Mod loading stage of mod containers during the mod loading process. These will have a corresponding {@link ModLoadingState} - * in the basic mod loading process provided by FML. - * - *

Each mod loading stage has a global {@link DeferredWorkQueue}, which is populated during the execution of the state - * associated with this stage and emptied at the end of the state's execution.

- */ -public enum ModLoadingStage { - /** - * Special stage for exceptional situations and error handling. - */ - ERROR, - /** - * Validation of the mod list. - * TODO: figure out where this is used and why this exists instead of CONSTRUCT being the first normal stage - */ - VALIDATE, - /** - * Default stage of mod containers after construction. - */ - CONSTRUCT, - /** - * Common (non-side-specific) setup and initialization. - */ - COMMON_SETUP, - /** - * Side-specific setup and initialization. - * - * @see net.neoforged.api.distmarker.Dist - */ - SIDED_SETUP, - /** - * Stage for enqueuing {@link InterModComms} messages for later processing. - */ - ENQUEUE_IMC, - /** - * Stage for processing received messages though {@link InterModComms}. - */ - PROCESS_IMC, - /** - * Marks the completion of mod loading for this container. - */ - COMPLETE, - /** - * Marks the completion of the full mod loading process. - */ - DONE; - - private final DeferredWorkQueue deferredWorkQueue; - - ModLoadingStage() { - deferredWorkQueue = new DeferredWorkQueue(this); - } - - /** - * {@return the next stage after this stage, or {@link #ERROR} if the exception is not {@code null}} - * - * @param exception the exception that occurred during this stage, may be {@code null} - */ - ModLoadingStage nextState(Throwable exception) { - return exception != null ? ERROR : values()[this.ordinal() + 1]; - } - - /** - * {@return this stage, or {@link #ERROR} if the exception is not {@code null}} - * - * @param exception the exception that occurred during this stage, may be {@code null} - */ - public ModLoadingStage currentState(Throwable exception) { - return exception != null ? ERROR : this; - } - - /** - * {@return the deferred work queue for this stage} - */ - public DeferredWorkQueue getDeferredWorkQueue() { - return deferredWorkQueue; - } -} diff --git a/loader/src/main/java/net/neoforged/fml/ModLoadingState.java b/loader/src/main/java/net/neoforged/fml/ModLoadingState.java deleted file mode 100644 index a477e2b1f..000000000 --- a/loader/src/main/java/net/neoforged/fml/ModLoadingState.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.ToIntFunction; -import net.neoforged.bus.api.Event; -import net.neoforged.fml.event.IModBusEvent; -import net.neoforged.fml.loading.progress.ProgressMeter; - -/** - * Implementation of the {@link IModLoadingState} interface. - * - * @param name the name of this state - * @param previous the name of the state immediately previous to this state - * @param message a function returning a human-friendly message for this state - * @param phase the mod loading phase this state belongs to - * @param inlineRunnable an optional runnable, which runs before starting the transition from this state to the next - * @param transition optional state transition information - */ -public record ModLoadingState(String name, String previous, - Function message, - ToIntFunction size, - ModLoadingPhase phase, - Optional> inlineRunnable, - Optional transition) implements IModLoadingState { - @Override - public Optional> buildTransition(final Executor syncExecutor, - final Executor parallelExecutor, - final ProgressMeter progressBar, - final Function> preSyncTask, - final Function> postSyncTask) { - return transition.map(t -> t.build(name, syncExecutor, parallelExecutor, progressBar, preSyncTask, postSyncTask)); - } - - /** - * {@return an empty mod loading state} The mod loading state has a blank human-readable message, no inline runnable, - * and no state transition information. - * - * @param name the name of the state - * @param previous the name of the immediately previous state to this state - * @param phase the mod loading phase the state belongs to - */ - public static ModLoadingState empty(final String name, final String previous, final ModLoadingPhase phase) { - return new ModLoadingState(name, previous, ml -> "", f -> 0, phase, Optional.empty(), Optional.empty()); - } - - /** - * Returns a mod loading state with state transition information and a default human-friendly message of - * {@code Processing transition [name]}. - * - * @param name the name of the state - * @param previous the name of the immediately previous state to this state - * @param phase the mod loading phase the state belongs to - * @param transition the state transition information - * @return a mod loading state with state transition information and a default message - */ - public static ModLoadingState withTransition(final String name, final String previous, final ModLoadingPhase phase, - final IModStateTransition transition) { - return new ModLoadingState(name, previous, ml -> "Processing transition " + name, ModList::size, phase, Optional.empty(), Optional.of(transition)); - } - - /** - * Returns a mod loading state with state transition information and a custom human-friendly message function. - * - * @param name the name of the state - * @param previous the name of the immediately previous state to this state - * @param message a function returning a human-friendly message for this state - * @param phase the mod loading phase the state belongs to - * @param transition the state transition information - * @return a mod loading state with state transition information and a custom message - */ - public static ModLoadingState withTransition(final String name, final String previous, - final Function message, final ModLoadingPhase phase, - final IModStateTransition transition) { - return new ModLoadingState(name, previous, message, ModList::size, phase, Optional.empty(), Optional.of(transition)); - } - - /** - * Returns a mod loading state with an inline runnable and a default human-friendly message of {@code Processing - * work [name]}. - * - * @param name the name of the state - * @param previous the name of the immediately previous state to this state - * @param phase the mod loading phase the state belongs to - * @param inline an optional runnable, which runs before starting the transition from this state to the next - * @return a mod loading state with an inline runnable and default message - */ - public static ModLoadingState withInline(final String name, final String previous, final ModLoadingPhase phase, - final Consumer inline) { - return new ModLoadingState(name, previous, ml -> "Processing work " + name, ml -> 0, phase, Optional.of(inline), Optional.empty()); - } -} diff --git a/loader/src/main/java/net/neoforged/fml/ModLoadingWarning.java b/loader/src/main/java/net/neoforged/fml/ModLoadingWarning.java index 5b2d7da89..5e006a2a2 100644 --- a/loader/src/main/java/net/neoforged/fml/ModLoadingWarning.java +++ b/loader/src/main/java/net/neoforged/fml/ModLoadingWarning.java @@ -17,10 +17,6 @@ public class ModLoadingWarning { * Mod Info for mod with warning */ private final IModInfo modInfo; - /** - * The stage where this warning was encountered - */ - private final ModLoadingStage warningStage; /** * I18N message to use for display @@ -32,18 +28,18 @@ public class ModLoadingWarning { */ private final List context; - public ModLoadingWarning(final IModInfo modInfo, final ModLoadingStage warningStage, final String i18nMessage, Object... context) { + public ModLoadingWarning(final IModInfo modInfo, final String i18nMessage, Object... context) { this.modInfo = modInfo; - this.warningStage = warningStage; this.i18nMessage = i18nMessage; this.context = Arrays.asList(context); } public String formatToString() { - return Bindings.getMessageParser().get().parseMessage(i18nMessage, Streams.concat(Stream.of(modInfo, warningStage), context.stream()).toArray()); + // TODO: cleanup null here - this requires moving all indices in the translations + return Bindings.getMessageParser().get().parseMessage(i18nMessage, Streams.concat(Stream.of(modInfo, null), context.stream()).toArray()); } static Stream fromEarlyException(final EarlyLoadingException e) { - return e.getAllData().stream().map(ed -> new ModLoadingWarning(ed.getModInfo(), ModLoadingStage.VALIDATE, ed.getI18message(), ed.getArgs())); + return e.getAllData().stream().map(ed -> new ModLoadingWarning(ed.getModInfo(), ed.getI18message(), ed.getArgs())); } } diff --git a/loader/src/main/java/net/neoforged/fml/ModStateManager.java b/loader/src/main/java/net/neoforged/fml/ModStateManager.java deleted file mode 100644 index de08e23f7..000000000 --- a/loader/src/main/java/net/neoforged/fml/ModStateManager.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -import com.google.common.graph.GraphBuilder; -import cpw.mods.modlauncher.util.ServiceLoaderUtils; -import java.util.Collection; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.List; -import java.util.ServiceLoader; -import java.util.function.Function; -import java.util.stream.Collectors; -import net.neoforged.fml.loading.FMLLoader; -import net.neoforged.fml.loading.toposort.TopologicalSort; - -@SuppressWarnings("UnstableApiUsage") -public class ModStateManager { - static ModStateManager INSTANCE; - private final EnumMap> stateMap; - - public ModStateManager() { - INSTANCE = this; - final var sp = ServiceLoader.load(FMLLoader.getGameLayer(), IModStateProvider.class); - this.stateMap = ServiceLoaderUtils.streamWithErrorHandling(sp, sce -> {}) - .map(IModStateProvider::getAllStates) - .mapMulti(Iterable::forEach) - .collect(Collectors.groupingBy(IModLoadingState::phase, () -> new EnumMap<>(ModLoadingPhase.class), Collectors.toUnmodifiableList())); - } - - public List getStates(final ModLoadingPhase phase) { - var nodes = stateMap.get(phase); - var lookup = nodes.stream().collect(Collectors.toMap(IModLoadingState::name, Function.identity())); - - var graph = GraphBuilder.directed().allowsSelfLoops(false).build(); - var dummy = ModLoadingState.empty("", "", phase); - nodes.forEach(graph::addNode); - graph.addNode(dummy); - nodes.forEach(n -> graph.putEdge(lookup.getOrDefault(n.previous(), dummy), n)); - return TopologicalSort.topologicalSort(graph, Comparator.comparingInt(nodes::indexOf)).stream().filter(st -> st != dummy).toList(); - } - - public IModLoadingState findState(final String stateName) { - return stateMap.values() - .stream() - .flatMap(Collection::stream) - .filter(mls -> mls.name().equals(stateName)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("Unknown IModLoadingState: " + stateName)); - } -} diff --git a/loader/src/main/java/net/neoforged/fml/ModWorkManager.java b/loader/src/main/java/net/neoforged/fml/ModWorkManager.java index 93dfd6812..1428f767b 100644 --- a/loader/src/main/java/net/neoforged/fml/ModWorkManager.java +++ b/loader/src/main/java/net/neoforged/fml/ModWorkManager.java @@ -7,101 +7,31 @@ import static net.neoforged.fml.Logging.LOADING; -import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinWorkerThread; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.LockSupport; import net.neoforged.fml.loading.FMLConfig; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class ModWorkManager { private static final Logger LOGGER = LogManager.getLogger(); - private static final long PARK_TIME = TimeUnit.MILLISECONDS.toNanos(1); - public interface DrivenExecutor extends Executor { - boolean selfDriven(); + private static final Executor syncWorkExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "modloading-sync-worker")); - boolean driveOne(); - - default void drive(Runnable ticker) { - boolean ranOne = false; - if (!selfDriven()) { - ticker.run(); - while (true) { - if (!driveOne()) break; - ranOne = true; - } - } - if (!ranOne) { - // park for a bit so other threads can schedule - LockSupport.parkNanos(PARK_TIME); - } - } - } - - 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); + /** + * Executor that runs tasks on a single thread in the order they are submitted. + */ + public static Executor syncExecutor() { + return syncWorkExecutor; } private static ForkJoinPool parallelThreadPool; + /** + * Executor that runs tasks in parallel across multiple background threads. + */ public static Executor parallelExecutor() { if (parallelThreadPool == null) { final int loadingThreadCount = FMLConfig.getIntConfigValue(FMLConfig.ConfigValue.MAX_THREADS); diff --git a/loader/src/main/java/net/neoforged/fml/ThreadSelector.java b/loader/src/main/java/net/neoforged/fml/ThreadSelector.java deleted file mode 100644 index a37f1fb3b..000000000 --- a/loader/src/main/java/net/neoforged/fml/ThreadSelector.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml; - -import java.util.concurrent.Executor; -import java.util.function.BinaryOperator; - -public 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); - } -} diff --git a/loader/src/main/java/net/neoforged/fml/core/ModStateProvider.java b/loader/src/main/java/net/neoforged/fml/core/ModStateProvider.java deleted file mode 100644 index b55d292a0..000000000 --- a/loader/src/main/java/net/neoforged/fml/core/ModStateProvider.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml.core; - -import java.util.List; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.fml.IModLoadingState; -import net.neoforged.fml.IModStateProvider; -import net.neoforged.fml.InterModComms; -import net.neoforged.fml.ModLoadingPhase; -import net.neoforged.fml.ModLoadingStage; -import net.neoforged.fml.ModLoadingState; -import net.neoforged.fml.config.ConfigTracker; -import net.neoforged.fml.config.ModConfig; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.fml.event.lifecycle.FMLConstructModEvent; -import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent; -import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent; -import net.neoforged.fml.event.lifecycle.InterModEnqueueEvent; -import net.neoforged.fml.event.lifecycle.InterModProcessEvent; -import net.neoforged.fml.loading.FMLEnvironment; -import net.neoforged.fml.loading.FMLPaths; - -/** - * Provider for the core FML mod loading states. - */ -public class ModStateProvider implements IModStateProvider { - /** - * The special mod loading state for exceptional situations and error handling. - * - * @see ModLoadingPhase#ERROR - */ - final ModLoadingState ERROR = ModLoadingState.empty("ERROR", "", - ModLoadingPhase.ERROR); - - /** - * First {@linkplain ModLoadingPhase#GATHER gathering state}, for the validation of the mod list. - * TODO: figure out where this is used and why this exists instead of CONSTRUCT being the first state - */ - private final ModLoadingState VALIDATE = ModLoadingState.empty("VALIDATE", "", - ModLoadingPhase.GATHER); - - /** - * {@linkplain ModLoadingPhase#GATHER Gathering state} after {@linkplain #VALIDATE validation}, for the construction - * of mod containers and their backing mod instances. - * - * @see FMLConstructModEvent - * @see ModLoadingStage#CONSTRUCT - */ - final ModLoadingState CONSTRUCT = ModLoadingState.withTransition("CONSTRUCT", "VALIDATE", - ml -> "Constructing %d mods".formatted(ml.size()), - ModLoadingPhase.GATHER, - new ParallelTransition(ModLoadingStage.CONSTRUCT, FMLConstructModEvent.class)); - - /** - * First {@linkplain ModLoadingPhase#LOAD loading state}, for loading of the common and (if applicable) - * {@linkplain Dist#CLIENT client-side} mod configurations. - */ - private final ModLoadingState CONFIG_LOAD = ModLoadingState.withInline("CONFIG_LOAD", "", - ModLoadingPhase.LOAD, - ml -> { - if (FMLEnvironment.dist == Dist.CLIENT) { - ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.CLIENT, FMLPaths.CONFIGDIR.get()); - } - ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.COMMON, FMLPaths.CONFIGDIR.get()); - }); - - /** - * {@linkplain ModLoadingPhase#LOAD Loading state} after {@linkplain #CONFIG_LOAD configuration loading}, for - * common (non-side-specific) setup and initialization. - * - * @see FMLCommonSetupEvent - * @see ModLoadingStage#COMMON_SETUP - */ - private final ModLoadingState COMMON_SETUP = ModLoadingState.withTransition("COMMON_SETUP", "CONFIG_LOAD", - ModLoadingPhase.LOAD, - new ParallelTransition(ModLoadingStage.COMMON_SETUP, FMLCommonSetupEvent.class)); - - /** - * {@linkplain ModLoadingPhase#LOAD Loading state} after {@linkplain #COMMON_SETUP common setup}, for side-specific - * setup and initialization. - * - * @see FMLClientSetupEvent - * @see FMLDedicatedServerSetupEvent - * @see ModLoadingStage#SIDED_SETUP - */ - private final ModLoadingState SIDED_SETUP = ModLoadingState.withTransition("SIDED_SETUP", "COMMON_SETUP", - ModLoadingPhase.LOAD, - new ParallelTransition(ModLoadingStage.SIDED_SETUP, - FMLEnvironment.dist == Dist.CLIENT ? FMLClientSetupEvent.class : FMLDedicatedServerSetupEvent.class)); - - /** - * First {@linkplain ModLoadingPhase#COMPLETE completion state}, for enqueuing {@link InterModComms} - * messages. - * - * @see InterModEnqueueEvent - * @see ModLoadingStage#ENQUEUE_IMC - */ - private final ModLoadingState ENQUEUE_IMC = ModLoadingState.withTransition("ENQUEUE_IMC", "", - ModLoadingPhase.COMPLETE, - new ParallelTransition(ModLoadingStage.ENQUEUE_IMC, InterModEnqueueEvent.class)); - - /** - * {@linkplain ModLoadingPhase#COMPLETE Completion state} after {@linkplain #ENQUEUE_IMC}, for processing of messages - * received through {@link InterModComms}. - * - * @see InterModProcessEvent - * @see ModLoadingStage#PROCESS_IMC - */ - private final ModLoadingState PROCESS_IMC = ModLoadingState.withTransition("PROCESS_IMC", "ENQUEUE_IMC", - ModLoadingPhase.COMPLETE, - new ParallelTransition(ModLoadingStage.PROCESS_IMC, InterModProcessEvent.class)); - - /** - * {@linkplain ModLoadingPhase#COMPLETE Completion state} after {@linkplain #PROCESS_IMC}, marking the completion - * of the basic mod loading process; however, additional completion states may be present after this. - * - * @see FMLLoadCompleteEvent - * @see ModLoadingStage#COMPLETE - */ - private final ModLoadingState COMPLETE = ModLoadingState.withTransition("COMPLETE", "PROCESS_IMC", - ml -> "completing load of %d mods".formatted(ml.size()), - ModLoadingPhase.COMPLETE, - new ParallelTransition(ModLoadingStage.COMPLETE, FMLLoadCompleteEvent.class)); - - /** - * The marker state for the completion of the full mod loading process. - * - * @see ModLoadingStage#DONE - */ - private final ModLoadingState DONE = ModLoadingState.empty("DONE", "", - ModLoadingPhase.DONE); - - @Override - public List getAllStates() { - return List.of(ERROR, VALIDATE, CONSTRUCT, CONFIG_LOAD, COMMON_SETUP, SIDED_SETUP, ENQUEUE_IMC, PROCESS_IMC, COMPLETE, DONE); - } -} diff --git a/loader/src/main/java/net/neoforged/fml/core/ParallelTransition.java b/loader/src/main/java/net/neoforged/fml/core/ParallelTransition.java deleted file mode 100644 index 4db2c015f..000000000 --- a/loader/src/main/java/net/neoforged/fml/core/ParallelTransition.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.fml.core; - -import cpw.mods.modlauncher.api.LambdaExceptionUtils; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BiFunction; -import java.util.function.Supplier; -import java.util.stream.Stream; -import net.neoforged.fml.IModStateTransition; -import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; -import net.neoforged.fml.ThreadSelector; -import net.neoforged.fml.event.lifecycle.ParallelDispatchEvent; - -record ParallelTransition(ModLoadingStage stage, Class event) implements IModStateTransition { - @Override - public Supplier>> eventFunctionStream() { - return () -> Stream.of(IModStateTransition.EventGenerator.fromFunction(LambdaExceptionUtils.rethrowFunction((ModContainer mc) -> event.getConstructor(ModContainer.class, ModLoadingStage.class).newInstance(mc, stage)))); - } - - @Override - public ThreadSelector threadSelector() { - return ThreadSelector.PARALLEL; - } - - @Override - public BiFunction, CompletableFuture> finalActivityGenerator() { - return (e, prev) -> prev.thenApplyAsync(t -> { - stage.getDeferredWorkQueue().runTasks(); - return t; - }, e); - } - - @Override - public BiFunction, CompletableFuture> preDispatchHook() { - return (t, f) -> CompletableFuture.completedFuture(null); - } - - @Override - public BiFunction, CompletableFuture> postDispatchHook() { - return (t, f) -> CompletableFuture.completedFuture(null); - } -} diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLClientSetupEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLClientSetupEvent.java index d2a9ad376..27b9f2610 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLClientSetupEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLClientSetupEvent.java @@ -5,8 +5,8 @@ package net.neoforged.fml.event.lifecycle; +import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; /** * This is the second of four commonly called events during mod lifecycle startup. @@ -23,7 +23,7 @@ * This is a parallel dispatch event. */ public class FMLClientSetupEvent extends ParallelDispatchEvent { - public FMLClientSetupEvent(ModContainer container, ModLoadingStage stage) { - super(container, stage); + public FMLClientSetupEvent(ModContainer container, DeferredWorkQueue deferredWorkQueue) { + super(container, deferredWorkQueue); } } diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLCommonSetupEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLCommonSetupEvent.java index 056d0931f..08d7288ea 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLCommonSetupEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLCommonSetupEvent.java @@ -8,7 +8,6 @@ import java.util.function.Consumer; import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; /** * This is the first of four commonly called events during mod initialization. @@ -27,7 +26,7 @@ * completed dispatch */ public class FMLCommonSetupEvent extends ParallelDispatchEvent { - public FMLCommonSetupEvent(final ModContainer container, final ModLoadingStage stage) { - super(container, stage); + public FMLCommonSetupEvent(ModContainer container, DeferredWorkQueue deferredWorkQueue) { + super(container, deferredWorkQueue); } } diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLConstructModEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLConstructModEvent.java index 7fa335f7b..48b2326d4 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLConstructModEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLConstructModEvent.java @@ -5,11 +5,11 @@ package net.neoforged.fml.event.lifecycle; +import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; public class FMLConstructModEvent extends ParallelDispatchEvent { - public FMLConstructModEvent(final ModContainer container, final ModLoadingStage stage) { - super(container, stage); + public FMLConstructModEvent(ModContainer container, DeferredWorkQueue deferredWorkQueue) { + super(container, deferredWorkQueue); } } diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java index ff8543bf0..fb7bce72a 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLDedicatedServerSetupEvent.java @@ -5,8 +5,8 @@ package net.neoforged.fml.event.lifecycle; +import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; /** * This is the second of four commonly called events during mod core startup. @@ -27,7 +27,7 @@ * This is a parallel dispatch event. */ public class FMLDedicatedServerSetupEvent extends ParallelDispatchEvent { - public FMLDedicatedServerSetupEvent(ModContainer container, ModLoadingStage stage) { - super(container, stage); + public FMLDedicatedServerSetupEvent(ModContainer container, DeferredWorkQueue deferredWorkQueue) { + super(container, deferredWorkQueue); } } diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLLoadCompleteEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLLoadCompleteEvent.java index 92a20da19..7f818ec8c 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLLoadCompleteEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/FMLLoadCompleteEvent.java @@ -5,8 +5,8 @@ package net.neoforged.fml.event.lifecycle; +import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; /** * This is a mostly internal event fired to mod containers that indicates that loading is complete. Mods should not @@ -15,7 +15,7 @@ * @author cpw */ public class FMLLoadCompleteEvent extends ParallelDispatchEvent { - public FMLLoadCompleteEvent(final ModContainer container, final ModLoadingStage stage) { - super(container, stage); + public FMLLoadCompleteEvent(ModContainer container, DeferredWorkQueue deferredWorkQueue) { + super(container, deferredWorkQueue); } } diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModEnqueueEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModEnqueueEvent.java index 2c05fa8e2..78df3156f 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModEnqueueEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModEnqueueEvent.java @@ -5,9 +5,9 @@ package net.neoforged.fml.event.lifecycle; +import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.InterModComms; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; /** * This is the third of four commonly called events during mod core startup. @@ -21,7 +21,7 @@ * This is a parallel dispatch event. */ public class InterModEnqueueEvent extends ParallelDispatchEvent { - public InterModEnqueueEvent(final ModContainer container, final ModLoadingStage stage) { - super(container, stage); + public InterModEnqueueEvent(ModContainer container, DeferredWorkQueue deferredWorkQueue) { + super(container, deferredWorkQueue); } } diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModProcessEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModProcessEvent.java index 0f3bb6151..14c7932e8 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModProcessEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/InterModProcessEvent.java @@ -6,9 +6,9 @@ package net.neoforged.fml.event.lifecycle; import java.util.function.Predicate; +import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.InterModComms; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; /** * This is the fourth of four commonly called events during mod core startup. @@ -24,7 +24,7 @@ * @see #getIMCStream(Predicate) */ public class InterModProcessEvent extends ParallelDispatchEvent { - public InterModProcessEvent(final ModContainer container, final ModLoadingStage stage) { - super(container, stage); + public InterModProcessEvent(ModContainer container, DeferredWorkQueue deferredWorkQueue) { + super(container, deferredWorkQueue); } } diff --git a/loader/src/main/java/net/neoforged/fml/event/lifecycle/ParallelDispatchEvent.java b/loader/src/main/java/net/neoforged/fml/event/lifecycle/ParallelDispatchEvent.java index 62b32e740..1b52d4991 100644 --- a/loader/src/main/java/net/neoforged/fml/event/lifecycle/ParallelDispatchEvent.java +++ b/loader/src/main/java/net/neoforged/fml/event/lifecycle/ParallelDispatchEvent.java @@ -5,30 +5,24 @@ package net.neoforged.fml.event.lifecycle; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import net.neoforged.fml.DeferredWorkQueue; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.ModLoadingStage; public abstract class ParallelDispatchEvent extends ModLifecycleEvent { - private final ModLoadingStage modLoadingStage; + private final DeferredWorkQueue workQueue; - public ParallelDispatchEvent(final ModContainer container, final ModLoadingStage stage) { + public ParallelDispatchEvent(ModContainer container, DeferredWorkQueue workQueue) { super(container); - this.modLoadingStage = stage; - } - - private Optional getQueue() { - return DeferredWorkQueue.lookup(Optional.of(modLoadingStage)); + this.workQueue = workQueue; } public CompletableFuture enqueueWork(Runnable work) { - return getQueue().map(q -> q.enqueueWork(getContainer(), work)).orElseThrow(() -> new RuntimeException("No work queue found!")); + return workQueue.enqueueWork(getContainer(), work); } public CompletableFuture enqueueWork(Supplier work) { - return getQueue().map(q -> q.enqueueWork(getContainer(), work)).orElseThrow(() -> new RuntimeException("No work queue found!")); + return workQueue.enqueueWork(getContainer(), work); } } diff --git a/loader/src/main/java/net/neoforged/fml/javafmlmod/FMLModContainer.java b/loader/src/main/java/net/neoforged/fml/javafmlmod/FMLModContainer.java index 3e594b762..3b9900b44 100644 --- a/loader/src/main/java/net/neoforged/fml/javafmlmod/FMLModContainer.java +++ b/loader/src/main/java/net/neoforged/fml/javafmlmod/FMLModContainer.java @@ -18,7 +18,6 @@ import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; import net.neoforged.fml.ModLoadingException; -import net.neoforged.fml.ModLoadingStage; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.neoforgespi.language.IModInfo; @@ -40,7 +39,6 @@ public FMLModContainer(IModInfo info, String className, ModFileScanData modFileS super(info); LOGGER.debug(LOADING, "Creating FMLModContainer instance for {}", className); this.scanResults = modFileScanResults; - activityMap.put(ModLoadingStage.CONSTRUCT, this::constructMod); this.eventBus = BusBuilder.builder() .setExceptionHandler(this::onEventFailed) .markerType(IModBusEvent.class) @@ -54,7 +52,7 @@ public FMLModContainer(IModInfo info, String className, ModFileScanData modFileS LOGGER.trace(LOADING, "Loaded modclass {} with {}", modClass.getName(), modClass.getClassLoader()); } catch (Throwable e) { LOGGER.error(LOADING, "Failed to load class {}", className, e); - throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmodclass", e); + throw new ModLoadingException(info, "fml.modloading.failedtoloadmodclass", e); } } @@ -62,7 +60,8 @@ private void onEventFailed(IEventBus iEventBus, Event event, EventListener[] iEv LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable)); } - private void constructMod() { + @Override + protected void constructMod() { try { LOGGER.trace(LOADING, "Loading mod instance {} of type {}", getModId(), modClass.getName()); @@ -105,7 +104,7 @@ private void constructMod() { } catch (Throwable e) { if (e instanceof InvocationTargetException) e = e.getCause(); // exceptions thrown when a reflected method call throws are wrapped in an InvocationTargetException. However, this isn't useful for the end user who has to dig through the logs to find the actual cause. LOGGER.error(LOADING, "Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e); - throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass); + throw new ModLoadingException(modInfo, "fml.modloading.failedtoloadmod", e, modClass); } try { LOGGER.trace(LOADING, "Injecting Automatic event subscribers for {}", getModId()); @@ -113,7 +112,7 @@ private void constructMod() { LOGGER.trace(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, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass); + throw new ModLoadingException(modInfo, "fml.modloading.failedtoloadmod", e, modClass); } } diff --git a/loader/src/main/java/net/neoforged/fml/loading/progress/StartupNotificationManager.java b/loader/src/main/java/net/neoforged/fml/loading/progress/StartupNotificationManager.java index 1c13f6f42..a25c8aade 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/progress/StartupNotificationManager.java +++ b/loader/src/main/java/net/neoforged/fml/loading/progress/StartupNotificationManager.java @@ -31,7 +31,7 @@ public static List getCurrentProgress() { public static ProgressMeter prependProgressBar(final String barName, final int count) { var pm = new ProgressMeter(barName, count, 0, new Message(barName, Message.MessageType.ML)); synchronized (progressMeters) { - progressMeters.addLast(pm); + progressMeters.addFirst(pm); } return pm; } @@ -39,14 +39,14 @@ public static ProgressMeter prependProgressBar(final String barName, final int c public static ProgressMeter addProgressBar(final String barName, final int count) { var pm = new ProgressMeter(barName, count, 0, new Message(barName, Message.MessageType.ML)); synchronized (progressMeters) { - progressMeters.push(pm); + progressMeters.addLast(pm); } return pm; } public static void popBar(final ProgressMeter progressMeter) { synchronized (progressMeters) { - progressMeters.remove(progressMeter); + progressMeters.removeLastOccurrence(progressMeter); } }