Basic error display GUI.

This commit is contained in:
cpw 2018-09-28 21:07:46 -04:00
parent dec65732ce
commit bb9eca96a8
26 changed files with 451 additions and 83 deletions

View file

@ -122,7 +122,7 @@ project(':forge') {
api 'org.ow2.asm:asm:6.2'
api 'org.ow2.asm:asm-commons:6.2'
api 'org.ow2.asm:asm-tree:6.2'
api 'cpw.mods:modlauncher:0.1.0-rc.+'
api 'cpw.mods:modlauncher:0.1.0'
api 'net.minecraftforge:accesstransformers:0.10+:shadowed'
api 'net.minecraftforge:eventbus:0.1+:service'
api 'net.minecraftforge:forgespi:0.1+'

View file

@ -35,9 +35,7 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PushbackInputStream;
import java.io.Reader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -51,7 +49,7 @@ import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Floats;
import net.minecraftforge.fml.common.FMLPaths;
import net.minecraftforge.fml.loading.FMLPaths;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View file

@ -45,8 +45,7 @@ public class BrandingControl
brd.add("MCP " + ForgeVersion.mcpVersion);
brd.add("Forge " + ForgeVersion.getVersion());
int tModCount = ModList.get().size();
brd.add(MessageFormat.format(ForgeI18n.getPattern("fml.menu.loadingmods"), tModCount));
brd.add(ForgeI18n.parseMessage("fml.menu.loadingmods", tModCount));
brandings = brd.build();
brandingsNoMC = brandings.subList(1, brandings.size());
}
@ -68,11 +67,11 @@ public class BrandingControl
}
private static final List<String> defaultClientBranding = Stream.of("fml", "forge").collect(Collectors.toList());
public static String getClientBranding() {
return defaultClientBranding.stream().collect(Collectors.joining(","));
return String.join(",", defaultClientBranding);
}
public static final List<String> defaultServerBranding = Arrays.asList("fml", "forge");
public static String getServerBranding() {
return defaultServerBranding.stream().collect(Collectors.joining(","));
return String.join(",", defaultServerBranding);
}
}

View file

@ -19,16 +19,83 @@
package net.minecraftforge.fml;
import net.minecraftforge.fml.loading.StringUtils;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import org.apache.commons.lang3.text.ExtendedMessageFormat;
import org.apache.commons.lang3.text.FormatFactory;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
public class ForgeI18n {
private static Map<String,String> i18n;
private static Map<String,FormatFactory> customFactories;
static String getPattern(final String patternName) {
return i18n.get(patternName);
static {
customFactories = new HashMap<>();
customFactories.put("modinfo", (name, formatString, locale) -> new CustomReadOnlyFormat((stringBuffer, objectToParse) -> parseModInfo(formatString, stringBuffer, objectToParse)));
customFactories.put("lower", (name, formatString, locale) -> new CustomReadOnlyFormat((stringBuffer, objectToParse) -> stringBuffer.append(StringUtils.toLowerCase((String)objectToParse))));
customFactories.put("upper", (name, formatString, locale) -> new CustomReadOnlyFormat((stringBuffer, objectToParse) -> stringBuffer.append(StringUtils.toUpperCase((String)objectToParse))));
customFactories.put("exc", (name, formatString, locale) -> new CustomReadOnlyFormat((stringBuffer, objectToParse) -> parseException(formatString, stringBuffer, objectToParse)));
}
private static void parseException(final String formatString, final StringBuffer stringBuffer, final Object objectToParse) {
Throwable t = (Throwable) objectToParse;
if (Objects.equals(formatString, "msg")) {
stringBuffer.append(t.getMessage());
} else if (Objects.equals(formatString, "cls")) {
stringBuffer.append(t.getClass());
}
}
private static void parseModInfo(final String formatString, final StringBuffer stringBuffer, final Object modInfo) {
final ModInfo info = (ModInfo) modInfo;
if (Objects.equals(formatString, "id")) {
stringBuffer.append(info.getModId());
} else if (Objects.equals(formatString, "name")) {
stringBuffer.append(info.getDisplayName());
}
}
public static String getPattern(final String patternName) {
return i18n.getOrDefault(patternName, patternName);
}
public static void loadLanguageData(final Map<String, String> properties) {
i18n = properties;
}
public static String parseMessage(final String i18nMessage, Object... args) {
final String pattern = getPattern(i18nMessage);
return parseFormat(pattern, args);
}
public static String parseFormat(final String format, final Object... args) {
final ExtendedMessageFormat extendedMessageFormat = new ExtendedMessageFormat(format, customFactories);
return extendedMessageFormat.format(args);
}
public static class CustomReadOnlyFormat extends Format {
private final BiConsumer<StringBuffer, Object> formatter;
CustomReadOnlyFormat(final BiConsumer<StringBuffer, Object> formatter) {
this.formatter = formatter;
}
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
formatter.accept(toAppendTo, obj);
return toAppendTo;
}
@Override
public Object parseObject(final String source, final ParsePosition pos) {
throw new UnsupportedOperationException("Parsing is not supported");
}
}
}

