Improve missing mod dependency error screen (#4762)

This commit is contained in:
mezz 2018-04-14 18:17:06 -07:00 committed by GitHub
parent fab45a32a8
commit a794f1daff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 214 additions and 56 deletions

View file

@ -19,9 +19,13 @@
package net.minecraftforge.fml.client;
import java.util.List;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.MissingModsException;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.versioning.ArtifactVersion;
import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion;
@ -38,31 +42,49 @@ public class GuiModsMissing extends GuiErrorBase
public void drawScreen(int mouseX, int mouseY, float partialTicks)
{
this.drawDefaultBackground();
int offset = Math.max(85 - modsMissing.missingMods.size() * 10, 10);
String modMissingDependenciesText = I18n.format("fml.messages.mod.missing.dependencies", TextFormatting.BOLD + modsMissing.getModName() + TextFormatting.RESET);
List<MissingModsException.MissingModInfo> missingModsVersions = modsMissing.getMissingModInfos();
int offset = Math.max(85 - missingModsVersions.size() * 10, 10);
String modMissingDependenciesText = I18n.format("fml.messages.mod.missing.dependencies.compatibility", TextFormatting.BOLD + modsMissing.getModName() + TextFormatting.RESET);
this.drawCenteredString(this.fontRenderer, modMissingDependenciesText, this.width / 2, offset, 0xFFFFFF);
offset+=10;
String fixMissingDependenciesText = I18n.format("fml.messages.mod.missing.dependencies.fix", modsMissing.getModName());
this.drawCenteredString(this.fontRenderer, fixMissingDependenciesText, this.width / 2, offset, 0xFFFFFF);
offset+=5;
for (ArtifactVersion v : modsMissing.missingMods)
for (MissingModsException.MissingModInfo versionInfo : missingModsVersions)
{
offset+=10;
if (v instanceof DefaultArtifactVersion)
ArtifactVersion acceptedVersion = versionInfo.getAcceptedVersion();
String acceptedModId = acceptedVersion.getLabel();
ArtifactVersion currentVersion = versionInfo.getCurrentVersion();
String missingReason;
if (currentVersion == null)
{
DefaultArtifactVersion dav = (DefaultArtifactVersion)v;
missingReason = I18n.format("fml.messages.mod.missing.dependencies.missing");
}
else
{
missingReason = I18n.format("fml.messages.mod.missing.dependencies.you.have", currentVersion.getVersionString());
}
String acceptedModVersionString = acceptedVersion.getRangeString();
if (acceptedVersion instanceof DefaultArtifactVersion)
{
DefaultArtifactVersion dav = (DefaultArtifactVersion)acceptedVersion;
if (dav.getRange() != null)
{
String message = String.format(TextFormatting.BOLD + "%s " + TextFormatting.RESET + "%s", v.getLabel(), dav.getRange().toStringFriendly());
this.drawCenteredString(this.fontRenderer, message, this.width / 2, offset, 0xEEEEEE);
continue;
acceptedModVersionString = dav.getRange().toStringFriendly();
}
}
this.drawCenteredString(this.fontRenderer, String.format(TextFormatting.BOLD + "%s" + TextFormatting.RESET + " : %s", v.getLabel(), v.getRangeString()), this.width / 2, offset, 0xEEEEEE);
ModContainer acceptedMod = Loader.instance().getIndexedModList().get(acceptedModId);
String acceptedModName = acceptedMod != null ? acceptedMod.getName() : acceptedModId;
String versionInfoText = String.format(TextFormatting.BOLD + "%s " + TextFormatting.RESET + "%s (%s)", acceptedModName, acceptedModVersionString, missingReason);
String message;
if (versionInfo.isRequired())
{
message = I18n.format("fml.messages.mod.missing.dependencies.requires", versionInfoText);
}
else
{
message = I18n.format("fml.messages.mod.missing.dependencies.compatible.with", versionInfoText);
}
offset += 10;
this.drawCenteredString(this.fontRenderer, message, this.width / 2, offset, 0xEEEEEE);
}
offset+=20;
String seeLogText = I18n.format("fml.messages.mod.missing.dependencies.see.log", GuiErrorBase.logFile.getName());
this.drawCenteredString(this.fontRenderer, seeLogText, this.width / 2, offset, 0xFFFFFF);
super.drawScreen(mouseX, mouseY, partialTicks);
}
}

