Skip to content

Commit

Permalink
Handle erroring during early mod construction phases, and actually re…
Browse files Browse the repository at this point in the history
…port that into the error UI

by doing armslength exception handling and propagation.

Signed-off-by: cpw <[email protected]>
  • Loading branch information
cpw committed Nov 8, 2020
1 parent 160f3f8 commit f78b943
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
30 changes: 25 additions & 5 deletions src/main/java/net/minecraftforge/fml/ModLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,10 @@ private List<ModContainer> buildMods(final ModFile modFile, final TransformingCl
final Map<String, IModInfo> modInfoMap = modFile.getModFileInfo().getMods().stream().collect(Collectors.toMap(IModInfo::getModId, Function.identity()));

LOGGER.debug(LOADING, "ModContainer is {}", ModContainer.class.getClassLoader());
final List<ModContainer> containers = modFile.getScanResult().getTargets().entrySet().stream().
map(e -> buildModContainerFromTOML(modFile, modClassLoader, modInfoMap, e))
final List<ModContainer> containers = modFile.getScanResult().getTargets()
.entrySet()
.stream()
.map(e -> buildModContainerFromTOML(modFile, modClassLoader, modInfoMap, e))
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (containers.size() != modInfoMap.size()) {
Expand All @@ -279,7 +281,8 @@ private List<ModContainer> buildMods(final ModFile modFile, final TransformingCl
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<String, IModInfo> modInfoMap, final Map.Entry<String, ? extends IModLanguageProvider.IModLanguageLoader> idToProviderEntry) {
Expand All @@ -291,9 +294,10 @@ private ModContainer buildModContainerFromTOML(final ModFile modFile, final Tran
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);
}
}

Expand Down Expand Up @@ -336,4 +340,20 @@ public void addWarning(ModLoadingWarning warning)
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,7 +43,6 @@

public class FMLJavaModLanguageProvider implements IModLanguageProvider
{

private static final Logger LOGGER = LogManager.getLogger();

private static class FMLModTarget implements IModLanguageProvider.IModLanguageLoader {
Expand Down Expand Up @@ -72,10 +75,24 @@ public <T> T loadMod(final IModInfo info, final ClassLoader modClassLoader, fina
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<RuntimeException> mle = (Class<RuntimeException>)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<ModLoadingStage> mls = (Class<ModLoadingStage>) 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<RuntimeException> mle = (Class<RuntimeException>)LamdbaExceptionUtils.uncheck(()->Class.forName("net.minecraftforge.fml.ModLoadingException", true, Thread.currentThread().getContextClassLoader()));
final Class<ModLoadingStage> mls = (Class<ModLoadingStage>) 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));
}
}
}
Expand Down

0 comments on commit f78b943

Please sign in to comment.