Skip to content

Commit d790008

Browse files
committedNov 4, 2015
Introduce a new centralized version checking system.
Using the @mod annotation mods can opt-in to a centrally controlled update system. This is PURELY a notification system and will NOT automatically download any updates. The End User can control which mods check for updates and disabel the system entirely using the Forge Config and GUI. Format for the json the URL must point to is described here: https://gist.github.com/LexManos/7aacb9aa991330523884
1 parent 479c7f8 commit d790008

File tree

11 files changed

+291
-41
lines changed

11 files changed

+291
-41
lines changed
 

‎src/main/java/net/minecraftforge/client/gui/ForgeGuiFactory.java

+59-9
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,38 @@
66
package net.minecraftforge.client.gui;
77

88
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.Comparator;
11+
import java.util.HashMap;
912
import java.util.List;
1013
import java.util.Map;
1114
import java.util.Set;
1215
import java.util.TreeMap;
13-
import java.util.regex.Pattern;
1416

1517
import net.minecraft.client.Minecraft;
1618
import net.minecraft.client.gui.GuiScreen;
17-
import net.minecraft.client.renderer.Tessellator;
1819
import net.minecraft.client.resources.I18n;
1920
import net.minecraftforge.common.ForgeChunkManager;
2021
import net.minecraftforge.common.ForgeModContainer;
22+
import net.minecraftforge.common.ForgeVersion;
2123
import net.minecraftforge.common.config.ConfigCategory;
2224
import net.minecraftforge.common.config.ConfigElement;
2325
import net.minecraftforge.common.config.Configuration;
2426
import net.minecraftforge.common.config.Property;
2527
import net.minecraftforge.fml.client.IModGuiFactory;
26-
import net.minecraftforge.fml.client.IModGuiFactory.RuntimeOptionCategoryElement;
27-
import net.minecraftforge.fml.client.IModGuiFactory.RuntimeOptionGuiHandler;
2828
import net.minecraftforge.fml.client.config.ConfigGuiType;
2929
import net.minecraftforge.fml.client.config.DummyConfigElement;
3030
import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement;
31-
import net.minecraftforge.fml.client.config.GuiButtonExt;
3231
import net.minecraftforge.fml.client.config.GuiConfig;
3332
import net.minecraftforge.fml.client.config.GuiConfigEntries;
3433
import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry;
3534
import net.minecraftforge.fml.client.config.GuiConfigEntries.IConfigEntry;
3635
import net.minecraftforge.fml.client.config.GuiConfigEntries.SelectValueEntry;
3736
import net.minecraftforge.fml.client.config.GuiConfigEntries.BooleanEntry;
38-
import net.minecraftforge.fml.client.config.HoverChecker;
3937
import net.minecraftforge.fml.client.config.IConfigElement;
40-
import net.minecraftforge.fml.client.config.GuiConfigEntries.ListEntryBase;
4138
import net.minecraftforge.fml.common.Loader;
4239
import net.minecraftforge.fml.common.ModContainer;
43-
import net.minecraftforge.fml.relauncher.Side;
44-
import net.minecraftforge.fml.relauncher.SideOnly;
40+
import static net.minecraftforge.common.ForgeModContainer.VERSION_CHECK_CAT;
4541