View file

@ -19,6 +19,8 @@
package net.minecraftforge.fml.client;
import java.util.List;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
@ -52,15 +54,17 @@ public class GuiModsMissingForServer extends GuiScreen
public void drawScreen(int mouseX, int mouseY, float partialTicks)
{
this.drawDefaultBackground();
int offset = Math.max(85 - modsMissing.missingMods.size() * 10, 10);
List<MissingModsException.MissingModInfo> missingModsVersions = modsMissing.getMissingModInfos();
int offset = Math.max(85 - missingModsVersions.size() * 10, 10);
this.drawCenteredString(this.fontRenderer, "Forge Mod Loader could not connect to this server", this.width / 2, offset, 0xFFFFFF);
offset += 10;
this.drawCenteredString(this.fontRenderer, "The mods and versions listed below could not be found", this.width / 2, offset, 0xFFFFFF);
offset += 10;
this.drawCenteredString(this.fontRenderer, "They are required to play on this server", this.width / 2, offset, 0xFFFFFF);
offset += 5;
for (ArtifactVersion v : modsMissing.missingMods)
for (MissingModsException.MissingModInfo info : missingModsVersions)
{
ArtifactVersion v = info.getAcceptedVersion();
offset += 10;
this.drawCenteredString(this.fontRenderer, String.format("%s : %s", v.getLabel(), v.getRangeString()), this.width / 2, offset, 0xEEEEEE);
}

View file

@ -26,6 +26,7 @@ import net.minecraft.client.resources.I18n;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.MissingModsException;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.MultipleModsErrored;
import net.minecraftforge.fml.common.WrongMinecraftVersionException;
import net.minecraftforge.fml.common.versioning.ArtifactVersion;
@ -51,12 +52,12 @@ public class GuiMultipleModsErrored extends GuiErrorBase
public void initGui()
{
super.initGui();
int additionalSize = missingModsExceptions.isEmpty()||wrongMinecraftExceptions.isEmpty() ? 20 : 55;
for(MissingModsException exception : missingModsExceptions)
int additionalSize = missingModsExceptions.isEmpty() || wrongMinecraftExceptions.isEmpty() ? 20 : 55;
for (MissingModsException exception : missingModsExceptions)
{
additionalSize+=exception.missingMods.size()*10;
additionalSize += exception.getMissingModInfos().size() * 10;
}
list = new GuiList(wrongMinecraftExceptions.size()*10+missingModsExceptions.size()*15+additionalSize);
list = new GuiList(wrongMinecraftExceptions.size() * 10 + missingModsExceptions.size() * 15 + additionalSize);
}
@Override
@ -139,26 +140,50 @@ public class GuiMultipleModsErrored extends GuiErrorBase
}
if (!missingModsExceptions.isEmpty())
{
renderer.drawString(TextFormatting.UNDERLINE + I18n.format("fml.messages.mod.missing.dependencies.multiple"), this.left, offset, 0xFFFFFF);
renderer.drawString(I18n.format("fml.messages.mod.missing.dependencies.multiple.issues"), this.left, offset, 0xFFFFFF);
offset+=15;
for (MissingModsException exception : missingModsExceptions)
{
renderer.drawString(I18n.format("fml.messages.mod.missing.dependencies.fix", TextFormatting.BOLD + exception.getModName() + TextFormatting.RESET), this.left, offset, 0xFFFFFF);
for (ArtifactVersion v : exception.missingMods)
renderer.drawString(exception.getModName() + ":", this.left, offset, 0xFFFFFF);
for (MissingModsException.MissingModInfo versionInfo : exception.getMissingModInfos())
{
offset+=10;
if (v instanceof DefaultArtifactVersion)
ArtifactVersion acceptedVersion = versionInfo.getAcceptedVersion();
String acceptedModId = acceptedVersion.getLabel();
ArtifactVersion currentVersion = versionInfo.getCurrentVersion();
String missingReason;
if (currentVersion == null)
{
DefaultArtifactVersion dav = (DefaultArtifactVersion)v;
missingReason = I18n.format("fml.messages.mod.missing.dependencies.missing");
}
else
{
missingReason = I18n.format("fml.messages.mod.missing.dependencies.you.have", currentVersion.getVersionString());
}
String acceptedModVersionString = acceptedVersion.getRangeString();
if (acceptedVersion instanceof DefaultArtifactVersion)
{
DefaultArtifactVersion dav = (DefaultArtifactVersion)acceptedVersion;
if (dav.getRange() != null)
{
String message = String.format(TextFormatting.BOLD + "%s " + TextFormatting.RESET + "%s", v.getLabel(), dav.getRange().toStringFriendly());
renderer.drawString(message, this.left, offset, 0xEEEEEE);
continue;
acceptedModVersionString = dav.getRange().toStringFriendly();
}
}
renderer.drawString(String.format(TextFormatting.BOLD + "%s" + TextFormatting.RESET + " : %s", v.getLabel(), v.getRangeString()), this.left, offset, 0xEEEEEE);
ModContainer acceptedMod = Loader.instance().getIndexedModList().get(acceptedModId);
String acceptedModName = acceptedMod != null ? acceptedMod.getName() : acceptedModId;
String versionInfoText = String.format(TextFormatting.BOLD + "%s " + TextFormatting.RESET + "%s (%s)", acceptedModName, acceptedModVersionString, missingReason);
String message;
if (versionInfo.isRequired())
{
message = I18n.format("fml.messages.mod.missing.dependencies.requires", versionInfoText);
}
else
{
message = I18n.format("fml.messages.mod.missing.dependencies.compatible.with", versionInfoText);
}
offset += 10;
renderer.drawString(message, this.left, offset, 0xEEEEEE);
}
offset += 15;
}
}

