diff --git a/loader/src/main/java/net/neoforged/fml/ModLoader.java b/loader/src/main/java/net/neoforged/fml/ModLoader.java
index 1a7539252..fa3cf3f78 100644
--- a/loader/src/main/java/net/neoforged/fml/ModLoader.java
+++ b/loader/src/main/java/net/neoforged/fml/ModLoader.java
@@ -50,83 +50,54 @@
import org.jetbrains.annotations.Nullable;
/**
- * Loads mods.
+ * Contains the logic to load mods, i.e. turn the {@link LoadingModList} into the {@link ModList},
+ * as well as initialization tasks for mods and methods to dispatch mod bus events.
*
- * Dispatch cycle is seen in {@code #loadMods()} and {@code #finishMods()}
+ *
For the mod initialization flow, see {@code CommonModLoader} in NeoForge.
*
- * Overall sequence for loadMods is:
- *
- * - CONSTRUCT
- * - Constructs the mod instance. Mods can typically setup basic environment such as Event listeners
- * and Configuration specifications here.
- * - Automated dispatches
- * - Dispatches automated elements : {@code net.neoforged.fml.common.Mod.EventBusSubscriber},
- * {@code net.neoforged.event.RegistryEvent}, {@code net.neoforged.common.capabilities.CapabilityInject}
- * and others
- * - CONFIG_LOAD
- * - Dispatches ConfigLoadEvent to mods
- * - COMMON_SETUP
- * - Dispatches {@code net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent} to mods
- * - SIDED_SETUP
- * - Dispatches {@code net.neoforged.fml.event.lifecycle.FMLClientSetupEvent} or
- * {@code net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent} to mods
- *
- *
- * Overall sequence for finishMods is:
- *
- * - ENQUEUE_IMC
- * - Dispatches {@code net.neoforged.fml.event.lifecycle.InterModEnqueueEvent} to mods,
- * for enqueuing {@link InterModComms} messages for other mods to receive subsequently
- * - PROCESS_IMC
- * - Dispatches {@code net.neoforged.fml.event.lifecycle.InterModProcessEvent} to mods,
- * for processing {@link InterModComms} messages received from other mods prior to this event
- * - COMPLETE
- * - Dispatches {@code net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent} to mods,
- * and completes the mod loading sequence.
- *
+ * For mod bus event dispatch, see {@link #postEvent(Event)} and related methods.
*/
-public class ModLoader {
+public final class ModLoader {
+ private ModLoader() {}
+
private static final Logger LOGGER = LogManager.getLogger();
- private static ModLoader INSTANCE;
- private final LoadingModList loadingModList;
-
- private final List loadingExceptions;
- private final List loadingWarnings;
- private boolean loadingStateValid;
- @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
- private final Optional> statusConsumer = StartupNotificationManager.modLoaderConsumer();
- private ModList modList;
-
- private ModLoader() {
- INSTANCE = this;
- this.loadingModList = FMLLoader.getLoadingModList();
- this.loadingExceptions = FMLLoader.getLoadingModList().getErrors().stream()
+
+ private static final List loadingExceptions = new ArrayList<>();
+ private static final List loadingWarnings = new ArrayList<>();
+ private static boolean loadingStateValid;
+ private static ModList modList;
+
+ static {
+ CrashReportCallables.registerCrashCallable("ModLauncher", FMLLoader::getLauncherInfo);
+ CrashReportCallables.registerCrashCallable("ModLauncher launch target", FMLLoader::launcherHandlerName);
+ CrashReportCallables.registerCrashCallable("ModLauncher services", ModLoader::computeModLauncherServiceList);
+ CrashReportCallables.registerCrashCallable("FML Language Providers", ModLoader::computeLanguageList);
+ }
+
+ private static void collectLoadingErrors(LoadingModList loadingModList) {
+ loadingModList.getErrors().stream()
.flatMap(ModLoadingException::fromEarlyException)
- .collect(Collectors.toList());
- this.loadingWarnings = FMLLoader.getLoadingModList().getBrokenFiles().stream()
+ .forEach(loadingExceptions::add);
+ loadingModList.getBrokenFiles().stream()
.map(file -> new ModLoadingWarning(null, InvalidModIdentifier.identifyJarProblem(file.getFilePath()).orElse("fml.modloading.brokenfile"), file.getFileName()))
- .collect(Collectors.toList());
+ .forEach(loadingWarnings::add);
- FMLLoader.getLoadingModList().getWarnings().stream()
+ loadingModList.getWarnings().stream()
.flatMap(ModLoadingWarning::fromEarlyException)
- .forEach(this.loadingWarnings::add);
+ .forEach(loadingWarnings::add);
- FMLLoader.getLoadingModList().getModFiles().stream()
+ loadingModList.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
+ .filter(modFileInfo -> modFileInfo.getMods().stream().noneMatch(thisModInfo -> loadingExceptions.stream().map(ModLoadingException::getModInfo).anyMatch(otherInfo -> otherInfo == thisModInfo))) //Ignore files where any other mod already encountered an error
.map(modFileInfo -> new ModLoadingException(null, "fml.modloading.missinglicense", null, modFileInfo.getFile()))
- .forEach(this.loadingExceptions::add);
- CrashReportCallables.registerCrashCallable("ModLauncher", FMLLoader::getLauncherInfo);
- CrashReportCallables.registerCrashCallable("ModLauncher launch target", FMLLoader::launcherHandlerName);
- CrashReportCallables.registerCrashCallable("ModLauncher services", this::computeModLauncherServiceList);
- CrashReportCallables.registerCrashCallable("FML Language Providers", this::computeLanguageList);
+ .forEach(loadingExceptions::add);
}
- private String computeLanguageList() {
+ private static String computeLanguageList() {
return "\n" + FMLLoader.getLanguageLoadingProvider().applyForEach(lp -> lp.name() + "@" + lp.getClass().getPackage().getImplementationVersion()).collect(Collectors.joining("\n\t\t", "\t\t", ""));
}
- private String computeModLauncherServiceList() {
+ private static String computeModLauncherServiceList() {
final List