4642
/**
4743
* This is the base GuiConfig screen class that all the other Forge-specific config screens will be called from.
@@ -109,6 +105,7 @@ private static List<IConfigElement> getConfigElements()
109105
List<IConfigElement> list = new ArrayList<IConfigElement>();
110106
list.add(new DummyCategoryElement("forgeCfg", "forge.configgui.ctgy.forgeGeneralConfig", GeneralEntry.class));
111107
list.add(new DummyCategoryElement("forgeChunkLoadingCfg", "forge.configgui.ctgy.forgeChunkLoadingConfig", ChunkLoaderEntry.class));
108+
list.add(new DummyCategoryElement("forgeVersionCheckCfg", "forge.configgui.ctgy.VersionCheckConfig", VersionCheckEntry.class));
112109
return list;
113110
}
114111

@@ -166,6 +163,59 @@ protected GuiScreen buildChildScreen()
166163
}
167164
}
168165

166+
/**
167+
* This custom list entry provides the Forge Version Checking Config entry on the Minecraft Forge Configuration screen.
168+
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.
169+
*/
170+
public static class VersionCheckEntry extends CategoryEntry
171+
{
172+
public VersionCheckEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
173+
{
174+
super(owningScreen, owningEntryList, prop);
175+
}
176+
177+
@Override
178+
protected GuiScreen buildChildScreen()
179+
{
180+
ConfigCategory cfg = ForgeModContainer.getConfig().getCategory(VERSION_CHECK_CAT);
181+
Map<String, Property> values = new HashMap<String, Property>(cfg.getValues());
182+
values.remove("Global");
183+
184+
Property global = ForgeModContainer.getConfig().get(VERSION_CHECK_CAT, "Global", true);
185+
186+
List<Property> props = new ArrayList<Property>();
187+
188+
for (ModContainer mod : ForgeVersion.gatherMods().keySet())
189+
{
190+
values.remove(mod.getModId());
191+
props.add(ForgeModContainer.getConfig().get(VERSION_CHECK_CAT, mod.getModId(), true)); //Get or make the value in the config
192+
}
193+
props.addAll(values.values()); // Add any left overs from the config
194+
Collections.sort(props, new Comparator<Property>()
195+
{
196+
@Override
197+
public int compare(Property o1, Property o2)
198+
{
199+
return o1.getName().compareTo(o2.getName());
200+
}
201+
});
202+
203+
List<IConfigElement> list = new ArrayList<IConfigElement>();
204+
list.add(new ConfigElement(global));
205+
for (Property prop : props)
206+
{
207+
list.add(new ConfigElement(prop));
208+
}
209+
210+
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. The parent
211+
// GuiConfig object's propertyList will also be refreshed to reflect the changes.
212+
return new GuiConfig(this.owningScreen,
213+
list,
214+
this.owningScreen.modID, VERSION_CHECK_CAT, true, true,
215+
GuiConfig.getAbridgedConfigPath(ForgeModContainer.getConfig().toString()));
216+
}
217+
}
218+
169219
/**
170220
* This custom list entry provides the Mod Overrides entry on the Forge Chunk Loading config screen.
171221
* It extends the base Category entry class and defines the IConfigElement objects that will be used to build the child screen.

‎src/main/java/net/minecraftforge/common/ForgeModContainer.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
import static net.minecraftforge.common.config.Configuration.CATEGORY_GENERAL;
1313

1414
import java.io.File;
15+
import java.net.MalformedURLException;
16+
import java.net.URL;
1517
import java.security.cert.Certificate;
1618
import java.util.ArrayList;
1719
import java.util.Arrays;
1820
import java.util.List;
1921
import java.util.Map;
2022

21-
import net.minecraft.init.Blocks;
2223
import net.minecraft.nbt.NBTBase;
2324
import net.minecraft.nbt.NBTTagCompound;
2425
import net.minecraft.world.storage.SaveHandler;
@@ -58,6 +59,7 @@
5859

5960
public class ForgeModContainer extends DummyModContainer implements WorldAccessContainer
6061
{
62+
public static final String VERSION_CHECK_CAT = "version_checking";
6163
public static int clumpingThreshold = 64;
6264
public static boolean removeErroringEntities = false;
6365
public static boolean removeErroringTileEntities = false;
@@ -73,6 +75,13 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
7375
public static boolean forgeLightPipelineEnabled = true;
7476

7577
private static Configuration config;
78+
private static ForgeModContainer INSTANCE;
79+
public static ForgeModContainer getInstance()
80+
{
81+
return INSTANCE;
82+
}
83+
84+
private URL updateJSONUrl = null;
7685

7786
public ForgeModContainer()
7887
{
@@ -82,20 +91,25 @@ public ForgeModContainer()
8291
meta.name = "Minecraft Forge";
8392
meta.version = String.format("%d.%d.%d.%d", majorVersion, minorVersion, revisionVersion, buildVersion);
8493
meta.credits = "Made possible with help from many people";
85-
meta.authorList = Arrays.asList("LexManos", "Eloraam", "Spacetoad");
94+
meta.authorList = Arrays.asList("LexManos", "Cpw");
8695
meta.description = "Minecraft Forge is a common open source API allowing a broad range of mods " +
8796
"to work cooperatively together. It allows many mods to be created without " +
8897
"them editing the main Minecraft code.";
8998
meta.url = "http://MinecraftForge.net";
9099
meta.updateUrl = "http://MinecraftForge.net/forum/index.php/topic,5.0.html";
91100
meta.screenshots = new String[0];
92101
meta.logoFile = "/forge_logo.png";
102+
try {
103+
updateJSONUrl = new URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json");
104+
} catch (MalformedURLException e) {}
93105

94106
config = null;
95107
File cfgFile = new File(Loader.instance().getConfigDir(), "forge.cfg");
96108
config = new Configuration(cfgFile);
97109

98110
syncConfig(true);
111+
112+
INSTANCE = this;
99113
}
100114

101115
@Override
@@ -230,6 +244,12 @@ private static void syncConfig(boolean load)
230244

231245
config.setCategoryPropertyOrder(CATEGORY_GENERAL, propOrder);
232246

247+
propOrder = new ArrayList<String>();
248+
prop = config.get(VERSION_CHECK_CAT, "Global", true, "Enable the entire mod update check system. This only applies to mods using the Forge system.");
249+
propOrder.add("Global");
250+
251+
config.setCategoryPropertyOrder(VERSION_CHECK_CAT, propOrder);
252+
233253
if (config.hasChanged())
234254
{
235255
config.save();
@@ -254,6 +274,10 @@ else if ("chunkLoader".equals(event.configID))
254274
ForgeChunkManager.syncConfigDefaults();
255275
ForgeChunkManager.loadConfiguration();
256276
}
277+
else if (VERSION_CHECK_CAT.equals(event.configID))
278+
{
279+
syncConfig(false);
280+
}
257281
}
258282
}
259283

@@ -396,4 +420,10 @@ public Certificate getSigningCertificate()
396420
Certificate[] certificates = getClass().getProtectionDomain().getCodeSource().getCertificates();
397421
return certificates != null ? certificates[0] : null;
398422
}
423+
424+
@Override
425+
public URL getUpdateUrl()
426+
{
427+
return updateJSONUrl;
428+
}
399429
}

‎src/main/java/net/minecraftforge/common/ForgeVersion.java

+127-15
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,25 @@
88

99
import java.io.InputStream;
1010
import java.net.URL;
11+
import java.util.ArrayList;
12+
import java.util.Collections;
13+
import java.util.HashMap;
14+
import java.util.LinkedHashMap;
15+
import java.util.List;
1116
import java.util.Map;
17+
import java.util.Map.Entry;
18+
import java.util.concurrent.ConcurrentHashMap;
19+
20+
import org.apache.logging.log4j.Level;
1221

1322
import com.google.common.io.ByteStreams;
1423
import com.google.gson.Gson;
1524

16-
import net.minecraftforge.fml.common.versioning.ArtifactVersion;
17-
import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion;
25+
import net.minecraftforge.fml.common.FMLLog;
26+
import net.minecraftforge.fml.common.InjectedModContainer;
27+
import net.minecraftforge.fml.common.Loader;
28+
import net.minecraftforge.fml.common.ModContainer;
29+
import net.minecraftforge.fml.common.versioning.ComparableVersion;
1830

1931
public class ForgeVersion
2032
{
@@ -55,12 +67,13 @@ public static int getBuildVersion()
5567

5668
public static Status getStatus()
5769
{
58-
return status;
70+
return getResult(ForgeModContainer.getInstance()).status;
5971
}
6072

6173
public static String getTarget()
6274
{
63-
return target;
75+
CheckResult res = getResult(ForgeModContainer.getInstance());
76+
return res.target != null ? res.target.toString() : null;
6477
}
6578

6679
public static String getVersion()
@@ -79,32 +92,75 @@ public static enum Status
7992
BETA_OUTDATED
8093
}
8194

95+
public static class CheckResult
96+
{
97+
public final Status status;
98+
public final ComparableVersion target;
99+
public final Map<ComparableVersion, String> changes;
100+
public final String url;
101+
102+
private CheckResult(Status status, ComparableVersion target, Map<ComparableVersion, String> changes, String url)
103+
{
104+
this.status = status;
105+
this.target = target;
106+
this.changes = changes == null ? null : Collections.unmodifiableMap(changes);
107+
this.url = url;
108+
}
109+
}
110+
82111
public static void startVersionCheck()
83112
{
84113
new Thread("Forge Version Check")
85114
{
86-
@SuppressWarnings("unchecked")
87115
@Override
88116
public void run()
117+
{
118+
if (!ForgeModContainer.getConfig().get(ForgeModContainer.VERSION_CHECK_CAT, "Global", true).getBoolean())
119+
{
120+
FMLLog.log("ForgeVersionCheck", Level.INFO, "Global Forge version check system disabeld, no futher processing.");
121+
return;
122+
}
123+
124+
for (Entry<ModContainer, URL> entry : gatherMods().entrySet())
125+
{
126+
ModContainer mod = entry.getKey();
127+
if (ForgeModContainer.getConfig().get(ForgeModContainer.VERSION_CHECK_CAT, mod.getModId(), true).getBoolean())
128+
{
129+
process(mod, entry.getValue());
130+
}
131+
else
132+
{
133+
FMLLog.log("ForgeVersionCheck", Level.INFO, "[%s] Skipped version check", mod.getModId());
134+
}
135+
}
136+
}
137+
138+
private void process(ModContainer mod, URL url)
89139
{
90140
try
91141
{
92-
URL url = new URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json");
142+
FMLLog.log("ForgeVersionCheck", Level.INFO, "[%s] Starting version check at %s", mod.getModId(), url.toString());
143+
Status status = PENDING;
144+
ComparableVersion target = null;
145+
93146
InputStream con = url.openStream();
94147
String data = new String(ByteStreams.toByteArray(con));
95148
con.close();
96149

150+
FMLLog.log("ForgeVersionCheck", Level.DEBUG, "[%s] Received version check data:\n%s", mod.getModId(), data);
151+
152+
97153
Map<String, Object> json = new Gson().fromJson(data, Map.class);
98-
//String homepage = (String)json.get("homepage");
99154
Map<String, String> promos = (Map<String, String>)json.get("promos");
155+
String display_url = (String)json.get("homepage");
100156

101157
String rec = promos.get(MinecraftForge.MC_VERSION + "-recommended");
102158
String lat = promos.get(MinecraftForge.MC_VERSION + "-latest");
103-
ArtifactVersion current = new DefaultArtifactVersion(getVersion());
159+
ComparableVersion current = new ComparableVersion(mod.getVersion());
104160

105161
if (rec != null)
106162
{
107-
ArtifactVersion recommended = new DefaultArtifactVersion(rec);
163+
ComparableVersion recommended = new ComparableVersion(rec);
108164
int diff = recommended.compareTo(current);
109165

110166
if (diff == 0)
@@ -114,39 +170,95 @@ else if (diff < 0)
114170
status = AHEAD;
115171
if (lat != null)
116172
{
117-
if (current.compareTo(new DefaultArtifactVersion(lat)) < 0)
173+
ComparableVersion latest = new ComparableVersion(lat);
174+
if (current.compareTo(latest) < 0)
118175
{
119176
status = OUTDATED;
120-
target = lat;
177+
target = latest;
121178
}
122179
}
123180
}
124181
else
125182
{
126183
status = OUTDATED;
127-
target = rec;
184+
target = recommended;
128185
}
129186
}
130187
else if (lat != null)
131188
{
132-
if (current.compareTo(new DefaultArtifactVersion(lat)) < 0)
189+
ComparableVersion latest = new ComparableVersion(lat);
190+
if (current.compareTo(latest) < 0)
133191
{
134192
status = BETA_OUTDATED;
135-
target = lat;
193+
target = latest;
136194
}
137195
else
138196
status = BETA;
139197
}
140198
else
141199
status = BETA;
200+
201+
FMLLog.log("ForgeVersionCheck", Level.INFO, "[%s] Found status: %s Target: %s", mod.getModId(), status, target);
202+
203+
Map<ComparableVersion, String> changes = new LinkedHashMap<ComparableVersion, String>();
204+
Map<String, String> tmp = (Map<String, String>)json.get(MinecraftForge.MC_VERSION);
205+
if (tmp != null)
206+
{
207+
List<ComparableVersion> ordered = new ArrayList<ComparableVersion>();
208+
for (String key : tmp.keySet())
209+
{
210+
ComparableVersion ver = new ComparableVersion(key);
211+
if (ver.compareTo(current) > 0 && (target == null || ver.compareTo(target) < 1))
212+
{
213+
ordered.add(ver);
214+
}
215+
}
216+
Collections.sort(ordered);
217+
218+
for (ComparableVersion ver : ordered)
219+
{
220+
changes.put(ver, tmp.get(ver.toString()));
221+
}
222+
}
223+
if (mod instanceof InjectedModContainer)
224+
mod = ((InjectedModContainer)mod).wrappedContainer;
225+
results.put(mod, new CheckResult(status, target, changes, display_url));
142226
}
143227
catch (Exception e)
144228
{
145-
e.printStackTrace();
229+
FMLLog.log("ForgeVersionCheck", Level.DEBUG, e, "Failed to process update information");
146230
status = FAILED;
147231
}
148232
}
149233
}.start();
150234
}
235+
236+
// Gather a list of mods that have opted in to this update system by providing a URL.
237+
// Small hack needed to support a interface change until we force a recompile.
238+
public static Map<ModContainer, URL> gatherMods()
239+
{
240+
Map<ModContainer, URL> ret = new HashMap<ModContainer, URL>();
241+
for (ModContainer mod : Loader.instance().getActiveModList())
242+
{
243+
URL url = null;
244+
try {
245+
url = mod.getUpdateUrl();
246+
} catch (AbstractMethodError abs) { } //TODO: Remove this in 1.8.8+?
247+
if (url != null)
248+
ret.put(mod, url);
249+
}
250+
return ret;
251+
}
252+
253+
private static Map<ModContainer, CheckResult> results = new ConcurrentHashMap<ModContainer, CheckResult>();
254+
private static final CheckResult PENDING_CHECK = new CheckResult(PENDING, null, null, null);
255+
256+
public static CheckResult getResult(ModContainer mod)
257+
{
258+
if (mod instanceof InjectedModContainer)
259+
mod = ((InjectedModContainer)mod).wrappedContainer;
260+
CheckResult ret = results.get(mod);
261+
return ret == null ? PENDING_CHECK : ret;
262+
}
151263
}
152264

‎src/main/java/net/minecraftforge/fml/client/GuiModList.java

+12-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Collections;
2323
import java.util.Comparator;
2424
import java.util.List;
25+
import java.util.Map.Entry;
2526

2627
import javax.imageio.ImageIO;
2728

@@ -43,10 +44,14 @@
4344
import net.minecraft.util.ResourceLocation;
4445
import net.minecraft.util.StringUtils;
4546
import net.minecraftforge.common.ForgeHooks;
47+
import net.minecraftforge.common.ForgeVersion;
48+
import net.minecraftforge.common.ForgeVersion.CheckResult;
49+
import net.minecraftforge.common.ForgeVersion.Status;
4650
import net.minecraftforge.fml.common.FMLLog;
4751
import net.minecraftforge.fml.common.Loader;
4852
import net.minecraftforge.fml.common.ModContainer;
4953
import net.minecraftforge.fml.common.ModContainer.Disableable;
54+
import net.minecraftforge.fml.common.versioning.ComparableVersion;
5055
import static net.minecraft.util.EnumChatFormatting.*;
5156

5257
import org.apache.logging.log4j.Level;
@@ -343,7 +348,7 @@ private void updateCache()
343348
ResourceLocation logoPath = null;
344349
Dimension logoDims = new Dimension(0, 0);
345350
List<String> lines = new ArrayList<String>();
346-
//CheckResult vercheck = ForgeVersion.getResult(selectedMod);
351+
CheckResult vercheck = ForgeVersion.getResult(selectedMod);
347352

348353
String logoFile = selectedMod.getMetadata().logoFile;
349354
if (!logoFile.isEmpty())
@@ -408,8 +413,8 @@ else if (disableable != Disableable.YES)
408413
else
409414
lines.add("Child mods: " + selectedMod.getMetadata().getChildModList());
410415

411-
//if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED)
412-
// lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url));
416+
if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED)
417+
lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url));
Has a comment. Original line has a comment.
413418

414419
lines.add(null);
415420
lines.add(selectedMod.getMetadata().description);
@@ -419,15 +424,15 @@ else if (disableable != Disableable.YES)
419424
lines.add(WHITE + selectedMod.getName());
420425
lines.add(WHITE + "Version: " + selectedMod.getVersion());
421426
lines.add(WHITE + "Mod State: " + Loader.instance().getModState(selectedMod));
422-
//if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED)
423-
// lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url));
427+
if (vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED)
428+
lines.add("Update Avalible: " + (vercheck.url == null ? "" : vercheck.url));
424429

425430
lines.add(null);
426431
lines.add(RED + "No mod information found");
427432
lines.add(RED + "Ask your mod author to provide a mod mcmod.info file");
428433
}
429434

430-
/*if ((vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) && vercheck.changes.size() > 0)
435+
if ((vercheck.status == Status.OUTDATED || vercheck.status == Status.BETA_OUTDATED) && vercheck.changes.size() > 0)
431436
{
432437
lines.add(null);
433438
lines.add("Changes:");
@@ -437,7 +442,7 @@ else if (disableable != Disableable.YES)
437442
lines.add(entry.getValue());
Has comments. Original line has comments.
438443
lines.add(null);
439444
}
440-
}*/
445+
}
441446

