diff --git a/fml/client/cpw/mods/fml/client/FMLClientHandler.java b/fml/client/cpw/mods/fml/client/FMLClientHandler.java index fcc55cd98..e030635d9 100644 --- a/fml/client/cpw/mods/fml/client/FMLClientHandler.java +++ b/fml/client/cpw/mods/fml/client/FMLClientHandler.java @@ -46,6 +46,7 @@ import cpw.mods.fml.common.IFMLSidedHandler; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.LoaderException; import cpw.mods.fml.common.MetadataCollection; +import cpw.mods.fml.common.MissingModsException; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.ObfuscationReflectionHelper; @@ -102,6 +103,8 @@ public class FMLClientHandler implements IFMLSidedHandler private boolean serverIsRunning; + private MissingModsException modsMissing; + public void beginMinecraftLoading(Minecraft minecraft) { if (minecraft.func_71355_q()) @@ -133,6 +136,10 @@ public class FMLClientHandler implements IFMLSidedHandler { Loader.instance().loadMods(); } + catch (MissingModsException missing) + { + modsMissing = missing; + } catch (LoaderException le) { haltGame("There was a severe problem during mod loading that has caused the game to fail", le); @@ -154,6 +161,10 @@ public class FMLClientHandler implements IFMLSidedHandler @SuppressWarnings("deprecation") public void finishMinecraftLoading() { + if (modsMissing != null) + { + return; + } try { Loader.instance().initializeMods(); @@ -169,9 +180,16 @@ public class FMLClientHandler implements IFMLSidedHandler KeyBindingRegistry.instance().uploadKeyBindingsToGame(client.field_71474_y); } - public void reloadTextureFX() + public void onInitializationComplete() { - TextureFXManager.instance().loadTextures(client.field_71418_C.func_77292_e()); + if (modsMissing != null) + { + client.func_71373_a(new ModsMissingGuiScreen(modsMissing)); + } + else + { + TextureFXManager.instance().loadTextures(client.field_71418_C.func_77292_e()); + } } /** * Get the server instance diff --git a/fml/client/cpw/mods/fml/client/ModsMissingGuiScreen.java b/fml/client/cpw/mods/fml/client/ModsMissingGuiScreen.java new file mode 100644 index 000000000..9075ae8eb --- /dev/null +++ b/fml/client/cpw/mods/fml/client/ModsMissingGuiScreen.java @@ -0,0 +1,34 @@ +package cpw.mods.fml.client; + +import cpw.mods.fml.common.MissingModsException; +import cpw.mods.fml.common.versioning.ArtifactVersion; +import net.minecraft.src.GuiErrorScreen; + +public class ModsMissingGuiScreen extends GuiErrorScreen +{ + + private MissingModsException modsMissing; + + public ModsMissingGuiScreen(MissingModsException modsMissing) + { + this.modsMissing = modsMissing; + } + + @Override + public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_) + { + this.func_73873_v_(); + int offset = 85 - modsMissing.missingMods.size() * 10; + this.func_73732_a(this.field_73886_k, "Forge Mod Loader has found a problem with your minecraft installation", this.field_73880_f / 2, offset, 0xFFFFFF); + offset+=10; + this.func_73732_a(this.field_73886_k, "The mods and versions listed below could not be found", this.field_73880_f / 2, offset, 0xFFFFFF); + offset+=5; + for (ArtifactVersion v : modsMissing.missingMods) + { + offset+=10; + this.func_73732_a(this.field_73886_k, String.format("%s : %s", v.getLabel(), v.getVersionString()), this.field_73880_f / 2, offset, 0xEEEEEE); + } + offset+=20; + this.func_73732_a(this.field_73886_k, "The file 'ForgeModLoader-client-0.log' contains more information", this.field_73880_f / 2, offset, 0xFFFFFF); + } +} diff --git a/fml/common/cpw/mods/fml/common/DummyModContainer.java b/fml/common/cpw/mods/fml/common/DummyModContainer.java index ee6bc3e09..3a375dded 100644 --- a/fml/common/cpw/mods/fml/common/DummyModContainer.java +++ b/fml/common/cpw/mods/fml/common/DummyModContainer.java @@ -3,14 +3,17 @@ package cpw.mods.fml.common; import java.io.File; import java.util.Collections; import java.util.List; +import java.util.Set; import com.google.common.eventbus.EventBus; import cpw.mods.fml.common.versioning.ArtifactVersion; +import cpw.mods.fml.common.versioning.DefaultArtifactVersion; public class DummyModContainer implements ModContainer { private ModMetadata md; + private ArtifactVersion processedVersion; public DummyModContainer(ModMetadata md) { @@ -39,9 +42,9 @@ public class DummyModContainer implements ModContainer } @Override - public List getRequirements() + public Set getRequirements() { - return Collections.emptyList(); + return Collections.emptySet(); } @Override @@ -105,7 +108,11 @@ public class DummyModContainer implements ModContainer @Override public ArtifactVersion getProcessedVersion() { - return null; + if (processedVersion == null) + { + processedVersion = new DefaultArtifactVersion(getModId(), getVersion()); + } + return processedVersion; } @Override diff --git a/fml/common/cpw/mods/fml/common/FMLModContainer.java b/fml/common/cpw/mods/fml/common/FMLModContainer.java index fd698f183..7297ecf8f 100644 --- a/fml/common/cpw/mods/fml/common/FMLModContainer.java +++ b/fml/common/cpw/mods/fml/common/FMLModContainer.java @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Set; import com.google.common.base.Strings; import com.google.common.base.Throwables; @@ -27,6 +28,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @@ -125,7 +127,7 @@ public class FMLModContainer implements ModContainer if (overridesMetadata || !modMetadata.useDependencyInformation) { - List requirements = Lists.newArrayList(); + Set requirements = Sets.newHashSet(); List dependencies = Lists.newArrayList(); List dependants = Lists.newArrayList(); annotationDependencies = (String) descriptor.get("dependencies"); @@ -133,11 +135,18 @@ public class FMLModContainer implements ModContainer modMetadata.requiredMods = requirements; modMetadata.dependencies = dependencies; modMetadata.dependants = dependants; + FMLLog.finest("Parsed dependency info : %s %s %s", requirements, dependencies, dependants); } if (Strings.isNullOrEmpty(modMetadata.name)) { + FMLLog.info("Mod %s is missing a required element, name. Substituting %s", getModId(), getModId()); modMetadata.name = getModId(); } + if (Strings.isNullOrEmpty(modMetadata.version)) + { + FMLLog.warning("Mod %s is missing a required element, version. Substituting 1", getModId()); + modMetadata.version = "1"; + } } @Override @@ -147,7 +156,7 @@ public class FMLModContainer implements ModContainer } @Override - public List getRequirements() + public Set getRequirements() { return modMetadata.requiredMods; } @@ -167,7 +176,7 @@ public class FMLModContainer implements ModContainer @Override public String getSortingRules() { - return (overridesMetadata ? annotationDependencies : modMetadata.printableSortingRules()); + return ((overridesMetadata || !modMetadata.useDependencyInformation) ? annotationDependencies : modMetadata.printableSortingRules()); } @Override diff --git a/fml/common/cpw/mods/fml/common/InjectedModContainer.java b/fml/common/cpw/mods/fml/common/InjectedModContainer.java index e6f83ac59..aa64446cc 100644 --- a/fml/common/cpw/mods/fml/common/InjectedModContainer.java +++ b/fml/common/cpw/mods/fml/common/InjectedModContainer.java @@ -2,6 +2,7 @@ package cpw.mods.fml.common; import java.io.File; import java.util.List; +import java.util.Set; import com.google.common.eventbus.EventBus; @@ -53,7 +54,7 @@ public class InjectedModContainer implements ModContainer wrappedContainer.setEnabledState(enabled); } - public List getRequirements() + public Set getRequirements() { return wrappedContainer.getRequirements(); } diff --git a/fml/common/cpw/mods/fml/common/Loader.java b/fml/common/cpw/mods/fml/common/Loader.java index 475d14f4f..408e35d3a 100644 --- a/fml/common/cpw/mods/fml/common/Loader.java +++ b/fml/common/cpw/mods/fml/common/Loader.java @@ -20,6 +20,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.concurrent.Callable; import java.util.logging.Level; @@ -30,14 +31,17 @@ import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.collect.Multiset.Entry; import com.google.common.collect.Multisets; import com.google.common.collect.Ordering; +import com.google.common.collect.Sets.SetView; import com.google.common.collect.TreeMultimap; import cpw.mods.fml.common.LoaderState.ModState; @@ -173,7 +177,7 @@ public class Loader FMLLog.fine("Verifying mod requirements are satisfied"); try { - Map modVersions = Maps.newHashMap(); + BiMap modVersions = HashBiMap.create(); for (ModContainer mod : mods) { modVersions.put(mod.getModId(), mod.getProcessedVersion()); @@ -181,18 +185,29 @@ public class Loader for (ModContainer mod : mods) { + Set missingMods = Sets.difference(mod.getRequirements(), modVersions.values()); + if (!missingMods.isEmpty()) + { + FMLLog.severe("The mod %s (%s) requires mods %s to be available", mod.getModId(), mod.getName(), missingMods); + throw new MissingModsException(missingMods); + } ImmutableList allDeps = ImmutableList.builder().addAll(mod.getDependants()).addAll(mod.getDependencies()).build(); for (ArtifactVersion v : allDeps) { + missingMods = Sets.newHashSet(); if (modVersions.containsKey(v.getLabel())) { if (!v.containsVersion(modVersions.get(v.getLabel()))) { - FMLLog.log(Level.SEVERE, "The mod %s (%s) requires mods %s to be available, one or more are not", mod.getModId(), mod.getName(), allDeps); - throw new LoaderException(); + missingMods.add(v); } } } + if (!missingMods.isEmpty()) + { + FMLLog.severe("The mod %s (%s) requires mod versions %s to be available", mod.getModId(), mod.getName(), missingMods); + throw new MissingModsException(missingMods); + } } FMLLog.fine("All mod requirements are satisfied"); @@ -506,7 +521,7 @@ public class Loader return modClassLoader; } - public void computeDependencies(String dependencyString, List requirements, List dependencies, List dependants) + public void computeDependencies(String dependencyString, Set requirements, List dependencies, List dependants) { if (dependencyString == null || dependencyString.length() == 0) { diff --git a/fml/common/cpw/mods/fml/common/MissingModsException.java b/fml/common/cpw/mods/fml/common/MissingModsException.java new file mode 100644 index 000000000..bd6170f95 --- /dev/null +++ b/fml/common/cpw/mods/fml/common/MissingModsException.java @@ -0,0 +1,19 @@ +package cpw.mods.fml.common; + +import java.util.Set; + +import com.google.common.collect.Sets.SetView; + +import cpw.mods.fml.common.versioning.ArtifactVersion; + +public class MissingModsException extends RuntimeException +{ + + public Set missingMods; + + public MissingModsException(Set missingMods) + { + this.missingMods = missingMods; + } + +} diff --git a/fml/common/cpw/mods/fml/common/ModContainer.java b/fml/common/cpw/mods/fml/common/ModContainer.java index 84fb6fad4..c188a29d7 100644 --- a/fml/common/cpw/mods/fml/common/ModContainer.java +++ b/fml/common/cpw/mods/fml/common/ModContainer.java @@ -15,6 +15,7 @@ package cpw.mods.fml.common; import java.io.File; import java.util.List; +import java.util.Set; import com.google.common.eventbus.EventBus; @@ -90,7 +91,7 @@ public interface ModContainer * * @return */ - List getRequirements(); + Set getRequirements(); /** * A list of modids that should be loaded prior to this one. The special diff --git a/fml/common/cpw/mods/fml/common/ModMetadata.java b/fml/common/cpw/mods/fml/common/ModMetadata.java index 8e86fb035..84f5ff049 100644 --- a/fml/common/cpw/mods/fml/common/ModMetadata.java +++ b/fml/common/cpw/mods/fml/common/ModMetadata.java @@ -16,8 +16,10 @@ package cpw.mods.fml.common; import static argo.jdom.JsonNodeBuilders.aStringBuilder; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import argo.jdom.JsonNode; @@ -82,7 +84,7 @@ public class ModMetadata public List childMods = Lists.newArrayList(); public boolean useDependencyInformation; - public List requiredMods; + public Set requiredMods; public List dependencies; public List dependants; public boolean autogenerated; @@ -109,9 +111,9 @@ public class ModMetadata credits = Strings.nullToEmpty((String)processedFields.get(aStringBuilder("credits"))); parent = Strings.nullToEmpty((String)processedFields.get(aStringBuilder("parent"))); authorList = Objects.firstNonNull(((List)processedFields.get(aStringBuilder("authors"))),Objects.firstNonNull(((List)processedFields.get(aStringBuilder("authorList"))), authorList)); - requiredMods = processReferences((List)processedFields.get(aStringBuilder("requiredMods"))); - dependencies = processReferences((List)processedFields.get(aStringBuilder("dependencies"))); - dependants = processReferences((List)processedFields.get(aStringBuilder("dependants"))); + requiredMods = processReferences(processedFields.get(aStringBuilder("requiredMods")), Set.class); + dependencies = processReferences(processedFields.get(aStringBuilder("dependencies")), List.class); + dependants = processReferences(processedFields.get(aStringBuilder("dependants")), List.class); useDependencyInformation = Boolean.parseBoolean(Strings.nullToEmpty((String)processedFields.get(aStringBuilder("useDependencyInformation")))); } @@ -119,14 +121,23 @@ public class ModMetadata { } - private List processReferences(List refs) + private > T processReferences(Object refs, Class retType) { - List res = Lists.newArrayList(); + T res = null; + try + { + res = retType.newInstance(); + } + catch (Exception e) + { + // unpossible + } + if (refs == null) { return res; } - for (String ref : refs) + for (String ref : ((List)refs)) { res.add(VersionParser.parseVersionReference(ref)); } diff --git a/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java b/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java index 94ccc008e..c81efd512 100644 --- a/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java +++ b/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java @@ -24,6 +24,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import java.util.logging.Level; import com.google.common.base.Strings; @@ -65,7 +66,7 @@ public class ModLoaderModContainer implements ModContainer { public BaseModProxy mod; private File modSource; - public List requirements = Lists.newArrayList(); + public Set requirements = Sets.newHashSet(); public ArrayList dependencies = Lists.newArrayList(); public ArrayList dependants = Lists.newArrayList(); private ContainerType sourceType; @@ -381,7 +382,7 @@ public class ModLoaderModContainer implements ModContainer } @Override - public List getRequirements() + public Set getRequirements() { return requirements; } diff --git a/fml/common/cpw/mods/fml/common/versioning/ArtifactVersion.java b/fml/common/cpw/mods/fml/common/versioning/ArtifactVersion.java index 7a574a6a9..8c9904fc3 100644 --- a/fml/common/cpw/mods/fml/common/versioning/ArtifactVersion.java +++ b/fml/common/cpw/mods/fml/common/versioning/ArtifactVersion.java @@ -30,5 +30,7 @@ public interface ArtifactVersion { String getLabel(); + String getVersionString(); + boolean containsVersion(ArtifactVersion source); } diff --git a/fml/common/cpw/mods/fml/common/versioning/DefaultArtifactVersion.java b/fml/common/cpw/mods/fml/common/versioning/DefaultArtifactVersion.java index 6ba93e8f8..bcb10ad72 100644 --- a/fml/common/cpw/mods/fml/common/versioning/DefaultArtifactVersion.java +++ b/fml/common/cpw/mods/fml/common/versioning/DefaultArtifactVersion.java @@ -70,6 +70,12 @@ public class DefaultArtifactVersion implements ArtifactVersion } } + @Override + public String getVersionString() + { + return comparableVersion==null ? "any" : comparableVersion.toString(); + } + @Override public String toString() { diff --git a/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch b/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch index 0911f8264..711252524 100644 --- a/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch +++ b/fml/patches/minecraft/net/minecraft/client/Minecraft.java.patch @@ -53,7 +53,7 @@ { this.func_71352_k(); } -+ FMLClientHandler.instance().reloadTextureFX(); ++ FMLClientHandler.instance().onInitializationComplete(); } private void func_71357_I() throws LWJGLException