diff --git a/src/main/java/com/google/common/eventbus/FMLThrowingEventBus.java b/src/main/java/com/google/common/eventbus/FMLThrowingEventBus.java deleted file mode 100644 index a2a7ef6283..0000000000 --- a/src/main/java/com/google/common/eventbus/FMLThrowingEventBus.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016. - * - * 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 com.google.common.eventbus; - -import com.google.common.base.Preconditions; - -/** - * Event bus that allows exceptions thrown by the exception handler to propagate. - * TODO remove this in 1.13 and stop using the guava event bus - */ -public class FMLThrowingEventBus extends EventBus -{ - private final SubscriberExceptionHandler exceptionHandler; - - public FMLThrowingEventBus(SubscriberExceptionHandler exceptionHandler) - { - this.exceptionHandler = Preconditions.checkNotNull(exceptionHandler); - } - - @Override - void handleSubscriberException(Throwable e, SubscriberExceptionContext context) - { - Preconditions.checkNotNull(e); - Preconditions.checkNotNull(context); - exceptionHandler.handleException(e, context); - } -} diff --git a/src/main/java/net/minecraftforge/fml/client/CustomModLoadingErrorDisplayException.java b/src/main/java/net/minecraftforge/fml/client/CustomModLoadingErrorDisplayException.java index fd8b2ec75e..98684c22a6 100644 --- a/src/main/java/net/minecraftforge/fml/client/CustomModLoadingErrorDisplayException.java +++ b/src/main/java/net/minecraftforge/fml/client/CustomModLoadingErrorDisplayException.java @@ -21,7 +21,6 @@ package net.minecraftforge.fml.client; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiErrorScreen; -import net.minecraft.client.gui.GuiScreen; import net.minecraftforge.fml.common.EnhancedRuntimeException; import net.minecraftforge.fml.common.IFMLHandledException; import net.minecraftforge.fml.relauncher.Side; @@ -38,7 +37,7 @@ import net.minecraftforge.fml.relauncher.SideOnly; * */ @SideOnly(Side.CLIENT) -public abstract class CustomModLoadingErrorDisplayException extends EnhancedRuntimeException implements IFMLHandledException, IDisplayableError +public abstract class CustomModLoadingErrorDisplayException extends EnhancedRuntimeException implements IFMLHandledException { public CustomModLoadingErrorDisplayException() { } @@ -73,10 +72,4 @@ public abstract class CustomModLoadingErrorDisplayException extends EnhancedRunt public abstract void drawScreen(GuiErrorScreen errorScreen, FontRenderer fontRenderer, int mouseRelX, int mouseRelY, float tickTime); @Override public void printStackTrace(EnhancedRuntimeException.WrappedPrintStream s){}; // Do Nothing unless the modder wants to. - - @Override - public final GuiScreen createGui() - { - return new GuiCustomModLoadingErrorScreen(this); - } } diff --git a/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java b/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java index 6f03eb5695..6dbedb119a 100644 --- a/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java +++ b/src/main/java/net/minecraftforge/fml/client/FMLClientHandler.java @@ -112,7 +112,6 @@ import net.minecraftforge.registries.GameData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.FormattedMessage; import org.lwjgl.LWJGLUtil; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; @@ -172,13 +171,19 @@ public class FMLClientHandler implements IFMLSidedHandler private DummyModContainer optifineContainer; - @Deprecated // TODO remove in 1.13. mods are referencing this to get around client-only dependencies in old Forge versions private MissingModsException modsMissing; + private ModSortingException modSorting; + private boolean loading = true; - @Nullable - private IDisplayableError errorToDisplay; + private WrongMinecraftVersionException wrongMC; + + private CustomModLoadingErrorDisplayException customError; + + private DuplicateModsFoundException dupesFound; + + private MultipleModsErrored multipleModsErrored; private boolean serverShouldBeKilledQuietly; @@ -204,6 +209,7 @@ public class FMLClientHandler implements IFMLSidedHandler * @param resourcePackList The resource pack list we will populate with mods * @param resourceManager The resource manager */ + @SuppressWarnings("unchecked") public void beginMinecraftLoading(Minecraft minecraft, List resourcePackList, IReloadableResourceManager resourceManager, MetadataSerializer metaSerializer) { detectOptifine(); @@ -224,11 +230,30 @@ public class FMLClientHandler implements IFMLSidedHandler { Loader.instance().loadMods(injectedModContainers); } - catch (WrongMinecraftVersionException | DuplicateModsFoundException | MissingModsException | ModSortingException | CustomModLoadingErrorDisplayException | MultipleModsErrored e) + catch (WrongMinecraftVersionException wrong) { - FMLLog.log.error("An exception was thrown, the game will display an error screen and halt.", e); - errorToDisplay = e; - MinecraftForge.EVENT_BUS.shutdown(); + wrongMC = wrong; + } + catch (DuplicateModsFoundException dupes) + { + dupesFound = dupes; + } + catch (MissingModsException missing) + { + modsMissing = missing; + } + catch (ModSortingException sorting) + { + modSorting = sorting; + } + catch (CustomModLoadingErrorDisplayException custom) + { + FMLLog.log.error("A custom exception was thrown by a mod, the game will now halt", custom); + customError = custom; + } + catch (MultipleModsErrored multiple) + { + multipleModsErrored = multiple; } catch (LoaderException le) { @@ -249,9 +274,8 @@ public class FMLClientHandler implements IFMLSidedHandler if (le.getCause() instanceof CustomModLoadingErrorDisplayException) { CustomModLoadingErrorDisplayException custom = (CustomModLoadingErrorDisplayException) le.getCause(); - FMLLog.log.error("A custom exception was thrown by a mod, the game will display an error screen and halt.", custom); - errorToDisplay = custom; - MinecraftForge.EVENT_BUS.shutdown(); + FMLLog.log.error("A custom exception was thrown by a mod, the game will now halt", custom); + customError = custom; } else { @@ -260,7 +284,6 @@ public class FMLClientHandler implements IFMLSidedHandler } } - @SuppressWarnings("unchecked") Map> sharedModList = (Map>) Launch.blackboard.get("modList"); if (sharedModList == null) { @@ -309,7 +332,7 @@ public class FMLClientHandler implements IFMLSidedHandler public boolean hasError() { - return errorToDisplay != null; + return modsMissing != null || wrongMC != null || customError != null || dupesFound != null || modSorting != null || multipleModsErrored != null; } /** @@ -333,9 +356,8 @@ public class FMLClientHandler implements IFMLSidedHandler if (le.getCause() instanceof CustomModLoadingErrorDisplayException) { CustomModLoadingErrorDisplayException custom = (CustomModLoadingErrorDisplayException) le.getCause(); - FMLLog.log.error("A custom exception was thrown by a mod, the game will display an error screen and halt.", custom); - errorToDisplay = custom; - MinecraftForge.EVENT_BUS.shutdown(); + FMLLog.log.error("A custom exception was thrown by a mod, the game will now halt", custom); + customError = custom; } else { @@ -347,7 +369,6 @@ public class FMLClientHandler implements IFMLSidedHandler // This call is being phased out for performance reasons in 1.12, // but we are keeping an option here in case something needs it for a little longer. // See https://github.com/MinecraftForge/MinecraftForge/pull/4032 - // TODO remove in 1.13 if (Boolean.parseBoolean(System.getProperty("fml.reloadResourcesOnStart", "false"))) { client.refreshResources(); @@ -380,7 +401,7 @@ public class FMLClientHandler implements IFMLSidedHandler } loading = false; client.gameSettings.loadOptions(); //Reload options to load any mod added keybindings. - if (!hasError()) + if (customError == null) Loader.instance().loadingComplete(); SplashProgress.finish(); } @@ -418,10 +439,29 @@ public class FMLClientHandler implements IFMLSidedHandler // re-sync TEXTURE_2D, splash screen disables it with a direct GL call GlStateManager.disableTexture2D(); GlStateManager.enableTexture2D(); - if (errorToDisplay != null) + if (wrongMC != null) { - GuiScreen errorScreen = errorToDisplay.createGui(); - showGuiScreen(errorScreen); + showGuiScreen(new GuiWrongMinecraft(wrongMC)); + } + else if (modsMissing != null) + { + showGuiScreen(new GuiModsMissing(modsMissing)); + } + else if (dupesFound != null) + { + showGuiScreen(new GuiDupesFound(dupesFound)); + } + else if (modSorting != null) + { + showGuiScreen(new GuiSortingProblem(modSorting)); + } + else if (customError != null) + { + showGuiScreen(new GuiCustomModLoadingErrorScreen(customError)); + } + else if (multipleModsErrored != null) + { + showGuiScreen(new GuiMultipleModsErrored(multipleModsErrored)); } else { @@ -576,10 +616,6 @@ public class FMLClientHandler implements IFMLSidedHandler return client.getIntegratedServer(); } - /** - * TODO remove in 1.13 - */ - @Deprecated public void displayMissingMods(Object modMissingPacket) { // showGuiScreen(new GuiModsMissingForServer(modMissingPacket)); @@ -643,8 +679,7 @@ public class FMLClientHandler implements IFMLSidedHandler } catch (Exception e) { - FormattedMessage message = new FormattedMessage("An unexpected exception occurred constructing the custom resource pack for {} ({})", container.getName(), container.getModId()); - throw new RuntimeException(message.getFormattedMessage(), e); + throw new RuntimeException("An unexpected exception occurred constructing the custom resource pack for " + container.getName(), e); } } } @@ -663,6 +698,14 @@ public class FMLClientHandler implements IFMLSidedHandler @Override public void serverStopped() { + // If the server crashes during startup, it might hang the client - reset the client so it can abend properly. + MinecraftServer server = getServer(); + + if (server != null && !server.serverIsInRunLoop()) + { +// ObfuscationReflectionHelper.setPrivateValue(MinecraftServer.class, server, true, "field_71296"+"_Q","serverIs"+"Running"); + } + GameData.revertToFrozen(); } diff --git a/src/main/java/net/minecraftforge/fml/client/IDisplayableError.java b/src/main/java/net/minecraftforge/fml/client/IDisplayableError.java deleted file mode 100644 index 7f16938f06..0000000000 --- a/src/main/java/net/minecraftforge/fml/client/IDisplayableError.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.minecraftforge.fml.client; - -import net.minecraft.client.gui.GuiScreen; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -public interface IDisplayableError -{ - @SideOnly(Side.CLIENT) - GuiScreen createGui(); -} diff --git a/src/main/java/net/minecraftforge/fml/common/DuplicateModsFoundException.java b/src/main/java/net/minecraftforge/fml/common/DuplicateModsFoundException.java index 860ecf976d..7b9ec5aede 100644 --- a/src/main/java/net/minecraftforge/fml/common/DuplicateModsFoundException.java +++ b/src/main/java/net/minecraftforge/fml/common/DuplicateModsFoundException.java @@ -23,14 +23,8 @@ import java.io.File; import java.util.Map.Entry; import com.google.common.collect.SetMultimap; -import net.minecraft.client.gui.GuiScreen; -import net.minecraftforge.fml.client.GuiDupesFound; -import net.minecraftforge.fml.client.IDisplayableError; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class DuplicateModsFoundException extends LoaderException implements IDisplayableError -{ +public class DuplicateModsFoundException extends LoaderException { private static final long serialVersionUID = 1L; public SetMultimap dupes; @@ -48,11 +42,4 @@ public class DuplicateModsFoundException extends LoaderException implements IDis } stream.println(""); } - - @Override - @SideOnly(Side.CLIENT) - public GuiScreen createGui() - { - return new GuiDupesFound(this); - } } diff --git a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java index 72f58ecc32..73eccdd2f8 100644 --- a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java @@ -36,7 +36,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import com.google.common.base.Throwables; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.Config; import net.minecraftforge.common.config.ConfigManager; @@ -74,7 +73,6 @@ import com.google.common.collect.Maps; import com.google.common.collect.SetMultimap; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import org.apache.logging.log4j.message.FormattedMessage; import javax.annotation.Nullable; @@ -382,7 +380,8 @@ public class FMLModContainer implements ModContainer } @Nullable - private Method gatherAnnotations(Class clazz) + @SuppressWarnings("unchecked") + private Method gatherAnnotations(Class clazz) throws Exception { Method factoryMethod = null; for (Method m : clazz.getDeclaredMethods()) @@ -394,9 +393,7 @@ public class FMLModContainer implements ModContainer if (m.getParameterTypes().length == 1 && FMLEvent.class.isAssignableFrom(m.getParameterTypes()[0])) { m.setAccessible(true); - @SuppressWarnings("unchecked") - Class parameterType = (Class) m.getParameterTypes()[0]; - eventMethods.put(parameterType, m); + eventMethods.put((Class)m.getParameterTypes()[0], m); } else { @@ -424,7 +421,7 @@ public class FMLModContainer implements ModContainer return factoryMethod; } - private void processFieldAnnotations(ASMDataTable asmDataTable) throws IllegalAccessException + private void processFieldAnnotations(ASMDataTable asmDataTable) throws Exception { SetMultimap annotations = asmDataTable.getAnnotationsFor(this); @@ -506,110 +503,86 @@ public class FMLModContainer implements ModContainer @Subscribe public void constructMod(FMLConstructionEvent event) { - BlamingTransformer.addClasses(getModId(), candidate.getClassList()); - ModClassLoader modClassLoader = event.getModClassLoader(); try { + BlamingTransformer.addClasses(getModId(), candidate.getClassList()); + ModClassLoader modClassLoader = event.getModClassLoader(); modClassLoader.addFile(source); - } - catch (MalformedURLException e) - { - FormattedMessage message = new FormattedMessage("{} Failed to add file to classloader: {}", getModId(), source); - throw new LoaderException(message.getFormattedMessage(), e); - } - modClassLoader.clearNegativeCacheFor(candidate.getClassList()); + modClassLoader.clearNegativeCacheFor(candidate.getClassList()); - //Only place I could think to add this... - MinecraftForge.preloadCrashClasses(event.getASMHarvestedData(), getModId(), candidate.getClassList()); + //Only place I could think to add this... + MinecraftForge.preloadCrashClasses(event.getASMHarvestedData(), getModId(), candidate.getClassList()); - Class clazz; - try - { - clazz = Class.forName(className, true, modClassLoader); - } - catch (ClassNotFoundException e) - { - FormattedMessage message = new FormattedMessage("{} Failed load class: {}", getModId(), className); - throw new LoaderException(message.getFormattedMessage(), e); - } + Class clazz = Class.forName(className, true, modClassLoader); - Certificate[] certificates = clazz.getProtectionDomain().getCodeSource().getCertificates(); - ImmutableList certList = CertificateHelper.getFingerprints(certificates); - sourceFingerprints = ImmutableSet.copyOf(certList); + Certificate[] certificates = clazz.getProtectionDomain().getCodeSource().getCertificates(); + ImmutableList certList = CertificateHelper.getFingerprints(certificates); + sourceFingerprints = ImmutableSet.copyOf(certList); - String expectedFingerprint = (String)descriptor.get("certificateFingerprint"); + String expectedFingerprint = (String)descriptor.get("certificateFingerprint"); - fingerprintNotPresent = true; + fingerprintNotPresent = true; - if (expectedFingerprint != null && !expectedFingerprint.isEmpty()) - { - if (!sourceFingerprints.contains(expectedFingerprint)) + if (expectedFingerprint != null && !expectedFingerprint.isEmpty()) { - Level warnLevel = source.isDirectory() ? Level.TRACE : Level.ERROR; - FMLLog.log.log(warnLevel, "The mod {} is expecting signature {} for source {}, however there is no signature matching that description", getModId(), expectedFingerprint, source.getName()); + if (!sourceFingerprints.contains(expectedFingerprint)) + { + Level warnLevel = Level.ERROR; + if (source.isDirectory()) + { + warnLevel = Level.TRACE; + } + FMLLog.log.log(warnLevel, "The mod {} is expecting signature {} for source {}, however there is no signature matching that description", getModId(), expectedFingerprint, source.getName()); + } + else + { + certificate = certificates[certList.indexOf(expectedFingerprint)]; + fingerprintNotPresent = false; + } + } + + @SuppressWarnings("unchecked") + List> props = (List>)descriptor.get("customProperties"); + if (props != null) + { + com.google.common.collect.ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Map p : props) + { + builder.put((String)p.get("k"), (String)p.get("v")); + } + customModProperties = builder.build(); } else { - certificate = certificates[certList.indexOf(expectedFingerprint)]; - fingerprintNotPresent = false; + customModProperties = EMPTY_PROPERTIES; } - } - @SuppressWarnings("unchecked") - List> props = (List>)descriptor.get("customProperties"); - if (props != null) - { - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (Map p : props) + Boolean hasDisableableFlag = (Boolean)descriptor.get("canBeDeactivated"); + boolean hasReverseDepends = !event.getReverseDependencies().get(getModId()).isEmpty(); + if (hasDisableableFlag != null && hasDisableableFlag) { - builder.put(p.get("k"), p.get("v")); + disableability = hasReverseDepends ? Disableable.DEPENDENCIES : Disableable.YES; } - customModProperties = builder.build(); - } - else - { - customModProperties = EMPTY_PROPERTIES; - } + else + { + disableability = hasReverseDepends ? Disableable.DEPENDENCIES : Disableable.RESTART; + } + Method factoryMethod = gatherAnnotations(clazz); + modInstance = getLanguageAdapter().getNewInstance(this, clazz, modClassLoader, factoryMethod); + NetworkRegistry.INSTANCE.register(this, clazz, (String)(descriptor.getOrDefault("acceptableRemoteVersions", null)), event.getASMHarvestedData()); + if (fingerprintNotPresent) + { + eventBus.post(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints), expectedFingerprint)); + } + ProxyInjector.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide(), getLanguageAdapter()); + AutomaticEventSubscriber.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide()); + ConfigManager.sync(this.getModId(), Config.Type.INSTANCE); - Boolean hasDisableableFlag = (Boolean)descriptor.get("canBeDeactivated"); - boolean hasReverseDepends = !event.getReverseDependencies().get(getModId()).isEmpty(); - if (hasDisableableFlag != null && hasDisableableFlag) - { - disableability = hasReverseDepends ? Disableable.DEPENDENCIES : Disableable.YES; - } - else - { - disableability = hasReverseDepends ? Disableable.DEPENDENCIES : Disableable.RESTART; - } - Method factoryMethod = gatherAnnotations(clazz); - ILanguageAdapter languageAdapter = getLanguageAdapter(); - try - { - modInstance = languageAdapter.getNewInstance(this, clazz, modClassLoader, factoryMethod); - } - catch (Exception e) - { - Throwables.throwIfUnchecked(e); - FormattedMessage message = new FormattedMessage("{} Failed to load new mod instance.", getModId()); - throw new LoaderException(message.getFormattedMessage(), e); - } - NetworkRegistry.INSTANCE.register(this, clazz, (String)(descriptor.getOrDefault("acceptableRemoteVersions", null)), event.getASMHarvestedData()); - if (fingerprintNotPresent) - { - eventBus.post(new FMLFingerprintViolationEvent(source.isDirectory(), source, ImmutableSet.copyOf(this.sourceFingerprints), expectedFingerprint)); - } - ProxyInjector.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide(), languageAdapter); - AutomaticEventSubscriber.inject(this, event.getASMHarvestedData(), FMLCommonHandler.instance().getSide()); - ConfigManager.sync(this.getModId(), Config.Type.INSTANCE); - - try - { processFieldAnnotations(event.getASMHarvestedData()); } - catch (IllegalAccessException e) + catch (Throwable e) { - FormattedMessage message = new FormattedMessage("{} Failed to process field annotations.", getModId()); - throw new LoaderException(message.getFormattedMessage(), e); + controller.errorOccurred(this, e); } } diff --git a/src/main/java/net/minecraftforge/fml/common/LoadController.java b/src/main/java/net/minecraftforge/fml/common/LoadController.java index 5e7e222aad..681631f062 100644 --- a/src/main/java/net/minecraftforge/fml/common/LoadController.java +++ b/src/main/java/net/minecraftforge/fml/common/LoadController.java @@ -20,14 +20,15 @@ package net.minecraftforge.fml.common; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.stream.Collectors; -import java.util.stream.Stream; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.TextTable; import net.minecraftforge.fml.common.LoaderState.ModState; import net.minecraftforge.fml.common.ProgressManager.ProgressBar; @@ -38,10 +39,9 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.event.FMLStateEvent; import net.minecraftforge.fml.common.versioning.ArtifactVersion; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.ThreadContext; -import org.apache.logging.log4j.message.FormattedMessage; -import com.google.common.base.Throwables; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; @@ -53,8 +53,9 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.eventbus.EventBus; -import com.google.common.eventbus.FMLThrowingEventBus; import com.google.common.eventbus.Subscribe; +import com.google.common.eventbus.SubscriberExceptionHandler; +import com.google.common.eventbus.SubscriberExceptionContext; import javax.annotation.Nullable; @@ -62,9 +63,11 @@ public class LoadController { private Loader loader; private EventBus masterChannel; - private ImmutableMap eventChannels; + private ImmutableMap eventChannels; private LoaderState state; private Multimap modStates = ArrayListMultimap.create(); + private Multimap errors = ArrayListMultimap.create(); + private Map modNames = Maps.newHashMap(); private List activeModList = Lists.newArrayList(); private ModContainer activeContainer; private BiMap modObjectList; @@ -73,13 +76,13 @@ public class LoadController public LoadController(Loader loader) { this.loader = loader; - this.masterChannel = new FMLThrowingEventBus((exception, context) -> { - Throwables.throwIfUnchecked(exception); - // should not happen, but add some extra context for checked exceptions - Method method = context.getSubscriberMethod(); - String parameterNames = Stream.of(method.getParameterTypes()).map(Class::getName).collect(Collectors.joining(", ")); - String message = "Exception thrown during LoadController." + method.getName() + '(' + parameterNames + ')'; - throw new LoaderExceptionModCrash(message, exception); + this.masterChannel = new EventBus(new SubscriberExceptionHandler() + { + @Override + public void handleException(Throwable exception, SubscriberExceptionContext context) + { + FMLLog.log.error("Could not dispatch event: {} to {}", context.getSubscriberMethod(), exception); + } }); this.masterChannel.register(this); @@ -94,12 +97,14 @@ public class LoadController String modId = mod.getModId(); EventBus bus = temporary.remove(modId); bus.post(new FMLModDisabledEvent()); - eventChannels = ImmutableMap.copyOf(temporary); - modStates.put(modId, ModState.DISABLED); - modObjectList.remove(mod); - activeModList.remove(mod); + if (errors.get(modId).isEmpty()) + { + eventChannels = ImmutableMap.copyOf(temporary); + modStates.put(modId, ModState.DISABLED); + modObjectList.remove(mod); + activeModList.remove(mod); + } } - @Subscribe public void buildModList(FMLLoadEvent event) { @@ -107,7 +112,15 @@ public class LoadController for (final ModContainer mod : loader.getModList()) { - EventBus bus = new FMLThrowingEventBus((exception, context) -> this.errorOccurred(mod, exception)); + //Create mod logger, and make the EventBus logger a child of it. + EventBus bus = new EventBus(new SubscriberExceptionHandler() + { + @Override + public void handleException(final Throwable exception, final SubscriberExceptionContext context) + { + LoadController.this.errorOccurred(mod, exception); + } + }); boolean isActive = mod.registerBus(bus, this); if (isActive) @@ -123,6 +136,7 @@ public class LoadController modStates.put(mod.getModId(), ModState.UNLOADED); modStates.put(mod.getModId(), ModState.DISABLED); } + modNames.put(mod.getModId(), mod.getName()); } eventChannels = eventBus.build(); @@ -145,13 +159,13 @@ public class LoadController } LoaderState oldState = state; - state = state.transition(false); + state = state.transition(!errors.isEmpty()); if (state != desiredState) { if (!forceState) { - FormattedMessage message = new FormattedMessage("A fatal error occurred during the state transition from {} to {}. State became {} instead. Loading cannot continue.", oldState, desiredState, state); - throw new LoaderException(message.getFormattedMessage()); + FMLLog.log.fatal("Fatal errors were detected during the transition from {} to {}. Loading cannot continue", oldState, desiredState); + throw throwStoredErrors(); } else { @@ -164,11 +178,60 @@ public class LoadController @Deprecated // TODO remove in 1.13 public void checkErrorsAfterAvailable() { + checkErrors(); } - @Deprecated // TODO remove in 1.13 public void checkErrors() { + if (errors.size() > 0) + { + FMLLog.log.fatal("Fatal errors were detected during {}. Loading cannot continue.", state); + MinecraftForge.EVENT_BUS.shutdown(); + state = state.transition(true); + throw throwStoredErrors(); + } + } + + private RuntimeException throwStoredErrors() + { + Entry toThrow = null; + StringBuilder sb = new StringBuilder(); + printModStates(sb); + FMLLog.log.fatal(sb.toString()); + if (errors.size() > 0) + { + FMLLog.log.fatal("The following problems were captured during this phase"); + for (Entry entry : errors.entries()) + { + String modId = entry.getKey(); + String modName = modNames.get(modId); + Throwable error = entry.getValue(); + FMLLog.log.error("Caught exception from {} ({})", modId, modName, error); + if (error instanceof IFMLHandledException) + { + toThrow = entry; + } + else if (toThrow == null) + { + toThrow = entry; + } + } + } + + if (toThrow == null) + { + FMLLog.log.fatal("The ForgeModLoader state engine has become corrupted. Probably, a state was missed by and invalid modification to a base class" + + "ForgeModLoader depends on. This is a critical error and not recoverable. Investigate any modifications to base classes outside of" + + "ForgeModLoader, especially Optifine, to see if there are fixes available."); + throw new RuntimeException("The ForgeModLoader state engine is invalid"); + } + else + { + String modId = toThrow.getKey(); + String modName = modNames.get(modId); + String errMsg = String.format("Caught exception from %s (%s)", modName, modId); + throw new LoaderExceptionModCrash(errMsg, toThrow.getValue()); + } } @Nullable @@ -181,7 +244,6 @@ public class LoadController { activeContainer = container; } - @Subscribe public void propogateStateMessage(FMLEvent stateEvent) { @@ -201,10 +263,10 @@ public class LoadController private void sendEventToModContainer(FMLEvent stateEvent, ModContainer mc) { String modId = mc.getModId(); - Collection requirements = mc.getRequirements().stream().map(ArtifactVersion::getLabel).collect(Collectors.toCollection(HashSet::new)); + Collection requirements = mc.getRequirements().stream().map(ArtifactVersion::getLabel).collect(Collectors.toCollection(HashSet::new)); for (ArtifactVersion av : mc.getDependencies()) { - if (av.getLabel() != null && requirements.contains(av.getLabel()) && modStates.containsEntry(av.getLabel(), ModState.ERRORED)) + if (av.getLabel()!= null && requirements.contains(av.getLabel()) && modStates.containsEntry(av.getLabel(),ModState.ERRORED)) { FMLLog.log.error("Skipping event {} and marking errored mod {} since required dependency {} has errored", stateEvent.getEventType(), modId, av.getLabel()); modStates.put(modId, ModState.ERRORED); @@ -221,7 +283,14 @@ public class LoadController activeContainer = null; if (stateEvent instanceof FMLStateEvent) { - modStates.put(modId, ((FMLStateEvent) stateEvent).getModState()); + if (!errors.containsKey(modId)) + { + modStates.put(modId, ((FMLStateEvent)stateEvent).getModState()); + } + else + { + modStates.put(modId, ModState.ERRORED); + } } } @@ -230,7 +299,7 @@ public class LoadController ImmutableBiMap.Builder builder = ImmutableBiMap.builder(); for (ModContainer mc : activeModList) { - if (!mc.isImmutable() && mc.getMod() != null) + if (!mc.isImmutable() && mc.getMod()!=null) { builder.put(mc, mc.getMod()); List packages = mc.getOwnedPackages(); @@ -239,10 +308,13 @@ public class LoadController packageOwners.put(pkg, mc); } } - if (mc.getMod() == null && !mc.isImmutable() && state != LoaderState.CONSTRUCTING) + if (mc.getMod()==null && !mc.isImmutable() && state!=LoaderState.CONSTRUCTING) { - FormattedMessage message = new FormattedMessage("There is a severe problem with {} ({}) - it appears not to have constructed correctly", mc.getName(), mc.getModId()); - this.errorOccurred(mc, new RuntimeException(message.getFormattedMessage())); + FMLLog.log.fatal("There is a severe problem with {} - it appears not to have constructed correctly", mc.getModId()); + if (state != LoaderState.CONSTRUCTING) + { + this.errorOccurred(mc, new RuntimeException()); + } } } return builder.build(); @@ -250,19 +322,14 @@ public class LoadController public void errorOccurred(ModContainer modContainer, Throwable exception) { - String modId = modContainer.getModId(); - String modName = modContainer.getName(); - modStates.put(modId, ModState.ERRORED); if (exception instanceof InvocationTargetException) { - exception = exception.getCause(); + errors.put(modContainer.getModId(), exception.getCause()); } - if (exception instanceof LoaderException) // avoid wrapping loader exceptions multiple times + else { - throw (LoaderException) exception; + errors.put(modContainer.getModId(), exception); } - FormattedMessage message = new FormattedMessage("Caught exception from {} ({})", modName, modId); - throw new LoaderExceptionModCrash(message.getFormattedMessage(), exception); } public void printModStates(StringBuilder ret) @@ -307,16 +374,15 @@ public class LoadController public void distributeStateMessage(Class customEvent) { - Object eventInstance; try { - eventInstance = customEvent.newInstance(); + masterChannel.post(customEvent.newInstance()); } - catch (InstantiationException | IllegalAccessException e) + catch (Exception e) { - throw new LoaderException("Failed to create new event instance for " + customEvent.getName(), e); + FMLLog.log.error("An unexpected exception", e); + throw new LoaderException(e); } - masterChannel.post(eventInstance); } public BiMap getModObjectList() @@ -334,9 +400,8 @@ public class LoadController return this.state == state; } - boolean hasReachedState(LoaderState state) - { - return this.state.ordinal() >= state.ordinal() && this.state != LoaderState.ERRORED; + boolean hasReachedState(LoaderState state) { + return this.state.ordinal()>=state.ordinal() && this.state!=LoaderState.ERRORED; } void forceState(LoaderState newState) @@ -354,7 +419,7 @@ public class LoadController { continue; } - String pkg = c.getName().substring(0, idx); + String pkg = c.getName().substring(0,idx); if (packageOwners.containsKey(pkg)) { return packageOwners.get(pkg).get(0); @@ -363,7 +428,6 @@ public class LoadController return null; } - private FMLSecurityManager accessibleManager = new FMLSecurityManager(); class FMLSecurityManager extends SecurityManager diff --git a/src/main/java/net/minecraftforge/fml/common/Loader.java b/src/main/java/net/minecraftforge/fml/common/Loader.java index 8daa93ed5d..c5e7b6995f 100644 --- a/src/main/java/net/minecraftforge/fml/common/Loader.java +++ b/src/main/java/net/minecraftforge/fml/common/Loader.java @@ -547,10 +547,9 @@ public class Loader ObjectHolderRegistry.INSTANCE.findObjectHolders(new ASMDataTable()); modController.forceActiveContainer(containers[0]); } - /** * Called from the hook to start mod loading. We trigger the - * {@link #identifyMods(List)} and Constructing, Preinitalization, and Initalization phases here. Finally, + * {@link #identifyMods()} and Constructing, Preinitalization, and Initalization phases here. Finally, * the mod list is frozen completely and is consider immutable from then on. * @param injectedModContainers containers to inject */ @@ -625,6 +624,7 @@ public class Loader ItemStackHolderInjector.INSTANCE.findHolders(discoverer.getASMTable()); CapabilityManager.INSTANCE.injectCapabilities(discoverer.getASMTable()); modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir); + modController.checkErrors(); GameData.fireRegistryEvents(rl -> !rl.equals(GameData.RECIPES)); FMLCommonHandler.instance().fireSidedRegistryEvents(); ObjectHolderRegistry.INSTANCE.applyObjectHolders(); @@ -750,6 +750,7 @@ public class Loader progressBar.step("Finishing up"); modController.transition(LoaderState.AVAILABLE, false); modController.distributeStateMessage(LoaderState.AVAILABLE); + modController.checkErrors(); GameData.freezeData(); FMLLog.log.info("Forge Mod Loader has successfully loaded {} mod{}", mods.size(), mods.size() == 1 ? "" : "s"); progressBar.step("Completing Minecraft initialization"); diff --git a/src/main/java/net/minecraftforge/fml/common/MissingModsException.java b/src/main/java/net/minecraftforge/fml/common/MissingModsException.java index 2bf455b105..c70ee69031 100644 --- a/src/main/java/net/minecraftforge/fml/common/MissingModsException.java +++ b/src/main/java/net/minecraftforge/fml/common/MissingModsException.java @@ -28,15 +28,9 @@ import java.util.Set; import java.util.stream.Collectors; import com.google.common.base.Preconditions; - -import net.minecraft.client.gui.GuiScreen; -import net.minecraftforge.fml.client.GuiModsMissing; -import net.minecraftforge.fml.client.IDisplayableError; import net.minecraftforge.fml.common.versioning.ArtifactVersion; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class MissingModsException extends EnhancedRuntimeException implements IDisplayableError +public class MissingModsException extends EnhancedRuntimeException { private static final long serialVersionUID = 1L; private final String id; @@ -107,13 +101,6 @@ public class MissingModsException extends EnhancedRuntimeException implements ID stream.println(""); } - @Override - @SideOnly(Side.CLIENT) - public GuiScreen createGui() - { - return new GuiModsMissing(this); - } - public static class MissingModInfo { private final ArtifactVersion acceptedVersion; @@ -145,5 +132,4 @@ public class MissingModsException extends EnhancedRuntimeException implements ID return required; } } - } diff --git a/src/main/java/net/minecraftforge/fml/common/MultipleModsErrored.java b/src/main/java/net/minecraftforge/fml/common/MultipleModsErrored.java index 7af6836fbc..470a9d2b64 100644 --- a/src/main/java/net/minecraftforge/fml/common/MultipleModsErrored.java +++ b/src/main/java/net/minecraftforge/fml/common/MultipleModsErrored.java @@ -21,13 +21,7 @@ package net.minecraftforge.fml.common; import java.util.List; -import net.minecraft.client.gui.GuiScreen; -import net.minecraftforge.fml.client.GuiMultipleModsErrored; -import net.minecraftforge.fml.client.IDisplayableError; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -public class MultipleModsErrored extends EnhancedRuntimeException implements IDisplayableError +public class MultipleModsErrored extends EnhancedRuntimeException { public final List wrongMinecraftExceptions; public final List missingModsExceptions; @@ -37,13 +31,6 @@ public class MultipleModsErrored extends EnhancedRuntimeException implements IDi this.missingModsExceptions = missingModsExceptions; } - @Override - @SideOnly(Side.CLIENT) - public GuiScreen createGui() - { - return new GuiMultipleModsErrored(this); - } - @Override protected void printStackTrace(WrappedPrintStream stream) { diff --git a/src/main/java/net/minecraftforge/fml/common/WrongMinecraftVersionException.java b/src/main/java/net/minecraftforge/fml/common/WrongMinecraftVersionException.java index 7da699bd2c..9aac5381bb 100644 --- a/src/main/java/net/minecraftforge/fml/common/WrongMinecraftVersionException.java +++ b/src/main/java/net/minecraftforge/fml/common/WrongMinecraftVersionException.java @@ -19,13 +19,7 @@ package net.minecraftforge.fml.common; -import net.minecraft.client.gui.GuiScreen; -import net.minecraftforge.fml.client.GuiWrongMinecraft; -import net.minecraftforge.fml.client.IDisplayableError; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -public class WrongMinecraftVersionException extends EnhancedRuntimeException implements IDisplayableError +public class WrongMinecraftVersionException extends EnhancedRuntimeException { private static final long serialVersionUID = 1L; public ModContainer mod; @@ -48,10 +42,4 @@ public class WrongMinecraftVersionException extends EnhancedRuntimeException imp stream.println(""); } - @Override - @SideOnly(Side.CLIENT) - public GuiScreen createGui() - { - return new GuiWrongMinecraft(this); - } } diff --git a/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java b/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java index 412ef0b9e3..e53b51445d 100644 --- a/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java +++ b/src/main/java/net/minecraftforge/fml/common/toposort/ModSortingException.java @@ -21,15 +21,10 @@ package net.minecraftforge.fml.common.toposort; import java.util.Set; -import net.minecraft.client.gui.GuiScreen; -import net.minecraftforge.fml.client.GuiSortingProblem; -import net.minecraftforge.fml.client.IDisplayableError; import net.minecraftforge.fml.common.EnhancedRuntimeException; import net.minecraftforge.fml.common.ModContainer; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class ModSortingException extends EnhancedRuntimeException implements IDisplayableError +public class ModSortingException extends EnhancedRuntimeException { private static final long serialVersionUID = 1L; @@ -81,11 +76,4 @@ public class ModSortingException extends EnhancedRuntimeException implements IDi } } - - @Override - @SideOnly(Side.CLIENT) - public GuiScreen createGui() - { - return new GuiSortingProblem(this); - } }