View file

@ -231,8 +231,8 @@ public class Loader
private void sortModList()
{
FMLLog.log.trace("Verifying mod requirements are satisfied");
List<WrongMinecraftVersionException> wrongMinecraftExceptions = new ArrayList<WrongMinecraftVersionException>();
List<MissingModsException> missingModsExceptions = new ArrayList<MissingModsException>();
List<WrongMinecraftVersionException> wrongMinecraftExceptions = new ArrayList<>();
List<MissingModsException> missingModsExceptions = new ArrayList<>();
try
{
BiMap<String, ArtifactVersion> modVersions = HashBiMap.create();
@ -253,39 +253,42 @@ public class Loader
continue;
}
Map<String,ArtifactVersion> names = Maps.uniqueIndex(mod.getRequirements(), ArtifactVersion::getLabel);
Set<ArtifactVersion> versionMissingMods = Sets.newHashSet();
Set<String> missingMods = Sets.difference(names.keySet(), modVersions.keySet());
if (!missingMods.isEmpty())
{
MissingModsException missingModsException = new MissingModsException(mod.getModId(), mod.getName());
FMLLog.log.fatal("The mod {} ({}) requires mods {} to be available", mod.getModId(), mod.getName(), missingMods);
for (String modid : missingMods)
{
versionMissingMods.add(names.get(modid));
ArtifactVersion acceptedVersion = names.get(modid);
ArtifactVersion currentVersion = modVersions.get(modid);
boolean required = mod.getRequirements().contains(acceptedVersion);
missingModsException.addMissingMod(acceptedVersion, currentVersion, required);
}
MissingModsException ret = new MissingModsException(versionMissingMods, mod.getModId(), mod.getName());
FMLLog.log.fatal(ret.getMessage());
missingModsExceptions.add(ret);
FMLLog.log.fatal(missingModsException.getMessage());
missingModsExceptions.add(missingModsException);
continue;
}
reqList.putAll(mod.getModId(), names.keySet());
ImmutableList<ArtifactVersion> allDeps = ImmutableList.<ArtifactVersion>builder().addAll(mod.getDependants()).addAll(mod.getDependencies()).build();
for (ArtifactVersion v : allDeps)
MissingModsException missingModsException = new MissingModsException(mod.getModId(), mod.getName());
for (ArtifactVersion acceptedVersion : allDeps)
{
if (modVersions.containsKey(v.getLabel()))
if (modVersions.containsKey(acceptedVersion.getLabel()))
{
if (!v.containsVersion(modVersions.get(v.getLabel())))
ArtifactVersion currentVersion = modVersions.get(acceptedVersion.getLabel());
if (!acceptedVersion.containsVersion(currentVersion))
{
versionMissingMods.add(v);
boolean required = mod.getRequirements().contains(acceptedVersion);
missingModsException.addMissingMod(acceptedVersion, currentVersion, required);
}
}
}
if (!versionMissingMods.isEmpty())
if (!missingModsException.getMissingModInfos().isEmpty())
{
FMLLog.log.fatal("The mod {} ({}) requires mod versions {} to be available", mod.getModId(), mod.getName(), versionMissingMods);
MissingModsException ret = new MissingModsException(versionMissingMods, mod.getModId(), mod.getName());
FMLLog.log.fatal(ret.toString());
missingModsExceptions.add(ret);
FMLLog.log.fatal(missingModsException.toString());
missingModsExceptions.add(missingModsException);
}
}
@ -306,7 +309,7 @@ public class Loader
throw new MultipleModsErrored(wrongMinecraftExceptions, missingModsExceptions);
}
reverseDependencies = Multimaps.invertFrom(reqList, ArrayListMultimap.<String,String>create());
reverseDependencies = Multimaps.invertFrom(reqList, ArrayListMultimap.create());
ModSorter sorter = new ModSorter(getActiveModList(), namedMods);
try

