diff --git a/build.gradle b/build.gradle index 931980c..c0a67c8 100644 --- a/build.gradle +++ b/build.gradle @@ -14,9 +14,13 @@ repositories { url "https://libraries.minecraft.net" name "minecraft" } + maven { + url "https://data.tilera.xyz/maven/" + } } dependencies { + implementation "ley.anvil:addonscript:1.0-SNAPSHOT" implementation group: 'net.lingala.zip4j', name: 'zip4j', version: '2.8.0' compileOnly group: 'com.google.code.gson', name: 'gson', version: '2.8.7' compileOnly group: "net.minecraft", name: "launchwrapper", version: "1.12" diff --git a/src/main/java/ley/modding/tcu/FileHandler.java b/src/main/java/ley/modding/tcu/FileHandler.java index 581990a..23a9854 100644 --- a/src/main/java/ley/modding/tcu/FileHandler.java +++ b/src/main/java/ley/modding/tcu/FileHandler.java @@ -1,7 +1,10 @@ package ley.modding.tcu; -import ley.modding.tcu.model.VersionDiff; +import ley.anvil.addonscript.v1.AddonscriptJSON; +import ley.modding.tcu.model.Config; +import ley.modding.tcu.model.RelationFile; import net.lingala.zip4j.ZipFile; +import net.lingala.zip4j.model.FileHeader; import java.io.BufferedInputStream; import java.io.File; @@ -9,6 +12,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; +import java.util.List; public class FileHandler { @@ -37,38 +41,55 @@ public class FileHandler { out.close(); } - public void processDiff(VersionDiff diff) { - - for (VersionDiff.RemoveFile rem : diff.remove) { - File f = getFile(rem.dir, rem.filename); - if (f.exists()) { - boolean del = f.delete(); - if (!del) - throw new RuntimeException("File deletion error"); - } + public void removeFiles(List rels) { + for (RelationFile rel : rels) { + File f = getFile(rel.dir, rel.filename()); + if (f.exists()) + f.delete(); } + } - for (VersionDiff.AddFile add : diff.add) { + public void addFiles(List rels) { + for (RelationFile rel : rels) { try { - downloadFile(add.dir, add.filename, new URL(add.url)); - } catch (Exception e) { + System.out.println("Downloading " + rel.id); + downloadFile(rel.dir, rel.filename(), new URL(rel.url)); + } catch (IOException e) { throw new RuntimeException(e); } } - } - public void processOverrides(URL overrides) { + public void processOverrides(String overrides, AddonscriptJSON as, Config config) { try { - downloadFile(".", "temp.zip", overrides); + System.out.println("Downloading overrides..."); + downloadFile(".", "temp.zip", new URL(overrides)); File tmp = getFile(".", "temp.zip"); ZipFile zip = new ZipFile(tmp); - zip.extractAll(gamedir.getPath()); + for (AddonscriptJSON.File f : as.versions.get(0).files) { + if (f.installer.equals("internal.override")) { + String loc = buildPath(config.repository, f.link); + List headers = zip.getFileHeaders(); + for (FileHeader header : headers) { + if (header.toString().startsWith(loc) && !header.isDirectory()) + zip.extractFile(header, ".", header.toString().replace(loc, "")); + } + } + } tmp.delete(); } catch (Exception e) { throw new RuntimeException(e); } + } + private String buildPath(String repo, String loc) { + String fileloc = loc.replace("file://", ""); + if (fileloc.startsWith("..")) { + fileloc = fileloc.replace("..", ""); + } else { + fileloc = "/src/" + fileloc; + } + return repo + fileloc + "/"; } } diff --git a/src/main/java/ley/modding/tcu/GiteaAPI.java b/src/main/java/ley/modding/tcu/GiteaAPI.java new file mode 100644 index 0000000..ec6600a --- /dev/null +++ b/src/main/java/ley/modding/tcu/GiteaAPI.java @@ -0,0 +1,68 @@ +package ley.modding.tcu; + +import ley.anvil.addonscript.v1.AddonscriptJSON; +import ley.modding.tcu.model.Release; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class GiteaAPI { + + String baseURL; + String repo; + String owner; + + public GiteaAPI(String baseURL, String owner, String repo) { + this.baseURL = baseURL; + this.repo = repo; + this.owner = owner; + if (!this.baseURL.endsWith("/")) { + this.baseURL += "/"; + } + } + + public AddonscriptJSON getASFromTag(String tag) { + String url = baseURL + owner + "/" + repo + "/raw/tag/" + tag + "/src/modpack.json"; + try { + URL u = new URL(url); + HttpURLConnection con = (HttpURLConnection) u.openConnection(); + con.setRequestMethod("GET"); + con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"); + if (con.getResponseCode() != 200) { + return null; + } + InputStreamReader reader = new InputStreamReader(con.getInputStream()); + AddonscriptJSON as = AddonscriptJSON.read(reader); + reader.close(); + return as; + } catch (IOException e) { + return null; + } + } + + public List getReleases() { + String url = baseURL + "api/v1/repos/" + owner + "/" + repo + "/releases"; + try { + URL u = new URL(url); + HttpURLConnection con = (HttpURLConnection) u.openConnection(); + con.setRequestMethod("GET"); + con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"); + if (con.getResponseCode() != 200) { + return new ArrayList<>(); + } + InputStreamReader reader = new InputStreamReader(con.getInputStream()); + Release[] releases = Release.fromJSON(reader); + reader.close(); + return Arrays.asList(releases); + } catch (IOException e) { + return new ArrayList<>(); + } + } + + +} diff --git a/src/main/java/ley/modding/tcu/UpdateTweaker.java b/src/main/java/ley/modding/tcu/UpdateTweaker.java index 8c2e735..6253a2c 100644 --- a/src/main/java/ley/modding/tcu/UpdateTweaker.java +++ b/src/main/java/ley/modding/tcu/UpdateTweaker.java @@ -1,12 +1,14 @@ package ley.modding.tcu; -import ley.modding.tcu.model.LocalPack; -import ley.modding.tcu.model.VersionDiff; +import ley.anvil.addonscript.v1.AddonscriptJSON; +import ley.modding.tcu.model.Config; +import ley.modding.tcu.model.RelationFile; +import ley.modding.tcu.model.Version; import net.minecraft.launchwrapper.ITweaker; import net.minecraft.launchwrapper.LaunchClassLoader; import java.io.File; -import java.net.URL; +import java.io.FileWriter; import java.util.List; public class UpdateTweaker implements ITweaker { @@ -32,20 +34,25 @@ public class UpdateTweaker implements ITweaker { public void injectIntoClassLoader(LaunchClassLoader classLoader) { this.classLoader = classLoader; try { - File modpack = new File(gameDir.getAbsolutePath() + "/modpack.json"); - if (modpack.exists()) { - String[] ver = Util.checkVersion(modpack); - if (ver.length == 0) + File configfile = new File(gameDir.getAbsolutePath() + "/pack.json"); + if (configfile.exists()) { + Config config = Util.getConfig(configfile); + Version version = Util.checkVersion(config); + if (version.isLatest()) return; - VersionDiff diff = Util.buildDiff(ver); + System.out.println(version.current + " is outdated, starting update..."); + AddonscriptJSON oldAS = config.getAPI().getASFromTag(version.current); + AddonscriptJSON newAS = config.getAPI().getASFromTag(version.latest); + List oldRel = Util.getRelations(oldAS); + List newRel = Util.getRelations(newAS); FileHandler handler = new FileHandler(gameDir); - handler.processDiff(diff); - URL overrides = Util.getOverrides(); - if (overrides != null) - handler.processOverrides(overrides); - LocalPack pack = Util.getLocal(modpack); - pack.version = ver[ver.length - 1]; - Util.writeLocal(modpack); + handler.removeFiles(Util.getToRemove(oldRel, newRel)); + handler.addFiles(Util.getToAdd(oldRel, newRel)); + handler.processOverrides(version.overrides, newAS, config); + config.version = version.latest; + FileWriter writer = new FileWriter(configfile); + config.toJson(writer); + writer.close(); } } catch (Exception e) { throw new RuntimeException(e); diff --git a/src/main/java/ley/modding/tcu/Util.java b/src/main/java/ley/modding/tcu/Util.java index 90a313e..2a473e3 100644 --- a/src/main/java/ley/modding/tcu/Util.java +++ b/src/main/java/ley/modding/tcu/Util.java @@ -1,119 +1,104 @@ package ley.modding.tcu; -import ley.modding.tcu.model.LocalPack; -import ley.modding.tcu.model.RemotePack; -import ley.modding.tcu.model.VersionDiff; +import ley.anvil.addonscript.v1.AddonscriptJSON; +import ley.modding.tcu.model.Config; +import ley.modding.tcu.model.RelationFile; +import ley.modding.tcu.model.Release; +import ley.modding.tcu.model.Version; -import java.io.*; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class Util { - private static RemotePack rcache = null; - private static LocalPack lcache = null; - - public static RemotePack getRemote(LocalPack pack) throws IOException { - if (rcache != null) - return rcache; - URL url = new URL(pack.packURL); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); - con.setRequestMethod("GET"); - con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"); - int status = con.getResponseCode(); - if (status != 200) { - return null; - } - InputStreamReader r2 = new InputStreamReader(con.getInputStream()); - RemotePack ret = RemotePack.fromJSON(r2); - r2.close(); - rcache = ret; - return ret; - } - - public static LocalPack getLocal(File local) throws IOException { - if (lcache != null) - return lcache; + public static Config getConfig(File local) throws IOException { Reader r = new FileReader(local); - LocalPack pack = LocalPack.fromJSON(r); + Config cfg = Config.fromJSON(r); r.close(); - lcache = pack; - return pack; + return cfg; } - public static void writeLocal(File local) throws IOException { - Writer w = new FileWriter(local); - lcache.toJson(w); - w.close(); + public static Version checkVersion(Config conf) { + GiteaAPI api = conf.getAPI(); + List releases = api.getReleases(); + return new Version(releases.get(0).tag, conf.version, releases.get(0).zipUrl); } - public static String[] checkVersion(File local) throws IOException { - LocalPack lpack = getLocal(local); + public static String resolve(String url) { + try { + HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); + con.setRequestMethod("HEAD"); + con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"); + con.setInstanceFollowRedirects(false); + int status = con.getResponseCode(); + if (status == 301 || status == 302) { + String loc = con.getHeaderField("location"); + return loc; + } else { + return url; + } + } catch (IOException e) { + return url; + } + } - RemotePack rpack = getRemote(lpack); + public static List getRelations(AddonscriptJSON as) { + List relations = new ArrayList<>(); + Map repos = new HashMap<>(); - if (rpack != null && rpack.versions.contains(lpack.version)) { - int index = rpack.versions.indexOf(lpack.version); - int lastIndex = rpack.versions.size() - 1; - if (index == lastIndex) { - return new String[0]; - } else { - String[] ret = new String[lastIndex - index]; - int j = 0; - for (int i = index + 1; i <= lastIndex; i++) { - ret [j] = rpack.versions.get(i); - j++; + for (AddonscriptJSON.Repository r : as.repositories) { + String url = r.url; + if (!url.endsWith("/")) { + url += "/"; + } + repos.put(r.id, url); + } + + for (AddonscriptJSON.Relation r : as.versions.get(0).relations) { + if (r.type.equals("modloader")) + continue; + RelationFile rel = new RelationFile(); + rel.id = r.id; + rel.dir = r.file.installer.split(":")[1]; + if (r.file.link != null && !r.file.link.isEmpty()) { + rel.url = r.file.link; + } else { + String[] parts = r.file.artifact.split(":"); + if (parts[0].equals("curse.maven")) { + parts[1] = parts[1] + "-" + parts[1]; } - return ret; - } - } - return new String[0]; - } - - public static VersionDiff buildDiff(String[] versions) throws IOException { - VersionDiff diff = new VersionDiff(); - diff.add = new ArrayList<>(); - diff.remove = new ArrayList<>(); - RemotePack pack = getRemote(lcache); - - for (String ver : versions) { - VersionDiff vdiff = pack.diff.get(ver); - if (vdiff != null) { - if (vdiff.remove != null) { - for (VersionDiff.RemoveFile f : vdiff.remove) { - boolean add = true; - List toRem = new ArrayList<>(); - for (VersionDiff.AddFile a : diff.add) { - if (a.dir.equals(f.dir) && a.filename.equals(f.filename)) { - add = false; - toRem.add(a); - } - } - diff.add.removeAll(toRem); - if (add) - diff.remove.add(f); - } + parts[0] = parts[0].replace('.', '/'); + rel.url = repos.get(r.file.repository) + parts[0] + "/" + parts[1] + "/" + parts[2] + "/" + parts[1] + "-" + parts[2]; + if (parts.length == 4) { + rel.url += "-"; + rel.url += parts[3]; } - if (vdiff.add != null) - diff.add.addAll(vdiff.add); + rel.url += ".jar"; } - } - return diff; + relations.add(rel); + } + return relations; } - public static URL getOverrides() { - if (rcache != null && rcache.overrides != null && !rcache.overrides.isEmpty()) { - try { - return new URL(rcache.overrides); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - } - return null; + public static List getToRemove(List old, List newrel) { + List oldRel = new ArrayList<>(old); + oldRel.removeAll(newrel); + return oldRel; + } + + public static List getToAdd(List old, List newrel) { + List newRel = new ArrayList<>(newrel); + newRel.removeAll(old); + return newRel; } } diff --git a/src/main/java/ley/modding/tcu/model/Config.java b/src/main/java/ley/modding/tcu/model/Config.java new file mode 100644 index 0000000..89b780a --- /dev/null +++ b/src/main/java/ley/modding/tcu/model/Config.java @@ -0,0 +1,31 @@ +package ley.modding.tcu.model; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import ley.modding.tcu.GiteaAPI; + +import java.io.Reader; +import java.io.Writer; + +public class Config { + + private static Gson gson = new GsonBuilder().create(); + + public static Config fromJSON(Reader reader) { + return gson.fromJson(reader, Config.class); + } + + public void toJson(Writer writer) { + gson.toJson(this, writer); + } + + public String version; + public String giteaInstance; + public String owner; + public String repository; + + public GiteaAPI getAPI() { + return new GiteaAPI(giteaInstance, owner, repository); + } + +} diff --git a/src/main/java/ley/modding/tcu/model/LocalPack.java b/src/main/java/ley/modding/tcu/model/LocalPack.java deleted file mode 100644 index 0945a8e..0000000 --- a/src/main/java/ley/modding/tcu/model/LocalPack.java +++ /dev/null @@ -1,24 +0,0 @@ -package ley.modding.tcu.model; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.io.Reader; -import java.io.Writer; - -public class LocalPack { - - private static Gson gson = new GsonBuilder().create(); - - public static LocalPack fromJSON(Reader reader) { - return gson.fromJson(reader, LocalPack.class); - } - - public void toJson(Writer writer) { - gson.toJson(this, writer); - } - - public String version; - public String packURL; - -} diff --git a/src/main/java/ley/modding/tcu/model/RelationFile.java b/src/main/java/ley/modding/tcu/model/RelationFile.java new file mode 100644 index 0000000..4b202fc --- /dev/null +++ b/src/main/java/ley/modding/tcu/model/RelationFile.java @@ -0,0 +1,37 @@ +package ley.modding.tcu.model; + +import ley.modding.tcu.Util; + +import java.util.Objects; + +public class RelationFile { + + public String id; + public String dir; + private String filename = null; + public String url; + + public String filename() { + if (filename == null) { + String url = Util.resolve(this.url); + String[] parts = url.split("/"); + filename = parts[parts.length - 1]; + } + return filename; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RelationFile that = (RelationFile) o; + return Objects.equals(id, that.id) && + Objects.equals(dir, that.dir) && + Objects.equals(url, that.url); + } + + @Override + public int hashCode() { + return Objects.hash(id, dir, filename, url); + } +} diff --git a/src/main/java/ley/modding/tcu/model/Release.java b/src/main/java/ley/modding/tcu/model/Release.java new file mode 100644 index 0000000..586442a --- /dev/null +++ b/src/main/java/ley/modding/tcu/model/Release.java @@ -0,0 +1,22 @@ +package ley.modding.tcu.model; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.SerializedName; + +import java.io.Reader; + +public class Release { + + private static Gson gson = new GsonBuilder().create(); + + public static Release[] fromJSON(Reader reader) { + return gson.fromJson(reader, Release[].class); + } + + @SerializedName("tag_name") + public String tag; + @SerializedName("zipball_url") + public String zipUrl; + +} diff --git a/src/main/java/ley/modding/tcu/model/RemotePack.java b/src/main/java/ley/modding/tcu/model/RemotePack.java deleted file mode 100644 index f7ef5dd..0000000 --- a/src/main/java/ley/modding/tcu/model/RemotePack.java +++ /dev/null @@ -1,22 +0,0 @@ -package ley.modding.tcu.model; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.io.Reader; -import java.util.List; -import java.util.Map; - -public class RemotePack { - - private static Gson gson = new GsonBuilder().create(); - - public static RemotePack fromJSON(Reader reader) { - return gson.fromJson(reader, RemotePack.class); - } - - public List versions; - public String overrides; - public Map diff; - -} diff --git a/src/main/java/ley/modding/tcu/model/Version.java b/src/main/java/ley/modding/tcu/model/Version.java new file mode 100644 index 0000000..6f79f4c --- /dev/null +++ b/src/main/java/ley/modding/tcu/model/Version.java @@ -0,0 +1,19 @@ +package ley.modding.tcu.model; + +public class Version { + + public String latest; + public String current; + public String overrides; + + public Version(String latest, String current, String overrides) { + this.latest = latest; + this.current = current; + this.overrides = overrides; + } + + public boolean isLatest() { + return latest.equals(current); + } + +} diff --git a/src/main/java/ley/modding/tcu/model/VersionDiff.java b/src/main/java/ley/modding/tcu/model/VersionDiff.java deleted file mode 100644 index cecb604..0000000 --- a/src/main/java/ley/modding/tcu/model/VersionDiff.java +++ /dev/null @@ -1,25 +0,0 @@ -package ley.modding.tcu.model; - -import java.util.List; - -public class VersionDiff { - - public List remove; - public List add; - - public static class RemoveFile { - - public String filename; - public String dir; - - } - - public static class AddFile { - - public String url; - public String filename; - public String dir; - - } - -}