View file

@ -22,6 +22,8 @@ package net.minecraftforge.fml;
import net.minecraftforge.fml.common.event.ModLifecycleEvent;
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
public enum LifecycleEventProvider
@ -33,8 +35,8 @@ public enum LifecycleEventProvider
POSTINIT(()->new LifecycleEvent(ModLoadingStage.POSTINIT)),
COMPLETE(()->new LifecycleEvent(ModLoadingStage.COMPLETE));
public void dispatch() {
ModList.get().dispatchLifeCycleEvent(this.event.get());
public void dispatch(Consumer<List<ModLoadingException>> errorHandler) {
ModList.get().dispatchLifeCycleEvent(this.event.get(), errorHandler);
}
private final Supplier<? extends LifecycleEvent> event;

View file

@ -0,0 +1,15 @@
package net.minecraftforge.fml;
import java.util.List;
public class LoadingFailedException extends RuntimeException {
private final List<ModLoadingException> loadingExceptions;
public LoadingFailedException(final List<ModLoadingException> loadingExceptions) {
this.loadingExceptions = loadingExceptions;
}
public List<ModLoadingException> getErrors() {
return this.loadingExceptions;
}
}

View file

@ -21,7 +21,7 @@ package net.minecraftforge.fml;
import net.minecraftforge.fml.language.IModInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@ -50,13 +50,11 @@ public abstract class ModContainer
protected ModLoadingStage modLoadingStage;
protected final Map<ModLoadingStage, Consumer<LifecycleEventProvider.LifecycleEvent>> triggerMap;
protected final Map<ExtensionPoint, Supplier<?>> extensionPoints = new IdentityHashMap<>();
protected final List<Throwable> modLoadingError;
public ModContainer(IModInfo info)
{
this.modId = info.getModId();
this.modInfo = info;
this.triggerMap = new HashMap<>();
this.modLoadingError = new ArrayList<>();
this.modLoadingStage = ModLoadingStage.CONSTRUCT;
}
@ -88,19 +86,18 @@ public abstract class ModContainer
* Transition the mod to this event if possible.
* @param event to transition to
*/
public final void transitionState(LifecycleEventProvider.LifecycleEvent event)
public final void transitionState(LifecycleEventProvider.LifecycleEvent event, Consumer<List<ModLoadingException>> errorHandler)
{
if (modLoadingStage == event.fromStage())
{
try
{
triggerMap.getOrDefault(modLoadingStage, e->{}).accept(event);
modLoadingStage = event.toStage();
}
catch (RuntimeException e)
catch (ModLoadingException e)
{
modLoadingError.add(e);
modLoadingStage = ModLoadingStage.ERROR;
errorHandler.accept(Collections.singletonList(e));
}
}
}

View file

@ -62,7 +62,7 @@ public class ModList
private ModList(final List<ModFile> modFiles, final List<ModInfo> sortedList)
{
this.modFiles = modFiles.stream().map(ModFile::getModFileInfo).map(ModFileInfo.class::cast).collect(Collectors.toList());
this.sortedList = Streams.concat(DefaultModInfos.getModInfos().stream(), sortedList.stream()).
this.sortedList = sortedList.stream().
map(ModInfo.class::cast).
collect(Collectors.toList());
this.fileById = this.modFiles.stream().map(ModFileInfo::getMods).flatMap(Collection::stream).
@ -91,23 +91,21 @@ public class ModList
return this.fileById.get(modid);
}
public void dispatchLifeCycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) {
public void dispatchLifeCycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer<List<ModLoadingException>> errorHandler) {
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.preLifecycleEvent(lifecycleEvent));
DeferredWorkQueue.deferredWorkQueue.clear();
try
{
modLoadingThreadPool.submit(()->this.mods.parallelStream().forEach(m->m.transitionState(lifecycleEvent))).get();
modLoadingThreadPool.submit(()->this.mods.parallelStream().forEach(m->m.transitionState(lifecycleEvent, errorHandler))).get();
}
catch (InterruptedException | ExecutionException e)
{
LOGGER.error(LOADING, "Encountered an exception during parallel processing", e);
}
LOGGER.debug(LOADING, "Dispatching synchronous work, {} jobs", DeferredWorkQueue.deferredWorkQueue.size());
DeferredWorkQueue.deferredWorkQueue.forEach(FutureTask::run);
LOGGER.debug(LOADING, "Synchronous work queue complete");
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.postLifecycleEvent(lifecycleEvent));
final List<ModContainer> erroredContainers = this.mods.stream().filter(m -> m.getCurrentState() == ModLoadingStage.ERROR).collect(Collectors.toList());
if (!erroredContainers.isEmpty()) {
throw new RuntimeException("Errored containers found!", erroredContainers.get(0).modLoadingError.get(0));
}
}
public void setLoadedMods(final List<ModContainer> modContainers)