442447
modInfo = new Info(this.width - this.listWidth - 30, lines, logoPath, logoDims);
443448
}

‎src/main/java/net/minecraftforge/fml/client/GuiSlotModList.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import net.minecraft.client.gui.FontRenderer;
1818
import net.minecraft.client.renderer.Tessellator;
1919
import net.minecraft.util.StringUtils;
20+
import net.minecraftforge.common.ForgeVersion;
21+
import net.minecraftforge.common.ForgeVersion.CheckResult;
2022
import net.minecraftforge.fml.common.Loader;
2123
import net.minecraftforge.fml.common.LoaderState.ModState;
2224
import net.minecraftforge.fml.common.ModContainer;
@@ -79,7 +81,7 @@ protected void drawSlot(int idx, int right, int top, int height, Tessellator tes
7981
String name = StringUtils.stripControlCodes(mc.getName());
8082
String version = StringUtils.stripControlCodes(mc.getDisplayVersion());
8183
FontRenderer font = this.parent.getFontRenderer();
82-
//CheckResult vercheck = ForgeVersion.getResult(mc);
84+
CheckResult vercheck = ForgeVersion.getResult(mc);
8385

8486
if (Loader.instance().getModState(mc) == ModState.DISABLED)
8587
{
@@ -93,7 +95,7 @@ protected void drawSlot(int idx, int right, int top, int height, Tessellator tes
9395
font.drawString(font.trimStringToWidth(version, listWidth - 10), this.left + 3 , top + 12, 0xCCCCCC);
9496
font.drawString(font.trimStringToWidth(mc.getMetadata() != null ? mc.getMetadata().getChildModCountString() : "Metadata not found", listWidth - 10), this.left + 3 , top + 22, 0xCCCCCC);
9597

96-
/*switch(vercheck.status) //TODO: Change to icons?
98+
switch(vercheck.status) //TODO: Change to icons?
9799
{
98100
case BETA_OUTDATED:
99101
case OUTDATED:
@@ -105,8 +107,7 @@ protected void drawSlot(int idx, int right, int top, int height, Tessellator tes
105107
case PENDING:
106108
case UP_TO_DATE:
107109
break;
108-
}*/
110+
}
109111
}
110112
}
111-
112113
}

