diff --git a/src/main/java/net/minecraftforge/client/gui/ForgeGuiFactory.java b/src/main/java/net/minecraftforge/client/gui/ForgeGuiFactory.java index 1f3e18616..fcfee4e9e 100644 --- a/src/main/java/net/minecraftforge/client/gui/ForgeGuiFactory.java +++ b/src/main/java/net/minecraftforge/client/gui/ForgeGuiFactory.java @@ -6,42 +6,38 @@ package net.minecraftforge.client.gui; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.regex.Pattern; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.resources.I18n; import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.ForgeModContainer; +import net.minecraftforge.common.ForgeVersion; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.ConfigElement; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; import net.minecraftforge.fml.client.IModGuiFactory; -import net.minecraftforge.fml.client.IModGuiFactory.RuntimeOptionCategoryElement; -import net.minecraftforge.fml.client.IModGuiFactory.RuntimeOptionGuiHandler; import net.minecraftforge.fml.client.config.ConfigGuiType; import net.minecraftforge.fml.client.config.DummyConfigElement; import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement; -import net.minecraftforge.fml.client.config.GuiButtonExt; import net.minecraftforge.fml.client.config.GuiConfig; import net.minecraftforge.fml.client.config.GuiConfigEntries; import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry; import net.minecraftforge.fml.client.config.GuiConfigEntries.IConfigEntry; import net.minecraftforge.fml.client.config.GuiConfigEntries.SelectValueEntry; import net.minecraftforge.fml.client.config.GuiConfigEntries.BooleanEntry; -import net.minecraftforge.fml.client.config.HoverChecker; import net.minecraftforge.fml.client.config.IConfigElement; -import net.minecraftforge.fml.client.config.GuiConfigEntries.ListEntryBase; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import static net.minecraftforge.common.ForgeModContainer.VERSION_CHECK_CAT; /** * This is the base GuiConfig screen class that all the other Forge-specific config screens will be called from. @@ -109,6 +105,7 @@ public class ForgeGuiFactory implements IModGuiFactory List list = new ArrayList(); list.add(new DummyCategoryElement("forgeCfg", "forge.configgui.ctgy.forgeGeneralConfig", GeneralEntry.class)); list.add(new DummyCategoryElement("forgeChunkLoadingCfg", "forge.configgui.ctgy.forgeChunkLoadingConfig", ChunkLoaderEntry.class)); + list.add(new DummyCategoryElement("forgeVersionCheckCfg", "forge.configgui.ctgy.VersionCheckConfig", VersionCheckEntry.class)); return list; } @@ -166,6 +163,59 @@ public class ForgeGuiFactory implements IModGuiFactory } } + /** + * This custom list entry provides the Forge Version Checking Config entry on the Minecraft Forge Configuration screen. + * It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen. + */ + public static class VersionCheckEntry extends CategoryEntry + { + public VersionCheckEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) + { + super(owningScreen, owningEntryList, prop); + } + + @Override + protected GuiScreen buildChildScreen() + { + ConfigCategory cfg = ForgeModContainer.getConfig().getCategory(VERSION_CHECK_CAT); + Map values = new HashMap(cfg.getValues()); + values.remove("Global"); + + Property global = ForgeModContainer.getConfig().get(VERSION_CHECK_CAT, "Global", true); + + List props = new ArrayList(); + + for (ModContainer mod : ForgeVersion.gatherMods().keySet()) + { + values.remove(mod.getModId()); + props.add(ForgeModContainer.getConfig().get(VERSION_CHECK_CAT, mod.getModId(), true)); //Get or make the value in the config + } + props.addAll(values.values()); // Add any left overs from the config + Collections.sort(props, new Comparator() + { + @Override + public int compare(Property o1, Property o2) + { + return o1.getName().compareTo(o2.getName()); + } + }); + + List list = new ArrayList(); + list.add(new ConfigElement(global)); + for (Property prop : props) + { + list.add(new ConfigElement(prop)); + } + + // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent + // GuiConfig object's propertyList will also be refreshed to reflect the changes. + return new GuiConfig(this.owningScreen, + list, + this.owningScreen.modID, VERSION_CHECK_CAT, true, true, + GuiConfig.getAbridgedConfigPath(ForgeModContainer.getConfig().toString())); + } + } + /** * This custom list entry provides the Mod Overrides entry on the Forge Chunk Loading config screen. * It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen. diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index 710529534..3805d49f7 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -12,13 +12,14 @@ import static net.minecraftforge.common.ForgeVersion.revisionVersion; import static net.minecraftforge.common.config.Configuration.CATEGORY_GENERAL; import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; -import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.storage.SaveHandler; @@ -58,6 +59,7 @@ import net.minecraftforge.fml.common.network.NetworkRegistry; public class ForgeModContainer extends DummyModContainer implements WorldAccessContainer { + public static final String VERSION_CHECK_CAT = "version_checking"; public static int clumpingThreshold = 64; public static boolean removeErroringEntities = false; public static boolean removeErroringTileEntities = false; @@ -73,6 +75,13 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC public static boolean forgeLightPipelineEnabled = true; private static Configuration config; + private static ForgeModContainer INSTANCE; + public static ForgeModContainer getInstance() + { + return INSTANCE; + } + + private URL updateJSONUrl = null; public ForgeModContainer() { @@ -82,7 +91,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC meta.name = "Minecraft Forge"; meta.version = String.format("%d.%d.%d.%d", majorVersion, minorVersion, revisionVersion, buildVersion); meta.credits = "Made possible with help from many people"; - meta.authorList = Arrays.asList("LexManos", "Eloraam", "Spacetoad"); + meta.authorList = Arrays.asList("LexManos", "Cpw"); meta.description = "Minecraft Forge is a common open source API allowing a broad range of mods " + "to work cooperatively together. It allows many mods to be created without " + "them editing the main Minecraft code."; @@ -90,12 +99,17 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC meta.updateUrl = "http://MinecraftForge.net/forum/index.php/topic,5.0.html"; meta.screenshots = new String[0]; meta.logoFile = "/forge_logo.png"; + try { + updateJSONUrl = new URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json"); + } catch (MalformedURLException e) {} config = null; File cfgFile = new File(Loader.instance().getConfigDir(), "forge.cfg"); config = new Configuration(cfgFile); syncConfig(true); + + INSTANCE = this; } @Override @@ -230,6 +244,12 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder); + propOrder = new ArrayList(); + prop = config.get(VERSION_CHECK_CAT, "Global", true, "Enable the entire mod update check system. This only applies to mods using the Forge system."); + propOrder.add("Global"); + + config.setCategoryPropertyOrder(VERSION_CHECK_CAT, propOrder); + if (config.hasChanged()) { config.save(); @@ -254,6 +274,10 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC ForgeChunkManager.syncConfigDefaults(); ForgeChunkManager.loadConfiguration(); } + else if (VERSION_CHECK_CAT.equals(event.configID)) + { + syncConfig(false); + } } } @@ -396,4 +420,10 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC Certificate[] certificates = getClass().getProtectionDomain().getCodeSource().getCertificates(); return certificates != null ? certificates[0] : null; } + + @Override + public URL getUpdateUrl() + { + return updateJSONUrl; + } } diff --git a/src/main/java/net/minecraftforge/common/ForgeVersion.java b/src/main/java/net/minecraftforge/common/ForgeVersion.java index bc5db2f84..71d594c54 100644 --- a/src/main/java/net/minecraftforge/common/ForgeVersion.java +++ b/src/main/java/net/minecraftforge/common/ForgeVersion.java @@ -8,13 +8,25 @@ import static net.minecraftforge.common.ForgeVersion.Status.*; import java.io.InputStream; import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.logging.log4j.Level; import com.google.common.io.ByteStreams; import com.google.gson.Gson; -import net.minecraftforge.fml.common.versioning.ArtifactVersion; -import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import net.minecraftforge.fml.common.FMLLog; +import net.minecraftforge.fml.common.InjectedModContainer; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.common.versioning.ComparableVersion; public class ForgeVersion { @@ -55,12 +67,13 @@ public class ForgeVersion public static Status getStatus() { - return status; + return getResult(ForgeModContainer.getInstance()).status; } public static String getTarget() { - return target; + CheckResult res = getResult(ForgeModContainer.getInstance()); + return res.target != null ? res.target.toString() : null; } public static String getVersion() @@ -79,32 +92,75 @@ public class ForgeVersion BETA_OUTDATED } + public static class CheckResult + { + public final Status status; + public final ComparableVersion target; + public final Map changes; + public final String url; + + private CheckResult(Status status, ComparableVersion target, Map changes, String url) + { + this.status = status; + this.target = target; + this.changes = changes == null ? null : Collections.unmodifiableMap(changes); + this.url = url; + } + } + public static void startVersionCheck() { new Thread("Forge Version Check") { - @SuppressWarnings("unchecked") @Override public void run() + { + if (!ForgeModContainer.getConfig().get(ForgeModContainer.VERSION_CHECK_CAT, "Global", true).getBoolean()) + { + FMLLog.log("ForgeVersionCheck", Level.INFO, "Global Forge version check system disabeld, no futher processing."); + return; + } + + for (Entry entry : gatherMods().entrySet()) + { + ModContainer mod = entry.getKey(); + if (ForgeModContainer.getConfig().get(ForgeModContainer.VERSION_CHECK_CAT, mod.getModId(), true).getBoolean()) + { + process(mod, entry.getValue()); + } + else + { + FMLLog.log("ForgeVersionCheck", Level.INFO, "[%s] Skipped version check", mod.getModId()); + } + } + } + + private void process(ModContainer mod, URL url) { try { - URL url = new URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json"); + FMLLog.log("ForgeVersionCheck", Level.INFO, "[%s] Starting version check at %s", mod.getModId(), url.toString()); + Status status = PENDING; + ComparableVersion target = null; + InputStream con = url.openStream(); String data = new String(ByteStreams.toByteArray(con)); con.close(); + FMLLog.log("ForgeVersionCheck", Level.DEBUG, "[%s] Received version check data:\n%s", mod.getModId(), data); + + Map json = new Gson().fromJson(data, Map.class); - //String homepage = (String)json.get("homepage"); Map promos = (Map)json.get("promos"); + String display_url = (String)json.get("homepage"); String rec = promos.get(MinecraftForge.MC_VERSION + "-recommended"); String lat = promos.get(MinecraftForge.MC_VERSION + "-latest"); - ArtifactVersion current = new DefaultArtifactVersion(getVersion()); + ComparableVersion current = new ComparableVersion(mod.getVersion()); if (rec != null) { - ArtifactVersion recommended = new DefaultArtifactVersion(rec); + ComparableVersion recommended = new ComparableVersion(rec); int diff = recommended.compareTo(current); if (diff == 0) @@ -114,39 +170,95 @@ public class ForgeVersion status = AHEAD; if (lat != null) { - if (current.compareTo(new DefaultArtifactVersion(lat)) < 0) + ComparableVersion latest = new ComparableVersion(lat); + if (current.compareTo(latest) < 0) { status = OUTDATED; - target = lat; + target = latest; } } } else { status = OUTDATED; - target = rec; + target = recommended; } } else if (lat != null) { - if (current.compareTo(new DefaultArtifactVersion(lat)) < 0) + ComparableVersion latest = new ComparableVersion(lat); + if (current.compareTo(latest) < 0) { status = BETA_OUTDATED; - target = lat; + target = latest; } else status = BETA; } else status = BETA; + + FMLLog.log("ForgeVersionCheck", Level.INFO, "[%s] Found status: %s Target: %s", mod.getModId(), status, target); + + Map changes = new LinkedHashMap(); + Map tmp = (Map)json.get(MinecraftForge.MC_VERSION); + if (tmp != null) + { + List ordered = new ArrayList(); + for (String key : tmp.keySet()) + { + ComparableVersion ver = new ComparableVersion(key); + if (ver.compareTo(current) > 0 && (target == null || ver.compareTo(target) < 1)) + { + ordered.add(ver); + } + } + Collections.sort(ordered); + + for (ComparableVersion ver : ordered) + { + changes.put(ver, tmp.get(ver.toString())); + } + } + if (mod instanceof InjectedModContainer) + mod = ((InjectedModContainer)mod).wrappedContainer; + results.put(mod, new CheckResult(status, target, changes, display_url)); } catch (Exception e) { - e.printStackTrace(); + FMLLog.log("ForgeVersionCheck", Level.DEBUG, e, "Failed to process update information"); status = FAILED; } } }.start(); } + + // Gather a list of mods that have opted in to this update system by providing a URL. + // Small hack needed to support a interface change until we force a recompile. + public static Map gatherMods() + { + Map ret = new HashMap(); + for (ModContainer mod : Loader.instance().getActiveModList()) + { + URL url = null; + try { + url = mod.getUpdateUrl(); + } catch (AbstractMethodError abs) { } //TODO: Remove this in 1.8.8+? + if (url != null) + ret.put(mod, url); + } + return ret; + } + + private static Map results = new ConcurrentHashMap(); + private static final CheckResult PENDING_CHECK = new CheckResult(PENDING, null, null, null); + + public static CheckResult getResult(ModContainer mod) + { + if (mod instanceof InjectedModContainer) + mod = ((InjectedModContainer)mod).wrappedContainer; + CheckResult ret = results.get(mod); + return ret == null ? PENDING_CHECK : ret; + } } diff --git a/src/main/java/net/minecraftforge/fml/client/GuiModList.java b/src/main/java/net/minecraftforge/fml/client/GuiModList.java index ba0085f0a..720e3e087 100644 --- a/src/main/java/net/minecraftforge/fml/client/GuiModList.java +++ b/src/main/java/net/minecraftforge/fml/client/GuiModList.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map.Entry; import javax.imageio.ImageIO; @@ -43,10 +44,14 @@ import net.minecraft.util.IChatComponent; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StringUtils; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.ForgeVersion; +import net.minecraftforge.common.ForgeVersion.CheckResult; +import net.minecraftforge.common.ForgeVersion.Status; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.ModContainer.Disableable; +import net.minecraftforge.fml.common.versioning.ComparableVersion; import static net.minecraft.util.EnumChatFormatting.*; import org.apache.logging.log4j.Level; @@ -343,7 +348,7 @@ public class GuiModList extends GuiScreen ResourceLocation logoPath = null; Dimension logoDims = new Dimension(0, 0); List lines = new ArrayList(); - //CheckResult vercheck = ForgeVersion.getResult(selectedMod); + CheckResult vercheck = ForgeVersion.getResult(selectedMod); String logoFile = selectedMod.getMetadata().logoFile; if (!logoFile.isEmpty()) @@ -408,8 +413,8 @@ public class GuiModList extends GuiScreen else lines.add("Child mods: " + selectedMod.getMetadata().getChildModList()); - //if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) - // lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url)); + if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) + lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url)); lines.add(null); lines.add(selectedMod.getMetadata().description); @@ -419,15 +424,15 @@ public class GuiModList extends GuiScreen lines.add(WHITE + selectedMod.getName()); lines.add(WHITE + "Version: " + selectedMod.getVersion()); lines.add(WHITE + "Mod State: " + Loader.instance().getModState(selectedMod)); - //if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) - // lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url)); + if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) + lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url)); lines.add(null); lines.add(RED + "No mod information found"); lines.add(RED + "Ask your mod author to provide a mod mcmod.info file"); } - /*if ((vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) && vercheck.changes.size() > 0) + if ((vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) && vercheck.changes.size() > 0) { lines.add(null); lines.add("Changes:"); @@ -437,7 +442,7 @@ public class GuiModList extends GuiScreen lines.add(entry.getValue()); lines.add(null); } - }*/ + } modInfo = new Info(this.width - this.listWidth - 30, lines, logoPath, logoDims); } diff --git a/src/main/java/net/minecraftforge/fml/client/GuiSlotModList.java b/src/main/java/net/minecraftforge/fml/client/GuiSlotModList.java index 1d47fea2f..90198e1d2 100644 --- a/src/main/java/net/minecraftforge/fml/client/GuiSlotModList.java +++ b/src/main/java/net/minecraftforge/fml/client/GuiSlotModList.java @@ -17,6 +17,8 @@ import java.util.ArrayList; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.Tessellator; import net.minecraft.util.StringUtils; +import net.minecraftforge.common.ForgeVersion; +import net.minecraftforge.common.ForgeVersion.CheckResult; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.LoaderState.ModState; import net.minecraftforge.fml.common.ModContainer; @@ -79,7 +81,7 @@ public class GuiSlotModList extends GuiScrollingList String name = StringUtils.stripControlCodes(mc.getName()); String version = StringUtils.stripControlCodes(mc.getDisplayVersion()); FontRenderer font = this.parent.getFontRenderer(); - //CheckResult vercheck = ForgeVersion.getResult(mc); + CheckResult vercheck = ForgeVersion.getResult(mc); if (Loader.instance().getModState(mc) == ModState.DISABLED) { @@ -93,7 +95,7 @@ public class GuiSlotModList extends GuiScrollingList font.drawString(font.trimStringToWidth(version, listWidth - 10), this.left + 3 , top + 12, 0xCCCCCC); font.drawString(font.trimStringToWidth(mc.getMetadata() != null ? mc.getMetadata().getChildModCountString() : "Metadata not found", listWidth - 10), this.left + 3 , top + 22, 0xCCCCCC); - /*switch(vercheck.status) //TODO: Change to icons? + switch(vercheck.status) //TODO: Change to icons? { case BETA_OUTDATED: case OUTDATED: @@ -105,8 +107,7 @@ public class GuiSlotModList extends GuiScrollingList case PENDING: case UP_TO_DATE: break; - }*/ + } } } - } diff --git a/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java b/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java index 88b171c36..d9e11b3af 100644 --- a/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/DummyModContainer.java @@ -13,6 +13,7 @@ package net.minecraftforge.fml.common; import java.io.File; +import java.net.URL; import java.security.cert.Certificate; import java.util.Collections; import java.util.List; @@ -206,4 +207,10 @@ public class DummyModContainer implements ModContainer { return true; } + + @Override + public URL getUpdateUrl() + { + return null; + } } diff --git a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java index 60ae2c6b2..05ea50593 100644 --- a/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/FMLModContainer.java @@ -18,6 +18,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URL; import java.security.cert.Certificate; import java.util.Arrays; import java.util.List; @@ -87,6 +89,7 @@ public class FMLModContainer implements ModContainer private ListMultimap,Method> eventMethods; private Map customModProperties; private ModCandidate candidate; + private URL updateJSONUrl; public FMLModContainer(String className, ModCandidate container, Map modDescriptor) { @@ -215,6 +218,19 @@ public class FMLModContainer implements ModContainer { minecraftAccepted = Loader.instance().getMinecraftModContainer().getStaticVersionRange(); } + + String jsonURL = (String)descriptor.get("updateJSON"); + if (!Strings.isNullOrEmpty(jsonURL)) + { + try + { + this.updateJSONUrl = new URL(jsonURL); + } + catch (MalformedURLException e) + { + FMLLog.log(getModId(), Level.DEBUG, "Specified json URL invalid: %s", jsonURL); + } + } } public Properties searchForVersionProperties() @@ -663,4 +679,10 @@ public class FMLModContainer implements ModContainer return true; } + + @Override + public URL getUpdateUrl() + { + return updateJSONUrl; + } } diff --git a/src/main/java/net/minecraftforge/fml/common/InjectedModContainer.java b/src/main/java/net/minecraftforge/fml/common/InjectedModContainer.java index af91db153..783c28b2d 100644 --- a/src/main/java/net/minecraftforge/fml/common/InjectedModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/InjectedModContainer.java @@ -13,6 +13,7 @@ package net.minecraftforge.fml.common; import java.io.File; +import java.net.URL; import java.security.cert.Certificate; import java.util.List; import java.util.Map; @@ -207,4 +208,10 @@ public class InjectedModContainer implements ModContainer { return true; } + + @Override + public URL getUpdateUrl() + { + return wrappedContainer.getUpdateUrl(); + } } diff --git a/src/main/java/net/minecraftforge/fml/common/Mod.java b/src/main/java/net/minecraftforge/fml/common/Mod.java index a2ad32063..7513fdf68 100644 --- a/src/main/java/net/minecraftforge/fml/common/Mod.java +++ b/src/main/java/net/minecraftforge/fml/common/Mod.java @@ -157,18 +157,18 @@ public @interface Mod * @return The language the mod is authored in */ String modLanguage() default "java"; - + /** * The language adapter to be used to load this mod. This overrides the value of modLanguage. The class must have a * public zero variable constructor and implement {@link ILanguageAdapter} just like the Java and Scala adapters. - * + * * A class with an invalid constructor or that doesn't implement {@link ILanguageAdapter} will throw an exception and * halt loading. - * + * * @return The full class name of the language adapter */ String modLanguageAdapter() default ""; - + /** * NOT YET IMPLEMENTED.
* An optional ASM hook class, that can be used to apply ASM to classes loaded from this mod. It is also given @@ -198,6 +198,14 @@ public @interface Mod * @return The name of a class implementing {@link IModGuiFactory} */ String guiFactory() default ""; + + /** + * An optional URL to a JSON file that will be checked once per launch to determine if there is an updated + * version of this mod and notify the end user. For more information see ForgeVersion. + * @return URL to update metadata json + */ + String updateJSON() default ""; + /** * A list of custom properties for this mod. Completely up to the mod author if/when they * want to put anything in here. diff --git a/src/main/java/net/minecraftforge/fml/common/ModContainer.java b/src/main/java/net/minecraftforge/fml/common/ModContainer.java index dde8ddef5..883f8b95c 100644 --- a/src/main/java/net/minecraftforge/fml/common/ModContainer.java +++ b/src/main/java/net/minecraftforge/fml/common/ModContainer.java @@ -13,6 +13,7 @@ package net.minecraftforge.fml.common; import java.io.File; +import java.net.URL; import java.security.cert.Certificate; import java.util.List; import java.util.Map; @@ -149,4 +150,6 @@ public interface ModContainer List getOwnedPackages(); boolean shouldLoadInEnvironment(); + + URL getUpdateUrl(); } diff --git a/src/main/java/net/minecraftforge/fml/common/ModMetadata.java b/src/main/java/net/minecraftforge/fml/common/ModMetadata.java index add4b37ae..f0173ca17 100644 --- a/src/main/java/net/minecraftforge/fml/common/ModMetadata.java +++ b/src/main/java/net/minecraftforge/fml/common/ModMetadata.java @@ -35,7 +35,12 @@ public class ModMetadata public String description = ""; public String url = ""; + @Deprecated //Never really used for anything and format is undefined. See updateJSON for replacement. public String updateUrl = ""; + /** + * URL to update json file. Format is defined here: https://gist.github.com/LexManos/7aacb9aa991330523884 + */ + public String updateJSON = ""; public String logoFile = ""; public String version = "";