View file

@ -25,6 +25,8 @@ import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.loading.DefaultModInfos;
import net.minecraftforge.fml.loading.EarlyLoadingException;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.LoadingModList;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
@ -34,11 +36,10 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -53,12 +54,15 @@ public class ModLoader
private final LoadingModList loadingModList;
private final ModLoadingClassLoader modClassLoader;
private final List<ModLoadingException> loadingExceptions;
private ModLoader()
{
INSTANCE = this;
this.launchClassLoader = FMLLoader.getLaunchClassLoader();
this.loadingModList = FMLLoader.getLoadingModList();
this.modClassLoader = new ModLoadingClassLoader(this.launchClassLoader);
this.loadingExceptions = FMLLoader.getLoadingModList().
getErrors().stream().map(ModLoadingException::fromEarlyException).collect(Collectors.toList());
Thread.currentThread().setContextClassLoader(this.modClassLoader);
}
@ -67,14 +71,17 @@ public class ModLoader
return INSTANCE == null ? INSTANCE = new ModLoader() : INSTANCE;
}
private static Callable<Boolean> fireClientEvents()
private static Runnable fireClientEvents()
{
return ()->MinecraftForge.EVENT_BUS.post(new ModelRegistryEvent());
}
public void loadMods() {
if (!this.loadingExceptions.isEmpty()) {
throw new LoadingFailedException(loadingExceptions);
}
final ModList modList = ModList.of(loadingModList.getModFiles().stream().map(ModFileInfo::getFile).collect(Collectors.toList()), loadingModList.getMods());
final ModContainer forgeModContainer;
ModContainer forgeModContainer;
try
{
forgeModContainer = (ModContainer)Class.forName("net.minecraftforge.common.ForgeModContainer", true, modClassLoader).
@ -83,21 +90,41 @@ public class ModLoader
catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InstantiationException | InvocationTargetException e)
{
LOGGER.error(CORE,"Unable to load the Forge Mod Container", e);
throw new RuntimeException(e);
loadingExceptions.add(new ModLoadingException(DefaultModInfos.forgeModInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadforge", e));
forgeModContainer = null;
}
final Stream<ModContainer> modContainerStream = loadingModList.getModFiles().stream().
map(ModFileInfo::getFile).
map(mf -> buildMods(mf, modClassLoader)).
flatMap(Collection::stream);
if (!loadingExceptions.isEmpty()) {
LOGGER.error(CORE, "Failed to initialize mod containers");
throw new LoadingFailedException(loadingExceptions);
}
modList.setLoadedMods(Streams.concat(Stream.of(forgeModContainer), modContainerStream).collect(Collectors.toList()));
LifecycleEventProvider.CONSTRUCT.dispatch();
dispatchAndHandleError(LifecycleEventProvider.CONSTRUCT);
WorldPersistenceHooks.addHook(new FMLWorldPersistenceHook());
WorldPersistenceHooks.addHook((WorldPersistenceHooks.WorldPersistenceHook)forgeModContainer.getMod());
GameData.fireCreateRegistryEvents();
CapabilityManager.INSTANCE.injectCapabilities(modList.getAllScanData());
LifecycleEventProvider.PREINIT.dispatch();
Boolean result = DistExecutor.callWhenOn(Dist.CLIENT, ModLoader::fireClientEvents);
LifecycleEventProvider.SIDEDINIT.dispatch();
dispatchAndHandleError(LifecycleEventProvider.PREINIT);
DistExecutor.runWhenOn(Dist.CLIENT, ModLoader::fireClientEvents);
dispatchAndHandleError(LifecycleEventProvider.SIDEDINIT);
}
private void dispatchAndHandleError(LifecycleEventProvider event) {
if (!loadingExceptions.isEmpty()) {
LOGGER.error("Skipping lifecycle event {}, {} errors found.", event, loadingExceptions.size());
} else {
event.dispatch(this::accumulateErrors);
}
if (!loadingExceptions.isEmpty()) {
LOGGER.error("Failed to complete lifecycle event {}, {} errors found", event, loadingExceptions.size());
throw new LoadingFailedException(loadingExceptions);
}
}
private void accumulateErrors(List<ModLoadingException> errors) {
loadingExceptions.addAll(errors);
}
private List<ModContainer> buildMods(final ModFile modFile, final ModLoadingClassLoader modClassLoader)
@ -105,16 +132,22 @@ public class ModLoader
final Map<String, IModInfo> modInfoMap = modFile.getModFileInfo().getMods().stream().collect(Collectors.toMap(IModInfo::getModId, Function.identity()));
return modFile.getScanResult().getTargets().entrySet().stream().
map(e->e.getValue().<ModContainer>loadMod(modInfoMap.get(e.getKey()), modClassLoader, modFile.getScanResult())).
collect(Collectors.toList());
map(e-> {
try {
return e.getValue().<ModContainer>loadMod(modInfoMap.get(e.getKey()), modClassLoader, modFile.getScanResult());
} catch (ModLoadingException mle) {
loadingExceptions.add(mle);
return null;
}
}).collect(Collectors.toList());
}
public void finishMods()
{
LifecycleEventProvider.INIT.dispatch();
LifecycleEventProvider.POSTINIT.dispatch();
LifecycleEventProvider.COMPLETE.dispatch();
dispatchAndHandleError(LifecycleEventProvider.INIT);
dispatchAndHandleError(LifecycleEventProvider.POSTINIT);
dispatchAndHandleError(LifecycleEventProvider.COMPLETE);
GameData.freezeData();
}

View file

@ -19,16 +19,60 @@
package net.minecraftforge.fml;
import java.util.ArrayList;
import com.google.common.collect.ObjectArrays;
import com.google.common.collect.Streams;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.loading.EarlyLoadingException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* Accumulates errors during loading for compact handling
* General purpose mod loading error message
*/
public class ModLoadingException extends RuntimeException
{
private List<ErrorEvent> errorEvents = new ArrayList<>();
public static class ErrorEvent {
/**
* 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
*/
private final String i18nMessage;
/**
* Context for message display
*/
private final List<Object> context;
public ModLoadingException(final IModInfo modInfo, final ModLoadingStage errorStage, final String i18nMessage, final Throwable originalException, Object... context) {
super(ForgeI18n.getPattern(i18nMessage), originalException);
this.modInfo = modInfo;
this.errorStage = errorStage;
this.i18nMessage = i18nMessage;
this.context = Arrays.asList(context);
}
static ModLoadingException fromEarlyException(final EarlyLoadingException e) {
return new ModLoadingException(null, ModLoadingStage.VALIDATE, e.getI18NMessage(), e, e.getContext().toArray());
}
public String getI18NMessage() {
return i18nMessage;
}
public Object[] getContext() {
return context.toArray();
}
public String formatToString() {
return ForgeI18n.parseMessage(i18nMessage, Streams.concat(Stream.of(modInfo, errorStage, getCause()), context.stream()).toArray());
}
}

View file

@ -31,6 +31,7 @@ import java.util.function.Supplier;
public enum ModLoadingStage
{
ERROR(null),
VALIDATE(null),
CONSTRUCT(null),
PREINIT(()->FMLPreInitializationEvent::new),
SIDEDINIT(SidedProvider.SIDEDINIT::get),

View file

@ -29,11 +29,13 @@ import net.minecraft.resources.ResourcePackList;
import net.minecraft.resources.data.IMetadataSectionSerializer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.LoadingFailedException;
import net.minecraftforge.fml.LogicalSidedProvider;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.SidedProvider;
import net.minecraftforge.fml.VersionChecker;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.client.gui.GuiNotification;
import net.minecraftforge.fml.client.gui.LoadingErrorScreen;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import java.io.IOException;
@ -44,6 +46,7 @@ public class ClientModLoader
{
private static boolean loading;
private static Minecraft mc;
private static LoadingFailedException error;
public static void begin(final Minecraft minecraft, final ResourcePackList<ResourcePackInfoClient> defaultResourcePacks, final IReloadableResourceManager mcResourceManager, DownloadingPackFinder metadataSerializer)
{
@ -51,13 +54,21 @@ public class ClientModLoader
ClientModLoader.mc = minecraft;
SidedProvider.setClient(()->minecraft);
LogicalSidedProvider.setClient(()->minecraft);
ModLoader.get().loadMods();
try {
ModLoader.get().loadMods();
} catch (LoadingFailedException e) {
error = e;
}
ResourcePackLoader.loadResourcePacks(defaultResourcePacks);
}
public static void end()
{
ModLoader.get().finishMods();
try {
ModLoader.get().finishMods();
} catch (LoadingFailedException e) {
if (error == null) error = e;
}
loading = false;
mc.gameSettings.loadOptions();
}
@ -71,11 +82,9 @@ public class ClientModLoader
{
GlStateManager.disableTexture2D();
GlStateManager.enableTexture2D();
}
public static boolean isErrored()
{
return false;
if (error != null) {
mc.displayGuiScreen(new LoadingErrorScreen(error));
}
}
public static boolean isLoading()

View file

@ -19,12 +19,9 @@
package net.minecraftforge.fml.client;
import net.minecraft.client.resources.ResourcePackInfoClient;
import net.minecraft.resources.AbstractResourcePack;
import net.minecraft.resources.FilePack;
import net.minecraft.resources.FolderPack;
import net.minecraft.resources.IPackFinder;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.resources.IResourcePack;
import net.minecraft.resources.ResourcePackInfo;
import net.minecraft.resources.ResourcePackList;
@ -33,7 +30,6 @@ import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

View file

@ -0,0 +1,19 @@
package net.minecraftforge.fml.client.gui;
import net.minecraft.client.gui.GuiButton;
import java.util.function.DoubleBinaryOperator;
public class GuiButtonClickConsumer extends GuiButton {
private final DoubleBinaryOperator onClickAction;
public GuiButtonClickConsumer(final int buttonId, final int x, final int y, final int widthIn, final int heightIn, final String buttonText, DoubleBinaryOperator onClick) {
super(buttonId, x, y, widthIn, heightIn, buttonText);
this.onClickAction = onClick;
}
@Override
public void onClick(final double mouseX, final double mouseY) {
onClickAction.applyAsDouble(mouseX, mouseY);
}
}

View file

@ -27,7 +27,7 @@ import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.resources.I18n;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.common.FMLPaths;
import net.minecraftforge.fml.loading.FMLPaths;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View file

@ -0,0 +1,132 @@
package net.minecraftforge.fml.client.gui;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiErrorScreen;
import net.minecraft.client.gui.GuiListExtended;
import net.minecraft.client.renderer.Tessellator;
import net.minecraftforge.fml.ForgeI18n;
import net.minecraftforge.fml.LoadingFailedException;
import net.minecraftforge.fml.ModLoadingException;
import net.minecraftforge.fml.VersionChecker;
import net.minecraftforge.fml.loading.FMLPaths;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import static net.minecraft.util.StringUtils.stripControlCodes;
public class LoadingErrorScreen extends GuiErrorScreen {
private static final Logger LOGGER = LogManager.getLogger();
private final Path modsDir;
private final Path logFile;
private final LoadingFailedException loadingFailedException;
private LoadingErrorList errorList;
public LoadingErrorScreen(LoadingFailedException loadingException)
{
super(null, null);
this.loadingFailedException = loadingException;
this.modsDir = FMLPaths.MODSDIR.get();
this.logFile = FMLPaths.GAMEDIR.get().resolve(Paths.get("logs","latest.log"));
}
private double openModsDir(double mouseX, double mouseY)
{
try
{
Desktop.getDesktop().open(modsDir.toFile());
}
catch (Exception e)
{
LOGGER.error("Problem opening mods folder", e);
}
return 0.0;
}
private double openLogFile(double mouseX, double mouseY)
{
try
{
Desktop.getDesktop().open(logFile.toFile());
}
catch (Exception e)
{
LOGGER.error("Problem opening log file {}", logFile, e);
}
return 0.0;
}
@Override
public void initGui()
{
super.initGui();
this.buttons.clear();
this.children.clear();
this.addButton(new GuiButtonClickConsumer(10, 50, this.height - 38, this.width / 2 - 55, 20,
ForgeI18n.parseMessage("fml.button.open.mods.folder"), this::openModsDir));
this.addButton(new GuiButtonClickConsumer(11, this.width / 2 + 5, this.height - 38, this.width / 2 - 55, 20,
ForgeI18n.parseMessage("fml.button.open.file", logFile.getFileName()), this::openLogFile));
this.errorList = new LoadingErrorList(this, this.loadingFailedException.getErrors());
}
@Override
public void render(int mouseX, int mouseY, float partialTicks)
{
this.drawDefaultBackground();
this.errorList.drawScreen(mouseX, mouseY, partialTicks);
drawMultiLineCenteredString(fontRenderer, ForgeI18n.parseMessage("fml.loadingerrorscreen.header", this.loadingFailedException.getErrors().size()), this.width / 2, 10);
this.buttons.forEach(button -> button.render(mouseX, mouseY, partialTicks));
}
private void drawMultiLineCenteredString(FontRenderer fr, String str, int x, int y) {
for (String s : fr.listFormattedStringToWidth(str, this.width)) {
fr.drawStringWithShadow(s, (float) (x - fr.getStringWidth(s) / 2.0), y, 0xFFFFFF);
y+=fr.FONT_HEIGHT;
}
}
public static class LoadingErrorList extends GuiListExtended<LoadingErrorList.ErrorEntry> {
LoadingErrorList(final LoadingErrorScreen parent, final List<ModLoadingException> errors) {
super(parent.mc,parent.width,parent.height,35,parent.height - 50, 2 * parent.mc.fontRenderer.FONT_HEIGHT + 8);
errors.forEach(e->addEntry(new ErrorEntry(e)));
}
@Override
protected int getScrollBarX()
{
return this.right - 6;
}
@Override
public int getListWidth()
{
return this.width;
}
public class ErrorEntry extends GuiListExtended.IGuiListEntry<ErrorEntry> {
private final ModLoadingException error;
ErrorEntry(final ModLoadingException e) {
this.error = e;
}
@Override
public void drawEntry(final int entryWidth, final int entryHeight, final int mouseX, final int mouseY, final boolean p_194999_5_, final float partialTicks) {
int top = this.getY();
int left = this.getX();
FontRenderer font = Minecraft.getInstance().fontRenderer;
final List<String> strings = font.listFormattedStringToWidth(error.formatToString(), LoadingErrorList.this.width);
float f = (float)top + 2;
for (int i = 0; i < Math.min(strings.size(), 2); i++) {
font.drawString(strings.get(i), left + 5, f, 0xFFFFFFFF);
f += font.FONT_HEIGHT;
}
}
}
}
}

View file

@ -23,12 +23,13 @@ import net.minecraftforge.eventbus.EventBusErrorMessage;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.IEventListener;
import net.minecraftforge.fml.AutomaticEventSubscriber;
import net.minecraftforge.fml.LifecycleEventProvider;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingException;
import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.ModThreadContext;
import net.minecraftforge.fml.common.event.ModLifecycleEvent;
import net.minecraftforge.fml.AutomaticEventSubscriber;
import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.language.ModFileScanData;
@ -67,7 +68,7 @@ public class FMLModContainer extends ModContainer
catch (Throwable e)
{
LOGGER.error(LOADING, "Failed to load class {}", className, e);
throw new RuntimeException(e);
throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmodclass", e);
}
}
@ -86,7 +87,7 @@ public class FMLModContainer extends ModContainer
private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable)
{
LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable));
modLoadingError.add(throwable);
}
private void beforeEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent) {
@ -105,8 +106,7 @@ public class FMLModContainer extends ModContainer
catch (Throwable e)
{
LOGGER.error(LOADING,"Caught exception during event {} dispatch for modid {}", event, this.getModId(), e);
modLoadingStage = ModLoadingStage.ERROR;
modLoadingError.add(e);
throw new ModLoadingException(modInfo, lifecycleEvent.fromStage(), "fml.modloading.errorduringevent", e);
}
}
@ -120,7 +120,9 @@ public class FMLModContainer extends ModContainer
private void preinitMod(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
LOGGER.debug(LOADING, "Injecting Automatic event subscribers for {}", getModId());
AutomaticEventSubscriber.inject(this, this.scanResults, this.modClass.getClassLoader());
LOGGER.debug(LOADING, "Completed Automatic event subscribers for {}", getModId());
}
private void constructMod(LifecycleEventProvider.LifecycleEvent event)
@ -134,8 +136,7 @@ public class FMLModContainer extends ModContainer
catch (Throwable e)
{
LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e);
modLoadingStage = ModLoadingStage.ERROR;
modLoadingError.add(e);
throw new ModLoadingException(modInfo, event.fromStage(), "fml.modloading.failedtoloadmod", e, modClass);
}
}