View file

@ -19,36 +19,117 @@
package net.minecraftforge.fml.common;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.base.Preconditions;
import net.minecraftforge.fml.common.versioning.ArtifactVersion;
public class MissingModsException extends EnhancedRuntimeException
{
private static final long serialVersionUID = 1L;
private final String id;
private final String name;
/** @deprecated use {@link #getMissingModInfos()} */
@Deprecated // TODO remove in 1.13
public final Set<ArtifactVersion> missingMods;
private final List<MissingModInfo> missingModsInfos;
private final String modName;
public MissingModsException(String id, String name)
{
this(new HashSet<>(), id, name);
}
/**
* @deprecated use {@link #MissingModsException(String, String)}
*/
@Deprecated // TODO remove in 1.13
public MissingModsException(Set<ArtifactVersion> missingMods, String id, String name)
{
super(String.format("Mod %s (%s) requires %s", id, name, missingMods));
this.id = id;
this.name = name;
this.missingMods = missingMods;
this.missingModsInfos = new ArrayList<>();
for (ArtifactVersion artifactVersion : missingMods)
{
missingModsInfos.add(new MissingModInfo(artifactVersion, null, true));
}
this.modName = name;
}
@Override
public String getMessage()
{
Set<ArtifactVersion> missingMods = missingModsInfos.stream().map(MissingModInfo::getAcceptedVersion).collect(Collectors.toSet());
return String.format("Mod %s (%s) requires %s", id, name, missingMods);
}
public void addMissingMod(ArtifactVersion acceptedVersion, @Nullable ArtifactVersion currentVersion, boolean required)
{
MissingModInfo missingModInfo = new MissingModInfo(acceptedVersion, currentVersion, required);
this.missingModsInfos.add(missingModInfo);
this.missingMods.add(acceptedVersion);
}
public String getModName()
{
return modName;
}
public List<MissingModInfo> getMissingModInfos()
{
return Collections.unmodifiableList(this.missingModsInfos);
}
@Override
protected void printStackTrace(WrappedPrintStream stream)
{
stream.println("Missing Mods:");
for (ArtifactVersion v : missingMods)
for (MissingModInfo info : this.missingModsInfos)
{
stream.println(String.format("\t%s : %s", v.getLabel(), v.getRangeString()));
ArtifactVersion acceptedVersion = info.getAcceptedVersion();
ArtifactVersion currentVersion = info.getCurrentVersion();
String currentString = currentVersion != null ? currentVersion.getVersionString() : "missing";
stream.println(String.format("\t%s : need %s: have %s", acceptedVersion.getVersionString(), acceptedVersion.getRangeString(), currentString));
}
stream.println("");
}
public static class MissingModInfo
{
private final ArtifactVersion acceptedVersion;
@Nullable
private final ArtifactVersion currentVersion;
private final boolean required;
private MissingModInfo(ArtifactVersion acceptedVersion, @Nullable ArtifactVersion currentVersion, boolean required)
{
Preconditions.checkNotNull(acceptedVersion, "acceptedVersion");
this.acceptedVersion = acceptedVersion;
this.currentVersion = currentVersion;
this.required = required;
}
@Nullable
public ArtifactVersion getCurrentVersion()
{
return currentVersion;
}
public ArtifactVersion getAcceptedVersion()
{
return acceptedVersion;
}
public boolean isRequired()
{
return required;
}
}
}

