diff --git a/fml/client/cpw/mods/fml/client/FMLClientHandler.java b/fml/client/cpw/mods/fml/client/FMLClientHandler.java index 3b918005a..6faa12c99 100644 --- a/fml/client/cpw/mods/fml/client/FMLClientHandler.java +++ b/fml/client/cpw/mods/fml/client/FMLClientHandler.java @@ -54,6 +54,7 @@ import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.ObfuscationReflectionHelper; import cpw.mods.fml.common.Side; +import cpw.mods.fml.common.WrongMinecraftVersionException; import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket; import cpw.mods.fml.common.network.EntitySpawnPacket; import cpw.mods.fml.common.network.ModMissingPacket; @@ -104,6 +105,8 @@ public class FMLClientHandler implements IFMLSidedHandler private boolean loading; + private WrongMinecraftVersionException wrongMC; + /** * Called to start the whole game off from * {@link MinecraftServer#startServer} @@ -142,6 +145,10 @@ public class FMLClientHandler implements IFMLSidedHandler { Loader.instance().loadMods(); } + catch (WrongMinecraftVersionException wrong) + { + wrongMC = wrong; + } catch (MissingModsException missing) { modsMissing = missing; @@ -167,7 +174,7 @@ public class FMLClientHandler implements IFMLSidedHandler @SuppressWarnings("deprecation") public void finishMinecraftLoading() { - if (modsMissing != null) + if (modsMissing != null || wrongMC != null) { return; } @@ -189,7 +196,11 @@ public class FMLClientHandler implements IFMLSidedHandler public void onInitializationComplete() { - if (modsMissing != null) + if (wrongMC != null) + { + client.func_71373_a(new GuiWrongMinecraft(wrongMC)); + } + else if (modsMissing != null) { client.func_71373_a(new GuiModsMissing(modsMissing)); } diff --git a/fml/client/cpw/mods/fml/client/GuiWrongMinecraft.java b/fml/client/cpw/mods/fml/client/GuiWrongMinecraft.java new file mode 100644 index 000000000..25d039bbc --- /dev/null +++ b/fml/client/cpw/mods/fml/client/GuiWrongMinecraft.java @@ -0,0 +1,35 @@ +package cpw.mods.fml.client; + +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.WrongMinecraftVersionException; +import cpw.mods.fml.common.versioning.ArtifactVersion; +import net.minecraft.src.GuiErrorScreen; +import net.minecraft.src.GuiScreen; + +public class GuiWrongMinecraft extends GuiErrorScreen +{ + private WrongMinecraftVersionException wrongMC; + public GuiWrongMinecraft(WrongMinecraftVersionException wrongMC) + { + this.wrongMC = wrongMC; + } + @Override + public void func_73866_w_() + { + super.func_73866_w_(); + } + @Override + public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_) + { + this.func_73873_v_(); + int offset = 75; + 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, String.format("The mod listed below does not want to run in Minecraft version %s", Loader.instance().getMinecraftModContainer().getVersion()), this.field_73880_f / 2, offset, 0xFFFFFF); + offset+=5; + offset+=10; + this.func_73732_a(this.field_73886_k, String.format("%s (%s) wants Minecraft %s", wrongMC.mod.getName(), wrongMC.mod.getModId(), wrongMC.mod.acceptableMinecraftVersionRange()), 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 4f2584c04..82182cbab 100644 --- a/fml/common/cpw/mods/fml/common/DummyModContainer.java +++ b/fml/common/cpw/mods/fml/common/DummyModContainer.java @@ -9,6 +9,7 @@ import com.google.common.eventbus.EventBus; import cpw.mods.fml.common.versioning.ArtifactVersion; import cpw.mods.fml.common.versioning.DefaultArtifactVersion; +import cpw.mods.fml.common.versioning.VersionRange; public class DummyModContainer implements ModContainer { @@ -132,4 +133,9 @@ public class DummyModContainer implements ModContainer { return md.version; } + @Override + public VersionRange acceptableMinecraftVersionRange() + { + return Loader.instance().getMinecraftModContainer().getStaticVersionRange(); + } } diff --git a/fml/common/cpw/mods/fml/common/FMLModContainer.java b/fml/common/cpw/mods/fml/common/FMLModContainer.java index 7ce8d9922..d580ec18a 100644 --- a/fml/common/cpw/mods/fml/common/FMLModContainer.java +++ b/fml/common/cpw/mods/fml/common/FMLModContainer.java @@ -56,6 +56,8 @@ import cpw.mods.fml.common.event.FMLStateEvent; import cpw.mods.fml.common.network.FMLNetworkHandler; import cpw.mods.fml.common.versioning.ArtifactVersion; import cpw.mods.fml.common.versioning.DefaultArtifactVersion; +import cpw.mods.fml.common.versioning.VersionParser; +import cpw.mods.fml.common.versioning.VersionRange; public class FMLModContainer implements ModContainer { @@ -84,6 +86,7 @@ public class FMLModContainer implements ModContainer .build(); private static final BiMap, Class> modTypeAnnotations = modAnnotationTypes.inverse(); private String annotationDependencies; + private VersionRange minecraftAccepted; public FMLModContainer(String className, File modSource, Map modDescriptor) @@ -175,6 +178,16 @@ public class FMLModContainer implements ModContainer FMLLog.warning("Mod %s is missing the required element 'version' and no fallback can be found. Substituting '1.0'.", getModId()); modMetadata.version = internalVersion = "1.0"; } + + String mcVersionString = (String) descriptor.get("acceptedMinecraftVersions"); + if (!Strings.isNullOrEmpty(mcVersionString)) + { + minecraftAccepted = VersionParser.parseRange(mcVersionString); + } + else + { + minecraftAccepted = Loader.instance().getMinecraftModContainer().getStaticVersionRange(); + } } public Properties searchForVersionProperties() @@ -457,4 +470,10 @@ public class FMLModContainer implements ModContainer { return modMetadata.version; } + + @Override + public VersionRange acceptableMinecraftVersionRange() + { + return minecraftAccepted; + } } diff --git a/fml/common/cpw/mods/fml/common/InjectedModContainer.java b/fml/common/cpw/mods/fml/common/InjectedModContainer.java index 104b01b35..6af15dbe5 100644 --- a/fml/common/cpw/mods/fml/common/InjectedModContainer.java +++ b/fml/common/cpw/mods/fml/common/InjectedModContainer.java @@ -7,6 +7,7 @@ import java.util.Set; import com.google.common.eventbus.EventBus; import cpw.mods.fml.common.versioning.ArtifactVersion; +import cpw.mods.fml.common.versioning.VersionRange; public class InjectedModContainer implements ModContainer { @@ -110,4 +111,10 @@ public class InjectedModContainer implements ModContainer { return wrappedContainer.getDisplayVersion(); } + + @Override + public VersionRange acceptableMinecraftVersionRange() + { + return wrappedContainer.acceptableMinecraftVersionRange(); + } } diff --git a/fml/common/cpw/mods/fml/common/Loader.java b/fml/common/cpw/mods/fml/common/Loader.java index 4c6fc56bc..2a1036833 100644 --- a/fml/common/cpw/mods/fml/common/Loader.java +++ b/fml/common/cpw/mods/fml/common/Loader.java @@ -132,6 +132,7 @@ public class Loader private Exception capturedError; private File canonicalModsDir; private LoadController modController; + private MinecraftDummyContainer minecraft; private static File minecraftDir; private static List injectedContainers; @@ -167,6 +168,8 @@ public class Loader FMLLog.severe("This version of FML is built for Minecraft %s, we have detected Minecraft %s in your minecraft jar file", mccversion, actualMCVersion); throw new LoaderException(); } + + minecraft = new MinecraftDummyContainer(actualMCVersion); } /** @@ -187,6 +190,11 @@ public class Loader for (ModContainer mod : getActiveModList()) { + if (!mod.acceptableMinecraftVersionRange().containsVersion(minecraft.getProcessedVersion())) + { + FMLLog.severe("The mod %s does not wish to run in Minecraft version %s. You will have to remove it to play.", mod.getModId(), getMCVersionString()); + throw new WrongMinecraftVersionException(mod); + } Map names = Maps.uniqueIndex(mod.getRequirements(), new Function() { public String apply(ArtifactVersion v) @@ -194,8 +202,8 @@ public class Loader return v.getLabel(); } }); - Set missingMods = Sets.difference(names.keySet(), modVersions.keySet()); Set versionMissingMods = Sets.newHashSet(); + Set missingMods = Sets.difference(names.keySet(), modVersions.keySet()); if (!missingMods.isEmpty()) { FMLLog.severe("The mod %s (%s) requires mods %s to be available", mod.getModId(), mod.getName(), missingMods); @@ -707,4 +715,9 @@ public class Loader { return modController.isInState(state); } + + public MinecraftDummyContainer getMinecraftModContainer() + { + return minecraft; + } } diff --git a/fml/common/cpw/mods/fml/common/MinecraftDummyContainer.java b/fml/common/cpw/mods/fml/common/MinecraftDummyContainer.java new file mode 100644 index 000000000..fec8d0326 --- /dev/null +++ b/fml/common/cpw/mods/fml/common/MinecraftDummyContainer.java @@ -0,0 +1,26 @@ +package cpw.mods.fml.common; + +import cpw.mods.fml.common.versioning.ArtifactVersion; +import cpw.mods.fml.common.versioning.DefaultArtifactVersion; +import cpw.mods.fml.common.versioning.VersionParser; +import cpw.mods.fml.common.versioning.VersionRange; + +public class MinecraftDummyContainer extends DummyModContainer +{ + + private VersionRange staticRange; + public MinecraftDummyContainer(String actualMCVersion) + { + super(new ModMetadata()); + getMetadata().modId = "Minecraft"; + getMetadata().name = "Minecraft"; + getMetadata().version = actualMCVersion; + staticRange = VersionParser.parseRange("["+actualMCVersion+"]"); + } + + + public VersionRange getStaticVersionRange() + { + return staticRange; + } +} diff --git a/fml/common/cpw/mods/fml/common/Mod.java b/fml/common/cpw/mods/fml/common/Mod.java index b46c0cbb0..640c2d7b0 100644 --- a/fml/common/cpw/mods/fml/common/Mod.java +++ b/fml/common/cpw/mods/fml/common/Mod.java @@ -52,6 +52,13 @@ public @interface Mod */ boolean useMetadata() default false; + /** + * The acceptable range of minecraft versions that this mod will load and run in + * The default ("empty string") indicates that only the current minecraft version is acceptable. + * FML will refuse to run with an error if the minecraft version is not in this range across all mods. + * @return A version range as specified by the maven version range specification or the empty string + */ + String acceptedMinecraftVersions() default ""; /** * Mark the designated method as being called at the "pre-initialization" phase * @author cpw diff --git a/fml/common/cpw/mods/fml/common/ModContainer.java b/fml/common/cpw/mods/fml/common/ModContainer.java index 0a7f8896b..5df0e7f54 100644 --- a/fml/common/cpw/mods/fml/common/ModContainer.java +++ b/fml/common/cpw/mods/fml/common/ModContainer.java @@ -20,6 +20,7 @@ import java.util.Set; import com.google.common.eventbus.EventBus; import cpw.mods.fml.common.versioning.ArtifactVersion; +import cpw.mods.fml.common.versioning.VersionRange; /** * The container that wraps around mods in the system. @@ -126,4 +127,6 @@ public interface ModContainer boolean isNetworkMod(); String getDisplayVersion(); + + VersionRange acceptableMinecraftVersionRange(); } diff --git a/fml/common/cpw/mods/fml/common/WrongMinecraftVersionException.java b/fml/common/cpw/mods/fml/common/WrongMinecraftVersionException.java new file mode 100644 index 000000000..d3352ca8c --- /dev/null +++ b/fml/common/cpw/mods/fml/common/WrongMinecraftVersionException.java @@ -0,0 +1,13 @@ +package cpw.mods.fml.common; + +public class WrongMinecraftVersionException extends RuntimeException +{ + + public ModContainer mod; + + public WrongMinecraftVersionException(ModContainer mod) + { + this.mod = mod; + } + +} diff --git a/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java b/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java index 18cbf8490..298a930e5 100644 --- a/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java +++ b/fml/common/cpw/mods/fml/common/modloader/ModLoaderModContainer.java @@ -65,6 +65,7 @@ import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.common.registry.TickRegistry; import cpw.mods.fml.common.versioning.ArtifactVersion; import cpw.mods.fml.common.versioning.DefaultArtifactVersion; +import cpw.mods.fml.common.versioning.VersionRange; public class ModLoaderModContainer implements ModContainer { @@ -609,4 +610,10 @@ public class ModLoaderModContainer implements ModContainer { serverCommands .add(command); } + + @Override + public VersionRange acceptableMinecraftVersionRange() + { + return Loader.instance().getMinecraftModContainer().getStaticVersionRange(); + } } diff --git a/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java index 9cb6ba778..d844d5ad9 100644 --- a/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java +++ b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java @@ -22,7 +22,7 @@ import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler; import cpw.mods.fml.common.network.Player; import cpw.mods.fml.common.ModMetadata; -@Mod(modid="MockMod", name="Mock Mod",version="1.2.3", dependencies="after:FML@(3.0.184.0,3.0.184.1]", useMetadata=true) +@Mod(modid="MockMod", name="Mock Mod",version="1.2.3", dependencies="after:FML@[3.1.29,)", useMetadata=true,acceptedMinecraftVersions="[1.4,),[12w27a,)") @NetworkMod(channels={"MockMod"},clientSideRequired=true,packetHandler=MockMod.PacketHandler.class,clientPacketHandlerSpec= @SidedPacketHandler(packetHandler=TestClass.class,channels={"Fish"}),tinyPacketHandler=MockMod.PacketHandler.class) public class MockMod