Reimplement update checker

This commit is contained in:
ichttt 2019-01-27 21:23:24 +01:00
parent da33233762
commit ad32b01d85
5 changed files with 194 additions and 12 deletions

View file

@ -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'

View file

@ -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 <here>
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 <here>
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

View file

@ -19,18 +19,38 @@
package net.minecraftforge.fml;
import com.google.common.io.ByteStreams;
import com.google.gson.Gson;
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 +105,16 @@ public class VersionChecker
public static class CheckResult
{
@Nonnull
public final Status status;
@Nullable
public final ComparableVersion target;
@Nullable
public final Map<ComparableVersion, String> changes;
@Nullable
public final String url;
private CheckResult(Status status, @Nullable ComparableVersion target, @Nullable Map<ComparableVersion, String> changes, @Nullable String url)
private CheckResult(@Nonnull Status status, @Nullable ComparableVersion target, @Nullable Map<ComparableVersion, String> changes, @Nullable String url)
{
this.status = status;
this.target = target;
@ -103,20 +125,179 @@ public class VersionChecker
public static void startVersionCheck()
{
new Thread("Forge Version Check")
{
@Override
public void run()
{
// if (!ForgeModContainer.getConfig().get(ForgeModContainer.VERSION_CHECK_CAT, "Global", true).getBoolean())
// {
// log.info("Global Forge version check system disabled, no further processing.");
// return;
// } TODO config
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<ComparableVersion, String> 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<String, Object> json = new Gson().fromJson(data, Map.class);
@SuppressWarnings("unchecked")
Map<String, String> promos = (Map<String, String>)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<String, String> tmp = (Map<String, String>)json.get(mcVersion);
if (tmp != null)
{
List<ComparableVersion> 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<ModContainer, URL> gatherMods()
private static List<IModInfo> gatherMods()
{
Map<ModContainer, URL> ret = new HashMap<>();
List<IModInfo> ret = new LinkedList<>();
for (ModInfo info : ModList.get().getMods()) {
URL url = info.getUpdateURL();
if (url != null)
ret.add(info);
}
return ret;
}
private static Map<ModContainer, CheckResult> results = new ConcurrentHashMap<>();
private static Map<IModInfo, CheckResult> 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);
}
}

View file

@ -19,6 +19,7 @@
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;
@ -65,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

View file

@ -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"