‎src/main/java/net/minecraftforge/fml/common/DummyModContainer.java

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package net.minecraftforge.fml.common;
1414

1515
import java.io.File;
16+
import java.net.URL;
1617
import java.security.cert.Certificate;
1718
import java.util.Collections;
1819
import java.util.List;
@@ -206,4 +207,10 @@ public boolean shouldLoadInEnvironment()
206207
{
207208
return true;
208209
}
210+
211+
@Override
212+
public URL getUpdateUrl()
213+
{
214+
return null;
215+
}
209216
}

‎src/main/java/net/minecraftforge/fml/common/FMLModContainer.java

+22
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.lang.reflect.Field;
1919
import java.lang.reflect.Method;
2020
import java.lang.reflect.Modifier;
21+
import java.net.MalformedURLException;
22+
import java.net.URL;
2123
import java.security.cert.Certificate;
2224
import java.util.Arrays;
2325
import java.util.List;
@@ -87,6 +89,7 @@ public class FMLModContainer implements ModContainer
8789
private ListMultimap<Class<? extends FMLEvent>,Method> eventMethods;
8890
private Map<String, String> customModProperties;
8991
private ModCandidate candidate;
92+
private URL updateJSONUrl;
9093

9194
public FMLModContainer(String className, ModCandidate container, Map<String,Object> modDescriptor)
9295
{
@@ -215,6 +218,19 @@ public void bindMetadata(MetadataCollection mc)
215218
{
216219
minecraftAccepted = Loader.instance().getMinecraftModContainer().getStaticVersionRange();
217220
}
221+
222+
String jsonURL = (String)descriptor.get("updateJSON");
223+
if (!Strings.isNullOrEmpty(jsonURL))
224+
{
225+
try
226+
{
227+
this.updateJSONUrl = new URL(jsonURL);
228+
}
229+
catch (MalformedURLException e)
230+
{
231+
FMLLog.log(getModId(), Level.DEBUG, "Specified json URL invalid: %s", jsonURL);
232+
}
233+
}
218234
}
219235