View file

@ -0,0 +1,27 @@
package net.minecraftforge.fml.loading;
import java.util.Arrays;
import java.util.List;
/**
* Thrown during early loading phase, and collected by the LoadingModList for handoff to the client
* or server.
*/
public class EarlyLoadingException extends RuntimeException {
private final String i18nMessage;
private final List<Object> context;
public EarlyLoadingException(final String message, final String i18nMessage, final Throwable originalException, Object... context) {
super(message, originalException);
this.i18nMessage = i18nMessage;
this.context = Arrays.asList(context);
}
public String getI18NMessage() {
return this.i18nMessage;
}
public List<Object> getContext() {
return this.context;
}
}

View file

@ -23,7 +23,6 @@ import com.electronwill.nightconfig.core.ConfigSpec;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.common.FMLPaths;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;

View file

@ -17,13 +17,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml.common;
package net.minecraftforge.fml.loading;
import cpw.mods.modlauncher.api.IEnvironment;
import net.minecraftforge.fml.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@ -72,6 +73,12 @@ public enum FMLPaths
for (FMLPaths path : FMLPaths.values())
{
path.absolutePath = rootPath.resolve(path.relativePath).toAbsolutePath();
try {
path.absolutePath = path.absolutePath.toRealPath();
} catch (IOException e) {
LOGGER.error("Unable to resolve path {}", path.absolutePath, e);
throw new RuntimeException(e);
}
LOGGER.debug(CORE,"Path {} is {}", ()-> path, ()-> path.absolutePath);
if (path.isDirectory)
{

View file

@ -25,7 +25,6 @@ import cpw.mods.modlauncher.api.ITransformer;
import cpw.mods.modlauncher.api.IncompatibleEnvironmentException;
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionSpecBuilder;
import net.minecraftforge.fml.common.FMLPaths;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -37,8 +36,6 @@ import java.util.function.BiFunction;
import static net.minecraftforge.fml.Logging.CORE;
import cpw.mods.modlauncher.api.ITransformationService.OptionResult;
public class FMLServiceProvider implements ITransformationService
{

View file

@ -30,10 +30,10 @@ import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -48,6 +48,7 @@ public class LoadingModList
private final List<ModInfo> sortedList;
private final Map<String, ModFileInfo> fileById;
private BackgroundScanHandler scanner;
private final List<EarlyLoadingException> preLoadErrors;
private LoadingModList(final List<ModFile> modFiles, final List<ModInfo> sortedList)
{
@ -58,11 +59,16 @@ public class LoadingModList
this.fileById = this.modFiles.stream().map(ModFileInfo::getMods).flatMap(Collection::stream).
map(ModInfo.class::cast).
collect(Collectors.toMap(ModInfo::getModId, ModInfo::getOwningFile));
this.preLoadErrors = new ArrayList<>();
}
public static LoadingModList of(List<ModFile> modFiles, List<ModInfo> sortedList)
public static LoadingModList of(List<ModFile> modFiles, List<ModInfo> sortedList, final EarlyLoadingException earlyLoadingException)
{
INSTANCE = new LoadingModList(modFiles, sortedList);
if (earlyLoadingException != null)
{
INSTANCE.preLoadErrors.add(earlyLoadingException);
}
return INSTANCE;
}
@ -109,4 +115,8 @@ public class LoadingModList
{
return this.sortedList;
}
public List<EarlyLoadingException> getErrors() {
return preLoadErrors;
}
}

View file

@ -56,10 +56,15 @@ public class ModSorter
public static LoadingModList sort(List<ModFile> mods)
{
final ModSorter ms = new ModSorter(mods);
ms.buildUniqueList();
ms.verifyDependencyVersions();
ms.sort();
return LoadingModList.of(ms.modFiles, ms.sortedList);
EarlyLoadingException earlyLoadingException = null;
try {
ms.buildUniqueList();
ms.verifyDependencyVersions();
ms.sort();
} catch (EarlyLoadingException ele) {
earlyLoadingException = ele;
}
return LoadingModList.of(ms.modFiles, ms.sortedList, earlyLoadingException);
}
private void sort()
@ -77,7 +82,7 @@ public class ModSorter
{
TopologicalSort.TopoSortException.TopoSortExceptionData<Supplier<ModInfo>> data = e.getData();
LOGGER.error(LOADING, ()-> data);
throw e;
throw new EarlyLoadingException("Sorting error", "fml.modloading.sortingerror", e, e.getData());
}
this.sortedList = sorted.stream().map(Supplier::get).map(ModFileInfo::getMods).
flatMap(Collection::stream).map(ModInfo.class::cast).collect(Collectors.toList());
@ -107,7 +112,7 @@ public class ModSorter
final List<Map.Entry<String, List<ModInfo>>> dupedMods = modIds.entrySet().stream().filter(e -> e.getValue().size() > 1).collect(Collectors.toList());
if (!dupedMods.isEmpty()) {
throw new DuplicateModsFoundException(null);
throw new EarlyLoadingException("Duplicate mods found", "fml.modloading.dupesfound", null, dupedMods);
}
modIdNameLookup = modIds.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
@ -127,7 +132,7 @@ public class ModSorter
LOGGER.debug(LOADING, "Found {} mandatory mod requirements missing", missingVersions.size());
if (!missingVersions.isEmpty()) {
throw new RuntimeException("Missing mods");
throw new EarlyLoadingException("Missing mods", "fml.modloading.missingmods", null, missingVersions);
}
}

View file

@ -23,6 +23,7 @@ import org.apache.commons.lang3.text.StrSubstitutor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@ -32,7 +33,11 @@ import java.util.Optional;
public class StringUtils
{
public static String toLowerCase(final String str) {
return str.toLowerCase(java.util.Locale.ROOT);
return str.toLowerCase(Locale.ROOT);
}
public static String toUpperCase(final String str) {
return str.toUpperCase(Locale.ROOT);
}
public static boolean endsWith(final String search, final String... endings) {

View file

@ -20,7 +20,7 @@
package net.minecraftforge.fml.loading.moddiscovery;
import net.minecraftforge.fml.loading.StringUtils;
import net.minecraftforge.fml.common.FMLPaths;
import net.minecraftforge.fml.loading.FMLPaths;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View file

@ -7,6 +7,13 @@
"fml.menu.mods.config": "Config",
"fml.menu.modoptions": "Mod Options...",
"fml.menu.loadingmods": "{0,choice,0#No mods|1#1 mod|1<{0} mods} loaded",
"fml.button.open.file": "Open {0}",
"fml.button.open.mods.folder": "Open Mods Folder",
"fml.loadingerrorscreen.header": "Error loading mods\n{0,choice,1#1 error has|1<{0} errors have} occured during loading",
"fml.modloading.failedtoloadmodclass":"{0,modinfo,name} has class loading errors\n\u00a77{2,exc,msg}",
"fml.modloading.failedtoloadmod":"{0,modinfo,name} ({0,modinfo,id}) has failed to load correctly\n\u00a77{2,exc,msg}",
"fml.modloading.errorduringevent":"{0,modinfo,name} ({0,modinfo,id}) encountered an error during the {1,lower} event phase\n\u00a77{2,exc,msg}",
"fml.modloading.failedtoloadforge": "Failed to load forge",
"commands.forge.dimensions.list": "Currently registered dimensions by type:",
"commands.forge.entity.list.invalid": "Invalid filter, does not match any entities. Use /forge entity list for a proper list",