View file

@ -21,13 +21,26 @@ package net.minecraftforge.fml.common;
import java.util.List;
public class MultipleModsErrored extends RuntimeException
public class MultipleModsErrored extends EnhancedRuntimeException
{
public final List<WrongMinecraftVersionException> wrongMinecraftExceptions;
public final List<MissingModsException>missingModsExceptions;
public final List<MissingModsException> missingModsExceptions;
public MultipleModsErrored(List<WrongMinecraftVersionException> wrongMinecraftExceptions, List<MissingModsException> missingModsExceptions)
{
this.wrongMinecraftExceptions = wrongMinecraftExceptions;
this.missingModsExceptions = missingModsExceptions;
}
@Override
protected void printStackTrace(WrappedPrintStream stream)
{
for (WrongMinecraftVersionException wrongMinecraftVersionException : this.wrongMinecraftExceptions)
{
wrongMinecraftVersionException.printStackTrace(stream);
}
for (MissingModsException missingModsException : this.missingModsExceptions)
{
missingModsException.printStackTrace(stream);
}
}
}

View file

@ -102,7 +102,11 @@ public class DefaultArtifactVersion implements ArtifactVersion
@Override
public String toString()
{
return label == null ? comparableVersion.toString() : label + ( unbounded ? "" : "@" + range);
if (label == null)
{
return getVersionString();
}
return label + (unbounded ? "" : "@" + range);
}
public VersionRange getRange()

View file

@ -478,7 +478,7 @@ public class VersionRange
{
if ( recommendedVersion != null )
{
return recommendedVersion.toString();
return recommendedVersion.getVersionString();
}
else
{

View file

@ -203,8 +203,14 @@ item.forge.bucketFilled.name=%s Bucket
fml.messages.mod.missing.dependencies=%s is missing mods it depends on.
fml.messages.mod.missing.dependencies.fix=Include the following mods or remove %s.
fml.messages.mod.missing.dependencies.compatibility=You must include the right dependencies for %s:
fml.messages.mod.missing.dependencies.missing=missing
fml.messages.mod.missing.dependencies.compatible.with=Only compatible with %s
fml.messages.mod.missing.dependencies.requires=Requires %s
fml.messages.mod.missing.dependencies.you.have=you have %s
fml.messages.mod.missing.dependencies.see.log=See '%s' for technical information.
fml.messages.mod.missing.dependencies.multiple=Some mods are missing mods they depends on.
fml.messages.mod.missing.dependencies.multiple.issues=You must include the right dependencies for the following mods:
fml.messages.mod.missing.multiple=There were %s errors loading Minecraft
fml.messages.mod.wrongminecraft=The mods listed below can't run in Minecraft version %s
fml.messages.mod.wrongminecraft.requirement=%s (%s) requires Minecraft %s