220236
public Properties searchForVersionProperties()
@@ -663,4 +679,10 @@ public boolean shouldLoadInEnvironment()
663679

664680
return true;
665681
}
682+
683+
@Override
684+
public URL getUpdateUrl()
685+
{
686+
return updateJSONUrl;
687+
}
666688
}

‎src/main/java/net/minecraftforge/fml/common/InjectedModContainer.java

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package net.minecraftforge.fml.common;
1414

1515
import java.io.File;
16+
import java.net.URL;
1617
import java.security.cert.Certificate;
1718
import java.util.List;
1819
import java.util.Map;
@@ -207,4 +208,10 @@ public boolean shouldLoadInEnvironment()
207208
{
208209
return true;
209210
}
211+
212+
@Override
213+
public URL getUpdateUrl()
214+
{
215+
return wrappedContainer.getUpdateUrl();
216+
}
210217
}

‎src/main/java/net/minecraftforge/fml/common/Mod.java

+12-4
Original file line numberDiff line numberDiff line change
@@ -157,18 +157,18 @@
157157
* @return The language the mod is authored in
158158
*/
159159
String modLanguage() default "java";
160-
160+
161161
/**
162162
* The language adapter to be used to load this mod. This overrides the value of modLanguage. The class must have a
163163
* public zero variable constructor and implement {@link ILanguageAdapter} just like the Java and Scala adapters.
164-
*
164+
*
165165
* A class with an invalid constructor or that doesn't implement {@link ILanguageAdapter} will throw an exception and
166166
* halt loading.
167-
*
167+
*
168168
* @return The full class name of the language adapter
169169
*/
170170
String modLanguageAdapter() default "";
171-
171+
172172
/**
173173
* NOT YET IMPLEMENTED. </br>
174174
* 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 @@
198198
* @return The name of a class implementing {@link IModGuiFactory}
199199
*/
200200
String guiFactory() default "";
201+
202+
/**
203+
* An optional URL to a JSON file that will be checked once per launch to determine if there is an updated
204+
* version of this mod and notify the end user. For more information see ForgeVersion.
205+
* @return URL to update metadata json
206+
*/
207+
String updateJSON() default "";
208+
201209
/**
202210
* A list of custom properties for this mod. Completely up to the mod author if/when they
203211
* want to put anything in here.

‎src/main/java/net/minecraftforge/fml/common/ModContainer.java

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package net.minecraftforge.fml.common;
1414

1515
import java.io.File;
16+
import java.net.URL;
1617
import java.security.cert.Certificate;
1718
import java.util.List;
1819
import java.util.Map;
@@ -149,4 +150,6 @@ public static enum Disableable {
149150
List<String> getOwnedPackages();
150151

151152
boolean shouldLoadInEnvironment();
153+
154+
URL getUpdateUrl();
152155
}

‎src/main/java/net/minecraftforge/fml/common/ModMetadata.java

+5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ public class ModMetadata
3535
public String description = "";
3636

3737
public String url = "";
38+
@Deprecated //Never really used for anything and format is undefined. See updateJSON for replacement.
3839
public String updateUrl = "";
40+
/**
41+
* URL to update json file. Format is defined here: https://gist.github.com/LexManos/7aacb9aa991330523884
42+
*/
43+
public String updateJSON = "";
3944

4045
public String logoFile = "";
4146
public String version = "";

0 commit comments

Comments
 (0)
Please sign in to comment.