i was too lazy to sort these changes

This commit is contained in:
FalsePattern committed 2022-08-17 20:08:46 +02:00
1 parent 8290e04fed
commit c440524bdd
15 files changed
+391 -243

No files matched your search

@@ -21,7 +21,6 @@
package com.falsepattern.lib.dependencies;
import com.falsepattern.lib.StableAPI;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import lombok.val;
@@ -30,23 +29,17 @@ import java.util.Objects;
@StableAPI(since = "0.6.0")
public class SemanticVersion extends Version {
@Getter
@Getter(onMethod_ = @StableAPI.Expose)
private final int majorVersion;
@Getter
@Getter(onMethod_ = @StableAPI.Expose)
private final int minorVersion;
@Getter
@Getter(onMethod_ = @StableAPI.Expose)
private final int patchVersion;
@Getter
@Getter(onMethod_ = @StableAPI.Expose)
private final String preRelease;
@Getter
@Getter(onMethod_ = @StableAPI.Expose)
private final String build;
@StableAPI.Expose
public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, String preRelease) {
this(majorVersion, minorVersion, patchVersion, preRelease, null);
}
@Builder
@StableAPI.Expose
public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, String preRelease, String build) {
this.majorVersion = majorVersion;
@@ -58,11 +51,31 @@ public class SemanticVersion extends Version {
this.build = "".equals(build) ? null : build;
}
@StableAPI.Expose
public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, String preRelease) {
this(majorVersion, minorVersion, patchVersion, preRelease, null);
}
@StableAPI.Expose
public SemanticVersion(int majorVersion, int minorVersion, int patchVersion) {
this(majorVersion, minorVersion, patchVersion, null, null);
}
@StableAPI.Expose(since = "0.10.0")
public SemanticVersion(int majorVersion, int minorVersion) {
this(majorVersion, minorVersion, -1, null, null);
}
@StableAPI.Expose(since = "0.10.0")
public SemanticVersion(int majorVersion) {
this(majorVersion, -1, -1, null, null);
}
@StableAPI.Expose
public static SemanticVersionBuilder builder() {
return new SemanticVersionBuilder();
}
@Override
public int compareTo(@NonNull Version o) {
if (o instanceof ComplexVersion) {
@@ -98,7 +111,62 @@ public class SemanticVersion extends Version {
@Override
public String toString() {
return majorVersion + "." + minorVersion + "." + patchVersion + (preRelease == null ? "" : "-" + preRelease) +
return majorVersion + (minorVersion < 0 ? "" : "." + minorVersion) + (patchVersion < 0 ? "" : "." + patchVersion) + (preRelease == null ? "" : "-" + preRelease) +
(build == null ? "" : "+" + build);
}
@StableAPI(since = "0.10.0")
public static class SemanticVersionBuilder {
private int majorVersion;
private int minorVersion = -1;
private int patchVersion = -1;
private String preRelease;
private String build;
@StableAPI.Internal
SemanticVersionBuilder() {
}
@StableAPI.Expose
public SemanticVersionBuilder majorVersion(int majorVersion) {
this.majorVersion = majorVersion;
return this;
}
@StableAPI.Expose
public SemanticVersionBuilder minorVersion(int minorVersion) {
this.minorVersion = minorVersion;
return this;
}
@StableAPI.Expose
public SemanticVersionBuilder patchVersion(int patchVersion) {
this.patchVersion = patchVersion;
return this;
}
@StableAPI.Expose
public SemanticVersionBuilder preRelease(String preRelease) {
this.preRelease = preRelease;
return this;
}
@StableAPI.Expose
public SemanticVersionBuilder build(String build) {
this.build = build;
return this;
}
@StableAPI.Expose
public SemanticVersion build() {
return new SemanticVersion(majorVersion, minorVersion, patchVersion, preRelease, build);
}
@Override
public String toString() {
return "SemanticVersion.SemanticVersionBuilder(majorVersion=" + this.majorVersion + ", minorVersion=" +
this.minorVersion + ", patchVersion=" + this.patchVersion + ", preRelease=" + this.preRelease +
", build=" + this.build + ")";
}
}
}
@@ -1,142 +0,0 @@
/*
* Copyright (C) 2022 FalsePattern
* All Rights Reserved
*
* The above copyright notice, this permission notice and the word "SNEED"
* shall be included in all copies or substantial portions of the Software.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.lib.internal;
import lombok.Getter;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@SuppressWarnings("ALL")
@Getter
class SRGGen {
private static final Map<String, String> class_notch_srg = new HashMap<>();
private static final Map<String, String> class_notch_mcp = new HashMap<>();
private static final Map<String, String> class_srg_mcp = new HashMap<>();
private static final Map<String, String> class_mcp_srg = new HashMap<>();
private static final Map<String, String> class_mcp_notch = new HashMap<>();
private static final Map<String, String> field_notch_srg = new HashMap<>();
private static final Map<String, String> field_notch_mcp = new HashMap<>();
private static final Map<String, String> field_srg_mcp = new HashMap<>();
private static final Map<String, String> field_mcp_srg = new HashMap<>();
private static final Map<String, String> field_mcp_notch = new HashMap<>();
private static final Map<StringPair, StringPair> method_notch_srg = new HashMap<>();
private static final Map<StringPair, StringPair> method_notch_mcp = new HashMap<>();
private static final Map<StringPair, StringPair> method_srg_mcp = new HashMap<>();
private static final Map<StringPair, StringPair> method_mcp_srg = new HashMap<>();
private static final Map<StringPair, StringPair> method_mcp_notch = new HashMap<>();
public static void main(String[] args) throws IOException {
Path dir = Paths.get(args[0]);
parseLines(Files.readAllLines(dir.resolve("notch-srg.srg")), class_notch_srg, field_notch_srg,
method_notch_srg);
parseLines(Files.readAllLines(dir.resolve("notch-mcp.srg")), class_notch_mcp, field_notch_mcp,
method_notch_mcp);
parseLines(Files.readAllLines(dir.resolve("srg-mcp.srg")), class_srg_mcp, field_srg_mcp, method_srg_mcp);
parseLines(Files.readAllLines(dir.resolve("mcp-srg.srg")), class_mcp_srg, field_mcp_srg, method_mcp_srg);
parseLines(Files.readAllLines(dir.resolve("mcp-notch.srg")), class_mcp_notch, field_mcp_notch,
method_mcp_notch);
try (OutputStream output = Files.newOutputStream(dir.resolve("classes.csv"))) {
output.write("notch,srg,mcp\n".getBytes(StandardCharsets.UTF_8));
crossValidate(class_notch_srg, class_notch_mcp, class_srg_mcp, class_mcp_srg, class_mcp_notch, output);
}
try (OutputStream output = Files.newOutputStream(dir.resolve("fields.csv"))) {
output.write("notch,srg,mcp\n".getBytes(StandardCharsets.UTF_8));
crossValidate(field_notch_srg, field_notch_mcp, field_srg_mcp, field_mcp_srg, field_mcp_notch, output);
}
try (OutputStream output = Files.newOutputStream(dir.resolve("methods.csv"))) {
output.write("notch,notchdesc,srg,srgdesc,mcp,mcpdesc\n".getBytes(StandardCharsets.UTF_8));
crossValidate(method_notch_srg, method_notch_mcp, method_srg_mcp, method_mcp_srg, method_mcp_notch, output);
}
}
private static boolean crossValidate(Map<?, ?> notch_srg, Map<?, ?> notch_mcp, Map<?, ?> srg_mcp, Map<?, ?> mcp_srg, Map<?, ?> mcp_notch, OutputStream output)
throws IOException {
for (Map.Entry<?, ?> notch_srg_entry : notch_srg.entrySet()) {
Object notch = notch_srg_entry.getKey();
Object srg = notch_srg_entry.getValue();
Object mcp = notch_mcp.get(notch);
if (!srg_mcp.get(srg).equals(mcp) || !mcp_srg.get(mcp).equals(srg) || !mcp_notch.get(mcp).equals(notch)) {
throw new RuntimeException(notch + " - " + srg + " - " + mcp);
}
output.write((notch + "," + srg + "," + mcp + "\n").getBytes(StandardCharsets.UTF_8));
}
return true;
}
private static void parseLines(List<String> lines, Map<String, String> classes, Map<String, String> fields, Map<StringPair, StringPair> methods) {
for (String line : lines) {
String[] parts = line.split(" ");
switch (parts[0]) {
case "CL:":
classes.put(parts[1], parts[2]);
break;
case "FD:":
fields.put(parts[1], parts[2]);
break;
case "MD:":
methods.put(new StringPair(parts[1], parts[2]), new StringPair(parts[3], parts[4]));
break;
}
}
}
private static class StringPair {
public final String name;
public final String desc;
private StringPair(String name, String desc) {
this.name = name;
this.desc = desc;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StringPair that = (StringPair) o;
return name.equals(that.name) && desc.equals(that.desc);
}
@Override
public int hashCode() {
return Objects.hash(name, desc);
}
@Override
public String toString() {
return name + "," + desc;
}
}
}
@@ -0,0 +1,66 @@
package com.falsepattern.lib.internal.impl.mixin;
import com.falsepattern.lib.mixin.MinecraftURLClassPath;
import lombok.val;
import net.minecraft.launchwrapper.LaunchClassLoader;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModClassLoader;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
public final class UCPImpl {
private static final Object ucp;
private static final Method addURL;
private static final boolean GRIMOIRE;
static {
boolean grimoire = false;
String[] knownGrimoireClassNames =
new String[]{"io.github.crucible.grimoire.Grimoire", "io.github.crucible.grimoire.common.GrimoireCore"};
for (val className : knownGrimoireClassNames) {
try {
Class.forName(className, false, MinecraftURLClassPath.class.getClassLoader());
grimoire = true;
break;
} catch (ClassNotFoundException ignored) {
}
}
GRIMOIRE = grimoire;
if (!GRIMOIRE) {
try {
val modClassLoaderField = Loader.class.getDeclaredField("modClassLoader");
modClassLoaderField.setAccessible(true);
val loaderInstanceField = Loader.class.getDeclaredField("instance");
loaderInstanceField.setAccessible(true);
val mainClassLoaderField = ModClassLoader.class.getDeclaredField("mainClassLoader");
mainClassLoaderField.setAccessible(true);
val ucpField = LaunchClassLoader.class.getSuperclass().getDeclaredField("ucp");
ucpField.setAccessible(true);
Object loader = loaderInstanceField.get(null);
val modClassLoader = (ModClassLoader) modClassLoaderField.get(loader);
val mainClassLoader = (LaunchClassLoader) mainClassLoaderField.get(modClassLoader);
ucp = ucpField.get(mainClassLoader);
addURL = ucp.getClass().getDeclaredMethod("addURL", URL.class);
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException(e.getMessage());
}
} else {
ucp = null;
addURL = null;
System.err.println("Grimoire detected, disabling jar loading utility");
}
}
public static void addJar(File pathToJar) throws Exception {
if (!GRIMOIRE) {
addURL.invoke(ucp, pathToJar.toURI().toURL());
}
}
}
@@ -0,0 +1,168 @@
package com.falsepattern.lib.internal.impl.updates;
import com.falsepattern.json.node.JsonNode;
import com.falsepattern.lib.dependencies.DependencyLoader;
import com.falsepattern.lib.dependencies.Library;
import com.falsepattern.lib.dependencies.SemanticVersion;
import com.falsepattern.lib.internal.Internet;
import com.falsepattern.lib.internal.Tags;
import com.falsepattern.lib.internal.config.LibraryConfig;
import com.falsepattern.lib.text.FormattedText;
import com.falsepattern.lib.updates.ModUpdateInfo;
import com.falsepattern.lib.updates.UpdateCheckException;
import lombok.val;
import net.minecraft.client.resources.I18n;
import net.minecraft.event.ClickEvent;
import net.minecraft.util.IChatComponent;
import cpw.mods.fml.common.Loader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicBoolean;
public final class UpdateCheckerImpl {
private static final AtomicBoolean jsonLibraryLoaded = new AtomicBoolean(false);
public static List<IChatComponent> updateListToChatMessages(String initiator, List<ModUpdateInfo> updates) {
if (updates == null || updates.size() == 0) {
return null;
}
val updateText = new ArrayList<IChatComponent>(
FormattedText.parse(I18n.format("falsepatternlib.chat.updatesavailable", initiator)).toChatText());
val mods = Loader.instance().getIndexedModList();
for (val update : updates) {
val mod = mods.get(update.modID);
updateText.addAll(
FormattedText.parse(I18n.format("falsepatternlib.chat.modname", mod.getName())).toChatText());
updateText.addAll(
FormattedText.parse(I18n.format("falsepatternlib.chat.currentversion", update.currentVersion))
.toChatText());
updateText.addAll(
FormattedText.parse(I18n.format("falsepatternlib.chat.latestversion", update.latestVersion))
.toChatText());
if (!update.updateURL.isEmpty()) {
val pre = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurlpre")).toChatText();
val link = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurl")).toChatText();
val post = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurlpost")).toChatText();
pre.get(pre.size() - 1).appendSibling(link.get(0));
link.get(link.size() - 1).appendSibling(post.get(0));
for (val l : link) {
l.getChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, update.updateURL));
}
link.remove(0);
post.remove(0);
updateText.addAll(pre);
updateText.addAll(link);
updateText.addAll(post);
}
}
return updateText;
}
public static CompletableFuture<List<ModUpdateInfo>> fetchUpdatesAsync(String url) {
return CompletableFuture.supplyAsync(() -> {
if (!LibraryConfig.ENABLE_UPDATE_CHECKER) {
throw new CompletionException(new UpdateCheckException("Update checker is disabled in config!"));
}
URL URL;
try {
URL = new URL(url);
} catch (MalformedURLException e) {
throw new CompletionException(new UpdateCheckException("Invalid URL: " + url, e));
}
if (!jsonLibraryLoaded.get()) {
try {
DependencyLoader.addMavenRepo("https://maven.falsepattern.com/");
DependencyLoader.loadLibraries(Library.builder()
.loadingModId(Tags.MODID)
.groupId("com.falsepattern")
.artifactId("json")
.minVersion(new SemanticVersion(0, 4, 0))
.maxVersion(new SemanticVersion(0, Integer.MAX_VALUE, Integer.MAX_VALUE))
.preferredVersion(new SemanticVersion(0, 4, 1))
.build());
} catch (Exception e) {
throw new CompletionException(
new UpdateCheckException("Failed to load json library for update checker!", e));
}
jsonLibraryLoaded.set(true);
}
val result = new ArrayList<ModUpdateInfo>();
JsonNode parsed;
try {
parsed = JsonNode.parse(Internet.download(URL).thenApply(String::new).join());
} catch (CompletionException e) {
throw new CompletionException(new UpdateCheckException("Failed to download update checker JSON file!",
e.getCause() == null ? e : e.getCause()));
}
List<JsonNode> modList;
if (parsed.isList()) {
modList = parsed.getJavaList();
} else {
modList = Collections.singletonList(parsed);
}
val installedMods = Loader.instance().getIndexedModList();
for (val node : modList) {
if (!node.isObject()) {
continue;
}
if (!node.containsKey("modid")) {
continue;
}
if (!node.containsKey("latestVersion")) {
continue;
}
val modid = node.getString("modid");
if (!installedMods.containsKey(modid)) {
continue;
}
val mod = installedMods.get(modid);
val latestVersionsNode = node.get("latestVersion");
List<String> latestVersions;
if (latestVersionsNode.isString()) {
latestVersions = Collections.singletonList(latestVersionsNode.stringValue());
} else if (latestVersionsNode.isList()) {
latestVersions = new ArrayList<>();
for (val version : latestVersionsNode.getJavaList()) {
if (!version.isString()) {
continue;
}
latestVersions.add(version.stringValue());
}
} else {
continue;
}
val currentVersion = mod.getVersion();
if (latestVersions.contains(currentVersion)) {
continue;
}
val updateURL =
node.containsKey("updateURL") && node.get("updateURL").isString() ? node.getString("updateURL")
: "";
result.add(new ModUpdateInfo(modid, currentVersion, latestVersions.get(0), updateURL));
}
return result;
});
}
public static List<ModUpdateInfo> fetchUpdates(String url) throws UpdateCheckException {
try {
return fetchUpdatesAsync(url).join();
} catch (CompletionException e) {
try {
throw e.getCause();
} catch (UpdateCheckException e1) {
throw e1;
} catch (Throwable e1) {
throw new UpdateCheckException("Failed to check for updates!", e1);
}
}
}
}
@@ -21,8 +21,10 @@
package com.falsepattern.lib.mapping;
import com.falsepattern.lib.StableAPI;
import com.falsepattern.lib.dependencies.DependencyLoader;
import com.falsepattern.lib.dependencies.Library;
import com.falsepattern.lib.dependencies.SemanticVersion;
import com.falsepattern.lib.internal.CoreLoadingPlugin;
import com.falsepattern.lib.internal.FalsePatternLib;
import com.falsepattern.lib.mapping.storage.Lookup;
import com.falsepattern.lib.mapping.types.MappingType;
import com.falsepattern.lib.mapping.types.NameType;
@@ -32,9 +34,12 @@ import com.falsepattern.lib.mapping.types.UniversalMethod;
import com.falsepattern.lib.util.ResourceUtil;
import lombok.SneakyThrows;
import lombok.val;
import lombok.var;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.tukaani.xz.LZMA2Options;
import java.io.DataInputStream;
import java.util.HashMap;
import java.util.Map;
@@ -51,9 +56,26 @@ public class MappingManager {
return;
}
initialized = true;
DependencyLoader.addMavenRepo("https://repo1.maven.org/maven2/");
DependencyLoader.loadLibraries(Library.builder()
.groupId("org.tukaani")
.artifactId("xz")
.minVersion(SemanticVersion.builder()
.majorVersion(1)
.minorVersion(9)
.patchVersion(-1)
.build())
.preferredVersion(SemanticVersion.builder()
.majorVersion(1)
.minorVersion(9)
.patchVersion(-1)
.build())
.build());
val input = new DataInputStream(new LZMA2Options(6).getInputStream(ResourceUtil.getResourceFromJar("/mappings.lzma2", CoreLoadingPlugin.class)));
{
val classMappings =
ResourceUtil.getResourceStringFromJar("/classes.csv", FalsePatternLib.class).split("\n");
var classBytes = new byte[input.readInt()];
input.readFully(classBytes);
val classMappings = new String(classBytes).split("\n");
for (int i = 1; i < classMappings.length; i++) {
val line = classMappings[i].split(",");
val clazz = new UniversalClass(line, stringPool);
@@ -62,7 +84,9 @@ public class MappingManager {
}
}
{
val fieldMappings = ResourceUtil.getResourceStringFromJar("/fields.csv", FalsePatternLib.class).split("\n");
var fieldBytes = new byte[input.readInt()];
input.readFully(fieldBytes);
var fieldMappings = new String(fieldBytes).split( "\n");
for (int i = 1; i < fieldMappings.length; i++) {
val line = fieldMappings[i].split(",");
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
@@ -70,8 +94,9 @@ public class MappingManager {
}
}
{
val methodMappings =
ResourceUtil.getResourceStringFromJar("/methods.csv", FalsePatternLib.class).split("\n");
var methodBytes = new byte[input.readInt()];
input.readFully(methodBytes);
val methodMappings = new String(methodBytes).split("\n");
for (int i = 1; i < methodMappings.length; i++) {
val line = methodMappings[i].split(",");
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
@@ -21,17 +21,11 @@
package com.falsepattern.lib.mixin;
import com.falsepattern.lib.StableAPI;
import com.falsepattern.lib.internal.impl.mixin.UCPImpl;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.val;
import net.minecraft.launchwrapper.LaunchClassLoader;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModClassLoader;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
/**
* Backport from spongemixins 1.3 for compat with the curseforge 1.2.0 version.
@@ -41,64 +35,8 @@ import java.net.URL;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@StableAPI(since = "0.10.0")
public final class MinecraftURLClassPath {
/**
* Utility to manipulate the minecraft URL ClassPath
*/
private static final Object ucp;
private static final Method addURL;
private static final boolean GRIMOIRE;
static {
boolean grimoire = false;
String[] knownGrimoireClassNames =
new String[]{"io.github.crucible.grimoire.Grimoire", "io.github.crucible.grimoire.common.GrimoireCore"};
for (val className : knownGrimoireClassNames) {
try {
Class.forName(className, false, MinecraftURLClassPath.class.getClassLoader());
grimoire = true;
break;
} catch (ClassNotFoundException ignored) {
}
}
GRIMOIRE = grimoire;
if (!GRIMOIRE) {
try {
val modClassLoaderField = Loader.class.getDeclaredField("modClassLoader");
modClassLoaderField.setAccessible(true);
val loaderInstanceField = Loader.class.getDeclaredField("instance");
loaderInstanceField.setAccessible(true);
val mainClassLoaderField = ModClassLoader.class.getDeclaredField("mainClassLoader");
mainClassLoaderField.setAccessible(true);
val ucpField = LaunchClassLoader.class.getSuperclass().getDeclaredField("ucp");
ucpField.setAccessible(true);
Object loader = loaderInstanceField.get(null);
val modClassLoader = (ModClassLoader) modClassLoaderField.get(loader);
val mainClassLoader = (LaunchClassLoader) mainClassLoaderField.get(modClassLoader);
ucp = ucpField.get(mainClassLoader);
addURL = ucp.getClass().getDeclaredMethod("addURL", URL.class);
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException(e.getMessage());
}
} else {
ucp = null;
addURL = null;
System.err.println("Grimoire detected, disabling jar loading utility");
}
}
/**
* Adds a Jar to the Minecraft URL ClassPath - Needed when using mixins on classes outside of Minecraft or other
* coremods
*/
@StableAPI.Expose
public static void addJar(File pathToJar) throws Exception {
if (!GRIMOIRE) {
addURL.invoke(ucp, pathToJar.toURI().toURL());
}
UCPImpl.addJar(pathToJar);
}
}
@@ -24,22 +24,22 @@ import com.falsepattern.lib.StableAPI;
@StableAPI(since = "0.8.3")
public class UpdateCheckException extends Exception {
@StableAPI.Expose
@StableAPI.Internal
public UpdateCheckException(String message) {
super(message);
}
@StableAPI.Expose
@StableAPI.Internal
public UpdateCheckException(String message, Throwable cause) {
super(message, cause);
}
@StableAPI.Expose
@StableAPI.Internal
public UpdateCheckException(Throwable cause) {
super(cause);
}
@StableAPI.Expose
@StableAPI.Internal
protected UpdateCheckException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
@@ -20,36 +20,16 @@
*/
package com.falsepattern.lib.updates;
import com.falsepattern.json.node.JsonNode;
import com.falsepattern.lib.StableAPI;
import com.falsepattern.lib.dependencies.DependencyLoader;
import com.falsepattern.lib.dependencies.Library;
import com.falsepattern.lib.dependencies.SemanticVersion;
import com.falsepattern.lib.internal.Internet;
import com.falsepattern.lib.internal.Tags;
import com.falsepattern.lib.internal.config.LibraryConfig;
import com.falsepattern.lib.text.FormattedText;
import lombok.val;
import com.falsepattern.lib.internal.impl.updates.UpdateCheckerImpl;
import net.minecraft.client.resources.I18n;
import net.minecraft.event.ClickEvent;
import net.minecraft.util.IChatComponent;
import cpw.mods.fml.common.Loader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicBoolean;
@StableAPI(since = "0.8.0")
public class UpdateChecker {
private static final AtomicBoolean jsonLibraryLoaded = new AtomicBoolean(false);
public final class UpdateChecker {
/**
* Checks for updates. The URL should be a JSON file that contains a list of mods, each with a mod ID, one or more
* versions, and a URL for the user to check for updates in case the current and latest versions are different.
@@ -76,89 +56,7 @@ public class UpdateChecker {
*/
@StableAPI.Expose
public static CompletableFuture<List<ModUpdateInfo>> fetchUpdatesAsync(String url) {
return CompletableFuture.supplyAsync(() -> {
if (!LibraryConfig.ENABLE_UPDATE_CHECKER) {
throw new CompletionException(new UpdateCheckException("Update checker is disabled in config!"));
}
URL URL;
try {
URL = new URL(url);
} catch (MalformedURLException e) {
throw new CompletionException(new UpdateCheckException("Invalid URL: " + url, e));
}
if (!jsonLibraryLoaded.get()) {
try {
DependencyLoader.addMavenRepo("https://maven.falsepattern.com/");
DependencyLoader.loadLibraries(Library.builder()
.loadingModId(Tags.MODID)
.groupId("com.falsepattern")
.artifactId("json")
.minVersion(new SemanticVersion(0, 4, 0))
.maxVersion(new SemanticVersion(0, Integer.MAX_VALUE, Integer.MAX_VALUE))
.preferredVersion(new SemanticVersion(0, 4, 1))
.build());
} catch (Exception e) {
throw new CompletionException(
new UpdateCheckException("Failed to load json library for update checker!", e));
}
jsonLibraryLoaded.set(true);
}
val result = new ArrayList<ModUpdateInfo>();
JsonNode parsed;
try {
parsed = JsonNode.parse(Internet.download(URL).thenApply(String::new).join());
} catch (CompletionException e) {
throw new CompletionException(new UpdateCheckException("Failed to download update checker JSON file!",
e.getCause() == null ? e : e.getCause()));
}
List<JsonNode> modList;
if (parsed.isList()) {
modList = parsed.getJavaList();
} else {
modList = Collections.singletonList(parsed);
}
val installedMods = Loader.instance().getIndexedModList();
for (val node : modList) {
if (!node.isObject()) {
continue;
}
if (!node.containsKey("modid")) {
continue;
}
if (!node.containsKey("latestVersion")) {
continue;
}
val modid = node.getString("modid");
if (!installedMods.containsKey(modid)) {
continue;
}
val mod = installedMods.get(modid);
val latestVersionsNode = node.get("latestVersion");
List<String> latestVersions;
if (latestVersionsNode.isString()) {
latestVersions = Collections.singletonList(latestVersionsNode.stringValue());
} else if (latestVersionsNode.isList()) {
latestVersions = new ArrayList<>();
for (val version : latestVersionsNode.getJavaList()) {
if (!version.isString()) {
continue;
}
latestVersions.add(version.stringValue());
}
} else {
continue;
}
val currentVersion = mod.getVersion();
if (latestVersions.contains(currentVersion)) {
continue;
}
val updateURL =
node.containsKey("updateURL") && node.get("updateURL").isString() ? node.getString("updateURL")
: "";
result.add(new ModUpdateInfo(modid, currentVersion, latestVersions.get(0), updateURL));
}
return result;
});
return UpdateCheckerImpl.fetchUpdatesAsync(url);
}
/**
@@ -172,17 +70,7 @@ public class UpdateChecker {
*/
@StableAPI.Expose
public static List<ModUpdateInfo> fetchUpdates(String url) throws UpdateCheckException {
try {
return fetchUpdatesAsync(url).join();
} catch (CompletionException e) {
try {
throw e.getCause();
} catch (UpdateCheckException e1) {
throw e1;
} catch (Throwable e1) {
throw new UpdateCheckException("Failed to check for updates!", e1);
}
}
return UpdateCheckerImpl.fetchUpdates(url);
}
/**
@@ -195,38 +83,6 @@ public class UpdateChecker {
*/
@StableAPI.Expose
public static List<IChatComponent> updateListToChatMessages(String initiator, List<ModUpdateInfo> updates) {
if (updates == null || updates.size() == 0) {
return null;
}
val updateText = new ArrayList<IChatComponent>(
FormattedText.parse(I18n.format("falsepatternlib.chat.updatesavailable", initiator)).toChatText());
val mods = Loader.instance().getIndexedModList();
for (val update : updates) {
val mod = mods.get(update.modID);
updateText.addAll(
FormattedText.parse(I18n.format("falsepatternlib.chat.modname", mod.getName())).toChatText());
updateText.addAll(
FormattedText.parse(I18n.format("falsepatternlib.chat.currentversion", update.currentVersion))
.toChatText());
updateText.addAll(
FormattedText.parse(I18n.format("falsepatternlib.chat.latestversion", update.latestVersion))
.toChatText());
if (!update.updateURL.isEmpty()) {
val pre = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurlpre")).toChatText();
val link = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurl")).toChatText();
val post = FormattedText.parse(I18n.format("falsepatternlib.chat.updateurlpost")).toChatText();
pre.get(pre.size() - 1).appendSibling(link.get(0));
link.get(link.size() - 1).appendSibling(post.get(0));
for (val l : link) {
l.getChatStyle().setChatClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, update.updateURL));
}
link.remove(0);
post.remove(0);
updateText.addAll(pre);
updateText.addAll(link);
updateText.addAll(post);
}
}
return updateText;
return UpdateCheckerImpl.updateListToChatMessages(initiator, updates);
}
}