From f78b943bdfde120fd03b69b06d73ada6ade5dca4 Mon Sep 17 00:00:00 2001 From: cpw Date: Sat, 7 Nov 2020 22:30:19 -0500 Subject: [PATCH] Handle erroring during early mod construction phases, and actually report that into the error UI by doing armslength exception handling and propagation. Signed-off-by: cpw --- .../net/minecraftforge/fml/ModLoader.java | 30 +++++++++++++++---- .../FMLJavaModLanguageProvider.java | 23 ++++++++++++-- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/minecraftforge/fml/ModLoader.java b/src/main/java/net/minecraftforge/fml/ModLoader.java index b49c197ae..cdd724710 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -268,8 +268,10 @@ public class ModLoader final Map modInfoMap = modFile.getModFileInfo().getMods().stream().collect(Collectors.toMap(IModInfo::getModId, Function.identity())); LOGGER.debug(LOADING, "ModContainer is {}", ModContainer.class.getClassLoader()); - final List containers = modFile.getScanResult().getTargets().entrySet().stream(). - map(e -> buildModContainerFromTOML(modFile, modClassLoader, modInfoMap, e)) + final List containers = modFile.getScanResult().getTargets() + .entrySet() + .stream() + .map(e -> buildModContainerFromTOML(modFile, modClassLoader, modInfoMap, e)) .filter(Objects::nonNull) .collect(Collectors.toList()); if (containers.size() != modInfoMap.size()) { @@ -279,7 +281,8 @@ public class ModLoader modInfoMap.size(), modInfoMap.values().stream().map(IModInfo::getModId).sorted().collect(Collectors.toList())); loadingExceptions.add(new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingclasses", null, modFile.getFilePath())); } - return containers; + // remove errored mod containers + return containers.stream().filter(obj -> !(obj instanceof ErroredModContainer)).collect(Collectors.toList()); } private ModContainer buildModContainerFromTOML(final ModFile modFile, final TransformingClassLoader modClassLoader, final Map modInfoMap, final Map.Entry idToProviderEntry) { @@ -291,9 +294,10 @@ public class ModLoader orElseThrow(()->new ModLoadingException(null, ModLoadingStage.CONSTRUCT, "fml.modloading.missingmetadata", null, modId)); return languageLoader.loadMod(info, modClassLoader, modFile.getScanResult()); } catch (ModLoadingException mle) { - // exceptions are caught and added to the error list for later handling. Null is returned here. + // exceptions are caught and added to the error list for later handling loadingExceptions.add(mle); - return null; + // return an errored container instance here, because we tried and failed building a container. + return new ErroredModContainer(mle); } } @@ -336,4 +340,20 @@ public class ModLoader public static boolean isDataGenRunning () { return runningDataGen; } + + static class ErroredModContainer extends ModContainer { + public ErroredModContainer(final ModLoadingException mle) { + super(mle.getModInfo()); + } + + @Override + public boolean matches(final Object mod) { + return false; + } + + @Override + public Object getMod() { + return null; + } + } } diff --git a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java index df9c57a60..e22fd5f34 100644 --- a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java +++ b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java @@ -19,6 +19,10 @@ package net.minecraftforge.fml.javafmlmod; +import cpw.mods.modlauncher.api.LamdbaExceptionUtils; +import net.minecraftforge.fml.ModLoadingException; +import net.minecraftforge.fml.ModLoadingStage; +import net.minecraftforge.fml.common.Mod; import net.minecraftforge.forgespi.language.ILifecycleEvent; import net.minecraftforge.forgespi.language.IModLanguageProvider; import net.minecraftforge.forgespi.language.IModInfo; @@ -39,7 +43,6 @@ import static net.minecraftforge.fml.Logging.*; public class FMLJavaModLanguageProvider implements IModLanguageProvider { - private static final Logger LOGGER = LogManager.getLogger(); private static class FMLModTarget implements IModLanguageProvider.IModLanguageLoader { @@ -72,10 +75,24 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider final Constructor constructor = fmlContainer.getConstructor(IModInfo.class, String.class, ClassLoader.class, ModFileScanData.class); return (T)constructor.newInstance(info, className, modClassLoader, modFileScanResults); } - catch (NoSuchMethodException | ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e) + // ALL exception handling has to be done through the classloader, because we're loaded in the wrong context, so any classes we just blind load will be in the wrong + // class loading context. Funky but works. + catch (InvocationTargetException e) { + LOGGER.fatal(LOADING, "Failed to build mod", e); + final Class mle = (Class)LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingException", true, Thread.currentThread().getContextClassLoader())); + if (mle.isInstance(e.getTargetException())) { + throw mle.cast(e.getTargetException()); + } else { + final Class mls = (Class) LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingStage", true, Thread.currentThread().getContextClassLoader())); + throw LamdbaExceptionUtils.uncheck(()->LamdbaExceptionUtils.uncheck(()->mle.getConstructor(IModInfo.class, mls, String.class, Throwable.class)).newInstance(info, Enum.valueOf(mls, "CONSTRUCT"), "fml.modloading.failedtoloadmodclass", e)); + } + } + catch (NoSuchMethodException | ClassNotFoundException | InstantiationException | IllegalAccessException e) { LOGGER.fatal(LOADING,"Unable to load FMLModContainer, wut?", e); - throw new RuntimeException(e); + final Class mle = (Class)LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingException", true, Thread.currentThread().getContextClassLoader())); + final Class mls = (Class) LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingStage", true, Thread.currentThread().getContextClassLoader())); + throw LamdbaExceptionUtils.uncheck(()->LamdbaExceptionUtils.uncheck(()->mle.getConstructor(IModInfo.class, mls, String.class, Throwable.class)).newInstance(info, Enum.valueOf(mls, "CONSTRUCT"), "fml.modloading.failedtoloadmodclass", e)); } } }