diff --git a/build.gradle b/build.gradle index 3833ef203c..302311096c 100644 --- a/build.gradle +++ b/build.gradle @@ -272,7 +272,7 @@ project(':forge') { installer 'cpw.mods:modlauncher:0.9.+' installer 'net.minecraftforge:accesstransformers:0.14.+:shadowed' installer 'net.minecraftforge:eventbus:0.6.+:service' - installer 'net.minecraftforge:forgespi:0.4.+' + installer 'net.minecraftforge:forgespi:0.5.+' installer 'net.minecraftforge:coremods:0.2.+' installer 'com.electronwill.night-config:core:3.4.2' installer 'com.electronwill.night-config:toml:3.4.2' diff --git a/mdk/src/main/resources/META-INF/mods.toml b/mdk/src/main/resources/META-INF/mods.toml index 3c4cc8777c..7c52e292cd 100644 --- a/mdk/src/main/resources/META-INF/mods.toml +++ b/mdk/src/main/resources/META-INF/mods.toml @@ -7,8 +7,6 @@ modLoader="javafml" #mandatory # A version range to match for said mod loader - for regular FML @Mod it will be the forge version loaderVersion="[24,)" #mandatory (24 is current forge version) -# A URL to query for updates for this mod. See the JSON update specification -updateJSONURL="http://myurl.me/" #optional # A URL to refer people to when problems occur with this mod issueTrackerURL="http://my.issue.tracker/" #optional # A URL for the "homepage" for this mod, displayed in the mod UI @@ -27,6 +25,8 @@ modId="examplemod" #mandatory version="${file.jarVersion}" #mandatory # A display name for the mod displayName="Example Mod" #mandatory +# A URL to query for updates for this mod. See the JSON update specification +updateJSONURL="http://myurl.me/" #optional # The description text for the mod (multi line!) (#mandatory) description=''' This is a long form description of the mod. You can write whatever you want here diff --git a/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.java.patch b/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.java.patch index be10f7c490..3b3000ab98 100644 --- a/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.java.patch +++ b/patches/minecraft/net/minecraft/client/gui/GuiMainMenu.java.patch @@ -45,7 +45,17 @@ ISaveFormat isaveformat1 = GuiMainMenu.this.field_146297_k.func_71359_d(); WorldInfo worldinfo1 = isaveformat1.func_75803_c("Demo_World"); if (worldinfo1 != null) { -@@ -283,7 +290,10 @@ +@@ -268,6 +275,9 @@ + + this.field_146297_k.func_110434_K().func_110577_a(field_194400_H); + func_146110_a(j + 88, 67, 0.0F, 0.0F, 98, 14, 128.0F, 16.0F); ++ ++ net.minecraftforge.client.ForgeHooksClient.renderMainMenu(this, this.field_146289_q, this.field_146294_l, this.field_146295_m); ++ + GlStateManager.func_179094_E(); + GlStateManager.func_179109_b((float)(this.field_146294_l / 2 + 90), 70.0F, 0.0F); + GlStateManager.func_179114_b(-20.0F, 0.0F, 0.0F, 1.0F); +@@ -283,7 +293,10 @@ s = s + ("release".equalsIgnoreCase(this.field_146297_k.func_184123_d()) ? "" : "/" + this.field_146297_k.func_184123_d()); } @@ -57,7 +67,7 @@ this.func_73731_b(this.field_146289_q, "Copyright Mojang AB. Do not distribute!", this.field_193979_N, this.field_146295_m - 10, -1); if (p_73863_1_ > this.field_193979_N && p_73863_1_ < this.field_193979_N + this.field_193978_M && p_73863_2_ > this.field_146295_m - 10 && p_73863_2_ < this.field_146295_m) { func_73734_a(this.field_193979_N, this.field_146295_m - 1, this.field_193979_N + this.field_193978_M, this.field_146295_m, -1); -@@ -299,6 +309,7 @@ +@@ -299,6 +312,7 @@ if (this.func_183501_a()) { this.field_183503_M.func_73863_a(p_73863_1_, p_73863_2_, p_73863_3_); } diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFileInfo.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFileInfo.java index 165d6b00cd..3ff577185a 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFileInfo.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModFileInfo.java @@ -35,7 +35,6 @@ public class ModFileInfo implements IModFileInfo { private final UnmodifiableConfig config; private final ModFile modFile; - private final URL updateJSONURL; private final URL issueURL; private final String modLoader; private final VersionRange modLoaderVersion; @@ -59,7 +58,6 @@ public class ModFileInfo implements IModFileInfo throw new InvalidModFileException("Missing mods list", this); } this.mods = modConfigs.stream().map(mi-> new ModInfo(this, mi)).collect(Collectors.toList()); - this.updateJSONURL = config.getOptional("updateJSONURL").map(StringUtils::toURL).orElse(null); this.issueURL = config.getOptional("issueTrackerURL").map(StringUtils::toURL).orElse(null); } @@ -80,12 +78,6 @@ public class ModFileInfo implements IModFileInfo return this.config; } - @Override - public URL getUpdateURL(IModFileInfo modFileInfo) - { - return this.updateJSONURL; - } - @Override public String getModLoader() { diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java index 4fa2b15af4..8cd0756601 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java @@ -20,9 +20,11 @@ package net.minecraftforge.fml.loading.moddiscovery; import com.electronwill.nightconfig.core.UnmodifiableConfig; +import net.minecraftforge.fml.loading.StringUtils; import net.minecraftforge.forgespi.language.IModInfo; import net.minecraftforge.fml.loading.StringSubstitutor; +import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -48,6 +50,7 @@ public class ModInfo implements IModInfo private final ArtifactVersion version; private final String displayName; private final String description; + private final URL updateJSONURL; private final List dependencies; private final Map properties; private final UnmodifiableConfig modConfig; @@ -71,6 +74,7 @@ public class ModInfo implements IModInfo map(DefaultArtifactVersion::new).orElse(DEFAULT_VERSION); this.displayName = modConfig.getOptional("displayName").orElse(null); this.description = modConfig.get("description"); + this.updateJSONURL = modConfig.getOptional("updateJSONURL").map(StringUtils::toURL).orElse(null); if (owningFile != null) { this.dependencies = owningFile.getConfig().>getOptional(Arrays.asList("dependencies", this.modId)). orElse(Collections.emptyList()).stream().map(dep -> new ModVersion(this, dep)).collect(Collectors.toList()); @@ -103,6 +107,7 @@ public class ModInfo implements IModInfo { return this.description; } + @Override public ArtifactVersion getVersion() { return version; @@ -128,6 +133,11 @@ public class ModInfo implements IModInfo return this.properties; } + @Override + public URL getUpdateURL() { + return this.updateJSONURL; + } + public Optional getLogoFile() { return this.owningFile != null ? this.owningFile.getConfig().getOptional("logoFile") : this.modConfig.getOptional("logoFile"); diff --git a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index e3e291e222..82e27a4e92 100644 --- a/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -296,8 +296,7 @@ public class ForgeHooksClient //RenderingRegistry.registerBlockHandler(RenderBlockFluid.instance); } - private static int updatescrollcounter = 0; - public static String renderMainMenu(GuiMainMenu gui, FontRenderer font, int width, int height, String splashText) + public static void renderMainMenu(GuiMainMenu gui, FontRenderer font, int width, int height) { VersionChecker.Status status = ForgeVersion.getStatus(); if (status == BETA || status == BETA_OUTDATED) @@ -325,8 +324,6 @@ public class ForgeHooksClient // if we have a line, render it in the bottom right, above Mojang's copyright line gui.drawString(font, line, width - font.getStringWidth(line) - 2, height - (2 * (font.FONT_HEIGHT + 1)), -1); } - - return splashText; } public static ISound playSound(SoundManager manager, ISound sound) diff --git a/src/main/java/net/minecraftforge/fml/VersionChecker.java b/src/main/java/net/minecraftforge/fml/VersionChecker.java index 54df97b366..6548978329 100644 --- a/src/main/java/net/minecraftforge/fml/VersionChecker.java +++ b/src/main/java/net/minecraftforge/fml/VersionChecker.java @@ -19,18 +19,39 @@ package net.minecraftforge.fml; +import com.google.common.io.ByteStreams; +import com.google.gson.Gson; +import net.minecraftforge.common.ForgeConfig; import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.forgespi.language.IModInfo; +import net.minecraftforge.versions.mcp.MCPVersion; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.maven.artifact.versioning.ComparableVersion; +import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import static net.minecraftforge.fml.VersionChecker.Status.*; + public class VersionChecker { + private static final Logger LOGGER = LogManager.getLogger(); + private static final int MAX_HTTP_REDIRECTS = Integer.getInteger("http.maxRedirects", 20); + public enum Status { PENDING(), @@ -85,14 +106,16 @@ public class VersionChecker public static class CheckResult { + @Nonnull public final Status status; @Nullable public final ComparableVersion target; + @Nullable public final Map changes; @Nullable public final String url; - private CheckResult(Status status, @Nullable ComparableVersion target, @Nullable Map changes, @Nullable String url) + private CheckResult(@Nonnull Status status, @Nullable ComparableVersion target, @Nullable Map changes, @Nullable String url) { this.status = status; this.target = target; @@ -103,20 +126,179 @@ public class VersionChecker public static void startVersionCheck() { + new Thread("Forge Version Check") + { + @Override + public void run() + { + if (ForgeConfig.GENERAL.disableVersionCheck.get()) + { + LOGGER.info("Global Forge version check system disabled, no further processing."); + return; + } + + for (IModInfo entry : gatherMods()) + { + process(entry); + } + } + + /** + * Opens stream for given URL while following redirects + */ + private InputStream openUrlStream(URL url) throws IOException + { + URL currentUrl = url; + for (int redirects = 0; redirects < MAX_HTTP_REDIRECTS; redirects++) + { + URLConnection c = currentUrl.openConnection(); + if (c instanceof HttpURLConnection) + { + HttpURLConnection huc = (HttpURLConnection) c; + huc.setInstanceFollowRedirects(false); + int responseCode = huc.getResponseCode(); + if (responseCode >= 300 && responseCode <= 399) + { + try + { + String loc = huc.getHeaderField("Location"); + currentUrl = new URL(currentUrl, loc); + continue; + } + finally + { + huc.disconnect(); + } + } + } + + return c.getInputStream(); + } + throw new IOException("Too many redirects while trying to fetch " + url); + } + + @SuppressWarnings("UnstableApiUsage") + private void process(IModInfo mod) + { + Status status = PENDING; + ComparableVersion target = null; + Map changes = null; + String display_url = null; + try + { + URL url = mod.getUpdateURL(); + LOGGER.info("[{}] Starting version check at {}", mod.getModId(), url.toString()); + + InputStream con = openUrlStream(url); + String data = new String(ByteStreams.toByteArray(con), StandardCharsets.UTF_8); + con.close(); + + LOGGER.debug("[{}] Received version check data:\n{}", mod.getModId(), data); + + + @SuppressWarnings("unchecked") + Map json = new Gson().fromJson(data, Map.class); + @SuppressWarnings("unchecked") + Map promos = (Map)json.get("promos"); + display_url = (String)json.get("homepage"); + + String mcVersion = MCPVersion.getMCVersion(); + String rec = promos.get(mcVersion + "-recommended"); + String lat = promos.get(mcVersion + "-latest"); + ComparableVersion current = new ComparableVersion(mod.getVersion().toString()); + + if (rec != null) + { + ComparableVersion recommended = new ComparableVersion(rec); + int diff = recommended.compareTo(current); + + if (diff == 0) + status = UP_TO_DATE; + else if (diff < 0) + { + status = AHEAD; + if (lat != null) + { + ComparableVersion latest = new ComparableVersion(lat); + if (current.compareTo(latest) < 0) + { + status = OUTDATED; + target = latest; + } + } + } + else + { + status = OUTDATED; + target = recommended; + } + } + else if (lat != null) + { + ComparableVersion latest = new ComparableVersion(lat); + if (current.compareTo(latest) < 0) + { + status = BETA_OUTDATED; + target = latest; + } + else + status = BETA; + } + else + status = BETA; + + LOGGER.info("[{}] Found status: {} Target: {}", mod.getModId(), status, target); + + changes = new LinkedHashMap<>(); + @SuppressWarnings("unchecked") + Map tmp = (Map)json.get(mcVersion); + 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())); + } + } + } + catch (Exception e) + { + LOGGER.debug("Failed to process update information", e); + status = FAILED; + } + results.put(mod, new CheckResult(status, target, changes, display_url)); + } + }.start(); } // Gather a list of mods that have opted in to this update system by providing a URL. - public static Map gatherMods() + private static List gatherMods() { - Map ret = new HashMap<>(); + List ret = new LinkedList<>(); + for (ModInfo info : ModList.get().getMods()) { + URL url = info.getUpdateURL(); + if (url != null) + ret.add(info); + } return ret; } - private static Map results = new ConcurrentHashMap<>(); + private static Map results = new ConcurrentHashMap<>(); + private static final CheckResult PENDING_CHECK = new CheckResult(PENDING, null, null, null); - public static CheckResult getResult(ModInfo mod) + public static CheckResult getResult(IModInfo mod) { - return new CheckResult(Status.PENDING, null, null, null); + return results.getOrDefault(mod, PENDING_CHECK); } } diff --git a/src/main/java/net/minecraftforge/versions/forge/ForgeVersion.java b/src/main/java/net/minecraftforge/versions/forge/ForgeVersion.java index 44d2f90a98..8918d5090d 100644 --- a/src/main/java/net/minecraftforge/versions/forge/ForgeVersion.java +++ b/src/main/java/net/minecraftforge/versions/forge/ForgeVersion.java @@ -19,10 +19,13 @@ package net.minecraftforge.versions.forge; +import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.VersionChecker; import net.minecraftforge.fml.loading.JarVersionLookupHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import javax.annotation.Nullable; @@ -63,7 +66,7 @@ public class ForgeVersion public static VersionChecker.Status getStatus() { - return VersionChecker.Status.PENDING; + return VersionChecker.getResult(ModList.get().getModFileById(MOD_ID).getMods().get(0)).status; } @Nullable diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index cb65a6db2d..f90ca51ab8 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,6 +1,5 @@ modLoader="javafml" loaderVersion="[24,]" -updateJSONURL="https://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json" issueTrackerURL="http://www.minecraftforge.net/" logoFile="forge_logo.png" @@ -8,6 +7,7 @@ logoFile="forge_logo.png" modId="forge" # We use the global forge version version="${global.forgeVersion}" + updateJSONURL="https://files.minecraftforge.net/maven/net/minecraftforge/forge/promotions_slim.json" displayName="Forge" credits="LexManos,cpw,Mezz,fry,tterrag,gigaherz,illy" authors="LexManos,cpw,Mezz,fry,Spacetoad,Eloraam" diff --git a/src/main/resources/assets/forge/lang/en_us.json b/src/main/resources/assets/forge/lang/en_us.json index f4e09daad8..cba3d1db71 100644 --- a/src/main/resources/assets/forge/lang/en_us.json +++ b/src/main/resources/assets/forge/lang/en_us.json @@ -70,6 +70,10 @@ "commands.forge.tracking.no_data": "No data has been recorded yet.", + "forge.update.beta.1": "%sWARNING: %sForge Beta", + "forge.update.beta.2": "Major issues may arise, verify before reporting.", + "forge.update.newversion": "New Forge version available: %s", + "forge.configgui.clumpingThreshold.tooltip": "Controls the number threshold at which Packet51 is preferred over Packet52.", "forge.configgui.clumpingThreshold": "Packet Clumping Threshold", "forge.configgui.disableVersionCheck.tooltip": "Set to true to disable Forge's version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.",