diff --git a/build.gradle b/build.gradle index 1ab7371..ac5c3c8 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,9 @@ buildscript { plugins { id 'org.cadixdev.licenser' version '0.6.1' id 'com.github.johnrengelman.shadow' version '7.0.0' - id 'net.neoforged.gradleutils' version '3.0.0-alpha.10' + id 'com.diffplug.spotless' version '6.22.0' + id 'net.neoforged.gradleutils' version '3.0.0-alpha.11' + id 'net.neoforged.gradleutils.spotless' version '3.0.0-alpha.11' //id 'maven' id 'maven-publish' id 'java' @@ -20,6 +22,8 @@ plugins { id 'idea' } +spotlessUtils.configure(spotless) + repositories { mavenCentral() maven gradleutils.maven diff --git a/src/main/java/net/minecraftforge/installer/DownloadUtils.java b/src/main/java/net/minecraftforge/installer/DownloadUtils.java index c981c4b..ca1150f 100644 --- a/src/main/java/net/minecraftforge/installer/DownloadUtils.java +++ b/src/main/java/net/minecraftforge/installer/DownloadUtils.java @@ -1,37 +1,39 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer; +import net.minecraftforge.installer.actions.ProgressCallback; +import net.minecraftforge.installer.json.Artifact; +import net.minecraftforge.installer.json.Manifest; +import net.minecraftforge.installer.json.Mirror; +import net.minecraftforge.installer.json.Util; +import net.minecraftforge.installer.json.Version.Library; +import net.minecraftforge.installer.json.Version.LibraryDownload; + +import javax.net.ssl.SSLHandshakeException; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.MalformedURLException; -import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.net.UnknownHostException; import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.Collections; @@ -39,29 +41,15 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.net.ssl.SSLHandshakeException; - -import net.minecraftforge.installer.actions.ProgressCallback; -import net.minecraftforge.installer.json.Artifact; -import net.minecraftforge.installer.json.Manifest; -import net.minecraftforge.installer.json.Mirror; -import net.minecraftforge.installer.json.Util; -import net.minecraftforge.installer.json.Version.Download; -import net.minecraftforge.installer.json.Version.Library; -import net.minecraftforge.installer.json.Version.LibraryDownload; -import org.jetbrains.annotations.Nullable; - public class DownloadUtils { - public static final String LIBRARIES_URL = "https://libraries.minecraft.net/"; - public static final String MANIFEST_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json"; + public static final String MANIFEST_URL = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"; public static boolean OFFLINE_MODE = false; - private static final LocalSource LOCAL_SOURCE = LocalSource.detect(); - public static boolean downloadLibrary(ProgressCallback monitor, Mirror mirror, Library library, File root, Predicate optional, List grabbed, List additionalLibraryDirs) { + public static boolean downloadLibrary(ProgressCallback monitor, Library library, File root, Predicate optional, List grabbed, List additionalLibraryDirs) { Artifact artifact = library.getName(); File target = artifact.getLocalPath(root); - LibraryDownload download = library.getDownloads() == null ? null : library.getDownloads().getArtifact(); + LibraryDownload download = library.getDownloads() == null ? null : library.getDownloads().getArtifact(); if (download == null) { download = new LibraryDownload(); download.setPath(artifact.getPath()); @@ -74,153 +62,23 @@ public static boolean downloadLibrary(ProgressCallback monitor, Mirror mirror, L monitor.message(String.format("Considering library %s", artifact.getDescriptor())); - if (target.exists()) { - if (download.getSha1() != null) { - String sha1 = getSha1(target); - if (download.getSha1().equals(sha1)) { - monitor.message(" File exists: Checksum validated."); - return true; - } - monitor.message(" File exists: Checksum invalid, deleting file:"); - monitor.message(" Expected: " + download.getSha1()); - monitor.message(" Actual: " + sha1); - if (!target.delete()) { - monitor.stage(" Failed to delete file, aborting."); - return false; - } - } else { - monitor.message(" File exists: No checksum, Assuming valid."); - return true; - } - } - - target.getParentFile().mkdirs(); - - // Try extracting first - try (final InputStream input = LOCAL_SOURCE.getArtifact(artifact.getPath())) { - if (input != null) { - monitor.message(" Extracting library from /maven/" + artifact.getPath()); - Files.copy(input, target.toPath(), StandardCopyOption.REPLACE_EXISTING); - if (download.getSha1() != null) { - String sha1 = getSha1(target); - if (download.getSha1().equals(sha1)) { - monitor.message(" Extraction completed: Checksum validated."); - grabbed.add(artifact); - return true; - } - monitor.message(" Extraction failed: Checksum invalid, deleting file:"); - monitor.message(" Expected: " + download.getSha1()); - monitor.message(" Actual: " + sha1); - if (!target.delete()) { - monitor.stage(" Failed to delete file, aborting."); - return false; - } - return false; - } else { - monitor.message(" Extraction completed: No checksum, Assuming valid."); - } - grabbed.add(artifact); - return true; - } - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - // Try searching local installs if the file can be validated - if (download.getSha1() != null) { - String providedSha1 = download.getSha1(); - for (File libDir : additionalLibraryDirs) { - File inLibDir = new File(libDir, artifact.getPath()); - if (inLibDir.exists()) { - monitor.message(String.format(" Found artifact in local folder %s", libDir.toString())); - String sha1 = DownloadUtils.getSha1(inLibDir); - if (providedSha1.equals(sha1)) { - monitor.message(" Checksum validated"); - } else { - // Do not fail immediately. We may have other sources - monitor.message(" Invalid checksum. Not using."); - continue; - } - // Valid checksum, copy the lib - try { - Files.copy(inLibDir.toPath(), target.toPath()); - monitor.message(" Successfully copied local file"); - grabbed.add(artifact); - return true; - } catch (IOException e) { - // The copy may have failed when the file is in use. Don't abort, we may have other sources - e.printStackTrace(); - monitor.message(String.format(" Failed to copy from local folder: %s", e.toString())); - // Clean up the file that may have been created if the copy failed - if (target.exists()) { - if (!target.delete()) { - monitor.message(" Failed to delete failed copy, aborting"); - return false; - } - } - } - } - } - } - String url = download.getUrl(); if (url == null || url.isEmpty()) { monitor.message(" Invalid library, missing url"); return false; } - if (download(monitor, mirror, download, target)) { + if (monitor.downloader(url) + .additionalDirectory(additionalLibraryDirs.toArray(new File[0])) + .sha(download.getSha1()) + .localPath(download.getPath()) + .download(target)) { grabbed.add(artifact); return true; } return false; } - public static boolean download(ProgressCallback monitor, Mirror mirror, LibraryDownload download, File target) { - String url = download.getUrl(); - if (url.startsWith("http") && !url.startsWith(LIBRARIES_URL) && mirror != null && url.endsWith(download.getPath())) { - // TODO: Vanilla launcher is dumb so we fake classifier only deps. One day the launcher will be sane/document... - // Anyways, the path is not the same as the real maven path. So we don't have a good way to determine the mirrored url - if (download(monitor, mirror, download, target, mirror.getUrl() + download.getPath())) // Use unmirrored if mirror fails. - return true; - } - return download(monitor, mirror, download, target, url); - } - - public static boolean download(ProgressCallback monitor, Mirror mirror, Download download, File target) { - return download(monitor, mirror, download, target, download.getUrl()); - } - - private static boolean download(ProgressCallback monitor, Mirror mirror, Download download, File target, String url) { - monitor.message(" Downloading library from " + url); - try { - URLConnection connection = getConnection(url); - if (connection != null) { - Files.copy(monitor.wrapStepDownload(connection), target.toPath(), StandardCopyOption.REPLACE_EXISTING); - - if (download.getSha1() != null) { - String sha1 = getSha1(target); - if (download.getSha1().equals(sha1)) { - monitor.message(" Download completed: Checksum validated."); - return true; - } - monitor.message(" Download failed: Checksum invalid, deleting file:"); - monitor.message(" Expected: " + download.getSha1()); - monitor.message(" Actual: " + sha1); - if (!target.delete()) { - monitor.stage(" Failed to delete file, aborting."); - return false; - } - } - monitor.message(" Download completed: No checksum, Assuming valid."); - } - } catch (IOException e) { - e.printStackTrace(); - } - return false; - } - public static String getSha1(File target) { try { return HashFunction.SHA1.hash(Files.readAllBytes(target.toPath())).toString(); @@ -237,13 +95,13 @@ private static boolean checksumValid(File target, String checksum) { return sha1 != null && sha1.equals(checksum); } - private static URLConnection getConnection(String address) { + public static URLConnection getConnection(String address) { if (OFFLINE_MODE) { System.out.println("Offline Mode: Not downloading: " + address); return null; } - URL url = null; + URL url; try { url = new URL(address); } catch (MalformedURLException e) { @@ -259,13 +117,13 @@ private static URLConnection getConnection(String address) { connection.setConnectTimeout(5000); connection.setReadTimeout(5000); if (connection instanceof HttpURLConnection) { - HttpURLConnection hcon = (HttpURLConnection)connection; + HttpURLConnection hcon = (HttpURLConnection) connection; hcon.setInstanceFollowRedirects(false); int res = hcon.getResponseCode(); if (res == HttpURLConnection.HTTP_MOVED_PERM || res == HttpURLConnection.HTTP_MOVED_TEMP) { String location = hcon.getHeaderField("Location"); hcon.disconnect(); //Kill old connection. - if (x == MAX-1) { + if (x == MAX - 1) { System.out.println("Invalid number of redirects: " + location); return null; } else { @@ -302,29 +160,6 @@ public static List getIps(String host) { return Collections.emptyList(); } - public static boolean downloadFileEtag(File target, String url) { - try { - URLConnection connection = getConnection(url); - String etag = connection.getHeaderField("ETag"); - if (etag == null) - etag = "-"; - else if ((etag.startsWith("\"")) && (etag.endsWith("\""))) - etag = etag.substring(1, etag.length() - 1); - - Files.copy(connection.getInputStream(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); - - if (etag.indexOf('-') != -1) return true; //No-etag, assume valid - byte[] fileData = Files.readAllBytes(target.toPath()); - String md5 = HashFunction.MD5.hash(fileData).toString(); - System.out.println(" ETag: " + etag); - System.out.println(" MD5: " + md5); - return etag.equalsIgnoreCase(md5); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - public static Mirror[] downloadMirrors(String url) { try { URLConnection connection = getConnection(url); @@ -339,14 +174,11 @@ public static Mirror[] downloadMirrors(String url) { return null; } - public static Manifest downloadManifest() { - try { - URLConnection connection = getConnection(MANIFEST_URL); - if (connection != null) { - try (InputStream stream = connection.getInputStream()) { - return Util.loadManifest(stream); - } - } + public static Manifest downloadManifest(ProgressCallback callback) { + try (InputStream stream = callback.downloader(MANIFEST_URL) + .localPath("version_manifest.json") + .openStream()) { + return Util.loadManifest(stream); } catch (IOException e) { e.printStackTrace(); } @@ -404,73 +236,4 @@ public static boolean extractFile(String name, File target) { return false; } } - - public interface LocalSource { - - @Nullable - InputStream getArtifact(String path) throws IOException; - - default LocalSource fallbackWith(@Nullable LocalSource other) { - if (other == null) { - return this; - } - - return p -> { - final InputStream art = getArtifact(p); - return art != null ? art : other.getArtifact(p); - }; - } - - static LocalSource walkFromClassesOut(Path out) { - // The local path would be LegacyInstaller/build/classes/java/main, so walk upwards 4 times - for (int i = 0; i < 3; i++) { - out = out.getParent(); - } - - // The maven src dir is in src/main/resources/maven - final Path base = out.resolve("src/main/resources/maven"); - return fromDir(base); - } - - @Nullable - static LocalSource walkFromLibs(Path source) { - source = source.getParent(); - if (source == null || source.getFileName() == null || !source.getFileName().toString().equals("libs")) return null; - source = source.getParent(); - if (source == null || source.getFileName() == null || !source.getFileName().toString().equals("build")) return null; - source = source.getParent(); - if (source == null) return null; - - final Path base = source.resolve("src/main/resources/maven"); - return Files.isDirectory(base) ? fromDir(base) : null; - } - - static LocalSource fromDir(Path base) { - return p -> { - final Path children = base.resolve(p); - try { - return Files.newInputStream(children); - } catch (NoSuchFileException ex) { - return null; - } - }; - } - - static LocalSource fromResource() { - return p -> DownloadUtils.class.getResourceAsStream("/maven/" + p); - } - - static LocalSource detect() { - try { - final URL url = DownloadUtils.class.getProtectionDomain().getCodeSource().getLocation(); - if (url.getProtocol().equals("file") && Files.isDirectory(Paths.get(url.toURI()))) { // If we're running local IDE, use the resources dir - return walkFromClassesOut(Paths.get(url.toURI())); - } - - return fromResource().fallbackWith(walkFromLibs(Paths.get(url.toURI()))); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - } } diff --git a/src/main/java/net/minecraftforge/installer/Downloader.java b/src/main/java/net/minecraftforge/installer/Downloader.java new file mode 100644 index 0000000..b9e10a3 --- /dev/null +++ b/src/main/java/net/minecraftforge/installer/Downloader.java @@ -0,0 +1,245 @@ +/* + * Installer + * Copyright (c) 2016-2018. + * This library 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 version 2.1 + * of the License. + * This library 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 + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package net.minecraftforge.installer; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import net.minecraftforge.installer.actions.ProgressCallback; +import org.jetbrains.annotations.CheckReturnValue; +import org.jetbrains.annotations.Nullable; + +@CheckReturnValue +public class Downloader { + private static final Logger LOGGER = LogManager.getLogManager().getLogger("Downloading"); + public static final LocalSource LOCAL = LocalSource.detect(); + + private LocalSource localSource; + private final ProgressCallback monitor; + private final String url; + private String sha1, localPath; + + public Downloader(LocalSource localSource, ProgressCallback monitor, String url) { + this.localSource = localSource; + this.monitor = monitor; + this.url = url; + } + + public Downloader sha(@Nullable String sha) { + this.sha1 = sha; + return this; + } + + public Downloader localPath(@Nullable String localPath) { + this.localPath = localPath; + return this; + } + + public Downloader additionalDirectory(File... dirs) { + for (File dir : dirs) { + this.localSource = this.localSource.fallbackWith(LocalSource.fromDir(dir.toPath())); + } + return this; + } + + public boolean download(File target) { + if (target.exists() && this.sha1 != null) { + if (Objects.equals(this.sha1, DownloadUtils.getSha1(target))) { + monitor.message("File " + target + " exists. Checksum valid."); + return true; + } else { + monitor.message("File " + target + " exists. Invalid checksum, deleting file."); + target.delete(); + } + } + + Path nio = target.toPath(); + try { + if (nio.getParent() != null) Files.createDirectories(nio.getParent()); + } catch (IOException exception) { + LOGGER.log(Level.WARNING, exception, () -> "Failed to create parent of " + target); + return false; + } + + if (localPath != null) { + try { + LocalFile alternative = this.localSource.getArtifact(localPath); + if (alternative != null) { + Files.copy(alternative.stream, nio, StandardCopyOption.REPLACE_EXISTING); + if (this.sha1 != null) { + String actualSha = DownloadUtils.getSha1(target); + if (!Objects.equals(actualSha, this.sha1)) { + monitor.message("Invalid checksum. Downloaded locally from " + alternative.path); + monitor.message("\tExpected: " + this.sha1); + monitor.message("\tActual: " + actualSha); + } else { + monitor.message("Downloaded file locally from " + alternative.path + ", valid checksum."); + return true; + } + } else { + monitor.message("Downloaded file locally from " + alternative.path + ", no checksum provided, assuming valid."); + return true; + } + } + } catch (IOException exception) { + LOGGER.log(Level.WARNING, exception, () -> "Failed to download from local download source"); + } + } + + if (DownloadUtils.OFFLINE_MODE) { + monitor.message("\tFound no cached library at " + target + ", expecting download from " + url + ", but running in offline mode."); + return false; + } + + monitor.message("Downloading library from " + url); + try { + URLConnection connection = DownloadUtils.getConnection(url); + if (connection != null) { + Files.copy(monitor.wrapStepDownload(connection), target.toPath(), StandardCopyOption.REPLACE_EXISTING); + + if (this.sha1 != null) { + String sha1 = DownloadUtils.getSha1(target); + if (Objects.equals(sha1, this.sha1)) { + monitor.message("\tDownload completed: Checksum validated."); + return true; + } + monitor.message("\tDownload failed: Checksum invalid, deleting file:"); + monitor.message("\t\tExpected: " + this.sha1); + monitor.message("\t\tActual: " + sha1); + if (!target.delete()) { + monitor.stage("\tFailed to delete file, aborting."); + return false; + } + } else { + monitor.message("\tDownload completed: No checksum, Assuming valid."); + return true; + } + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, e, () -> "Failed to download from " + url); + } + + return false; + } + + public InputStream openStream() throws IOException { + if (localPath != null) { + LocalFile alternative = this.localSource.getArtifact(localPath); + if (alternative != null) { + return alternative.stream; + } + } + + if (DownloadUtils.OFFLINE_MODE) { + monitor.message("\tLibrary not cached, expecting download from " + url + ", but running in offline mode."); + throw new RuntimeException("Running in offline mode, cannot download from " + url + ", cached version not found"); + } + return monitor.wrapStepDownload(DownloadUtils.getConnection(url)); + } + + public static class LocalFile { + public final InputStream stream; + public final String path; + + public LocalFile(InputStream stream, String path) { + this.stream = stream; + this.path = path; + } + } + + public interface LocalSource { + @Nullable + LocalFile getArtifact(String path) throws IOException; + + default LocalSource fallbackWith(@Nullable LocalSource other) { + if (other == null) { + return this; + } + + return p -> { + final LocalFile art = getArtifact(p); + return art != null ? art : other.getArtifact(p); + }; + } + + static LocalSource walkFromClassesOut(Path out) { + // The local path would be LegacyInstaller/build/classes/java/main, so walk upwards 4 times + for (int i = 0; i < 3; i++) { + out = out.getParent(); + } + + // The maven src dir is in src/main/resources/maven + final Path base = out.resolve("src/main/resources/maven"); + return fromDir(base); + } + + @Nullable + static LocalSource walkFromLibs(Path source) { + source = source.getParent(); + if (source == null || source.getFileName() == null || !source.getFileName().toString().equals("libs")) return null; + source = source.getParent(); + if (source == null || source.getFileName() == null || !source.getFileName().toString().equals("build")) return null; + source = source.getParent(); + if (source == null) return null; + + final Path base = source.resolve("src/main/resources/maven"); + return Files.isDirectory(base) ? fromDir(base) : null; + } + + static LocalSource fromDir(Path base) { + return p -> { + final Path children = base.resolve(p); + try { + return new LocalFile(Files.newInputStream(children), children.toFile().getAbsolutePath()); + } catch (NoSuchFileException ex) { + return null; + } + }; + } + + static LocalSource fromResource() { + return p -> { + InputStream is = DownloadUtils.class.getResourceAsStream("/maven/" + p); + return is == null ? null : new LocalFile(is, "jar:/maven/" + p); + }; + } + + static LocalSource detect() { + try { + final URL url = DownloadUtils.class.getProtectionDomain().getCodeSource().getLocation(); + if (url.getProtocol().equals("file") && Files.isDirectory(Paths.get(url.toURI()))) { // If we're running local IDE, use the resources dir + return walkFromClassesOut(Paths.get(url.toURI())); + } + + return fromResource().fallbackWith(walkFromLibs(Paths.get(url.toURI()))); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/main/java/net/minecraftforge/installer/FixSSL.java b/src/main/java/net/minecraftforge/installer/FixSSL.java index a54fec8..a23c4f3 100644 --- a/src/main/java/net/minecraftforge/installer/FixSSL.java +++ b/src/main/java/net/minecraftforge/installer/FixSSL.java @@ -1,28 +1,20 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer; -import net.minecraftforge.installer.actions.ProgressCallback; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -37,7 +29,10 @@ import java.util.Collections; import java.util.Map; import java.util.stream.Collectors; - +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import net.minecraftforge.installer.actions.ProgressCallback; /** * Ripped out of forge - modified to work for installer @@ -49,6 +44,7 @@ * https://letsencrypt.org/certificates/ * * To create the keystore, the following commands were run: + * *
  *     keytool -import -alias letsencryptisrgx1 -file isrgrootx1.pem -keystore lekeystore.jks -storetype jks -storepass supersecretpassword -v
  *     keytool -import -alias identrustx3 -file identrustx3.pem -keystore lekeystore.jks -storetype jks -storepass supersecretpassword -v
@@ -57,9 +53,7 @@
  * The PEM files were obtained from the above URL.
  */
 class FixSSL {
-
-    private static boolean hasJavaForDownload(ProgressCallback callback)
-    {
+    private static boolean hasJavaForDownload(ProgressCallback callback) {
         String javaVersion = System.getProperty("java.version");
         callback.message("Found java version " + javaVersion);
         if (javaVersion != null && javaVersion.startsWith("1.8.0_")) {
@@ -78,7 +72,7 @@ static void fixup(ProgressCallback callback) {
         if (hasJavaForDownload(callback)) return;
         try {
             final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
-            Path ksPath = Paths.get(System.getProperty("java.home"),"lib", "security", "cacerts");
+            Path ksPath = Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts");
             keyStore.load(Files.newInputStream(ksPath), "changeit".toCharArray());
             final Map jdkTrustStore = Collections.list(keyStore.aliases()).stream().collect(Collectors.toMap(a -> a, (String alias) -> {
                 try {
@@ -104,7 +98,7 @@ static void fixup(ProgressCallback callback) {
             for (Map.Entry entry : jdkTrustStore.entrySet()) {
                 mergedTrustStore.setCertificateEntry(entry.getKey(), entry.getValue());
             }
-            for (Map.Entry entry : leTrustStore.entrySet()) {
+            for (Map.Entry entry : leTrustStore.entrySet()) {
                 mergedTrustStore.setCertificateEntry(entry.getKey(), entry.getValue());
             }
 
diff --git a/src/main/java/net/minecraftforge/installer/HashFunction.java b/src/main/java/net/minecraftforge/installer/HashFunction.java
index 3fee7ac..49a77a7 100644
--- a/src/main/java/net/minecraftforge/installer/HashFunction.java
+++ b/src/main/java/net/minecraftforge/installer/HashFunction.java
@@ -1,20 +1,17 @@
 /*
  * Installer
  * Copyright (c) 2016-2018.
- *
  * This library 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 version 2.1
  * of the License.
- *
  * This library 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
- *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 package net.minecraftforge.installer;
 
@@ -28,7 +25,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.Locale;
 
-//These are all standard hashing functions the JRE is REQUIRED to have, so add a nice factory that doesn't require catching annoying exceptions;
+// These are all standard hashing functions the JRE is REQUIRED to have, so add a nice factory that doesn't require catching annoying exceptions;
 public enum HashFunction {
     MD5("md5", 32),
     SHA1("SHA-1", 40),
@@ -44,7 +41,7 @@ private HashFunction(String algo, int length) {
     }
 
     public String getExtension() {
-         return this.name().toLowerCase(Locale.ENGLISH);
+        return this.name().toLowerCase(Locale.ENGLISH);
     }
 
     public MessageDigest get() {
diff --git a/src/main/java/net/minecraftforge/installer/SimpleInstaller.java b/src/main/java/net/minecraftforge/installer/SimpleInstaller.java
index 138ecda..140b09d 100644
--- a/src/main/java/net/minecraftforge/installer/SimpleInstaller.java
+++ b/src/main/java/net/minecraftforge/installer/SimpleInstaller.java
@@ -1,22 +1,20 @@
 /*
  * Installer
  * Copyright (c) 2016-2018.
- *
  * This library 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 version 2.1
  * of the License.
- *
  * This library 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
- *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 package net.minecraftforge.installer;
+
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -26,20 +24,21 @@
 import java.io.PrintStream;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.nio.file.Files;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Locale;
+import java.util.jar.JarFile;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-
 import javax.swing.JOptionPane;
 import javax.swing.UIManager;
-
 import joptsimple.OptionParser;
 import joptsimple.OptionSet;
 import joptsimple.OptionSpec;
 import net.minecraftforge.installer.actions.Actions;
+import net.minecraftforge.installer.actions.FatInstallerAction;
 import net.minecraftforge.installer.actions.ProgressCallback;
 import net.minecraftforge.installer.json.InstallV1;
 import net.minecraftforge.installer.json.Util;
@@ -48,21 +47,16 @@
 import net.neoforged.cliutils.progress.ProgressManager;
 import net.neoforged.cliutils.progress.ProgressReporter;
 
-public class SimpleInstaller
-{
+public class SimpleInstaller {
     public static boolean headless = false;
     public static boolean debug = false;
     public static URL mirror = null;
 
-    public static void main(String[] args) throws IOException, URISyntaxException
-    {
+    public static void main(String[] args) throws IOException, URISyntaxException {
         ProgressCallback monitor;
-        try
-        {
+        try {
             monitor = ProgressCallback.withOutputs(System.out, getLog());
-        }
-        catch (FileNotFoundException e)
-        {
+        } catch (FileNotFoundException e) {
             e.printStackTrace();
             monitor = ProgressCallback.withOutputs(System.out);
         }
@@ -80,8 +74,7 @@ public static void main(String[] args) throws IOException, URISyntaxException
         monitor.message("Current Time: " + new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date()));
 
         File installer = new File(SimpleInstaller.class.getProtectionDomain().getCodeSource().getLocation().toURI());
-        if (installer.getAbsolutePath().contains("!/"))
-        {
+        if (installer.getAbsolutePath().contains("!/")) {
             monitor.stage("Due to java limitation, please do not run this jar in a folder ending with !");
             monitor.message(installer.getAbsolutePath());
             return;
@@ -90,8 +83,14 @@ public static void main(String[] args) throws IOException, URISyntaxException
         OptionParser parser = new OptionParser();
         OptionSpec clientInstallOption = parser.acceptsAll(Arrays.asList("installClient", "install-client"), "Install a client to the specified directory, defaulting to the MC installation directory").withOptionalArg().ofType(File.class).defaultsTo(getMCDir());
         OptionSpec serverInstallOption = parser.acceptsAll(Arrays.asList("installServer", "install-server"), "Install a server to the current directory").withOptionalArg().ofType(File.class).defaultsTo(new File("."));
-        OptionSpec extractOption = parser.accepts("extract", "Extract the contained jar file to the specified directory").withOptionalArg().ofType(File.class).defaultsTo(new File("."));
-        OptionSpec helpOption = parser.acceptsAll(Arrays.asList("h", "help"),"Help with this installer");
+
+        OptionSpec fatInstallerOption = parser.acceptsAll(Arrays.asList("fat-installer", "fat", "generate-fat"), "Generate a fat installer jar").withOptionalArg().ofType(File.class).defaultsTo(new File(installer.getParent(), installer.getName().replace(".jar", "-fat.jar")));
+        OptionSpec fatIncludeMC = parser.acceptsAll(Arrays.asList("fat-include-minecraft"), "Include the Minecraft client / server jar in the fat installer").availableIf(fatInstallerOption);
+        OptionSpec fatIncludeMCLibs = parser.acceptsAll(Arrays.asList("fat-include-minecraft-libs"), "Include the Minecraft libraries in the fat installer").availableIf(fatInstallerOption);
+        OptionSpec fatIncludeInstallerLibs = parser.acceptsAll(Arrays.asList("fat-include-installer-libs"), "Include the installer libraries in the fat installer").availableIf(fatInstallerOption);
+        OptionSpec fatOffline = parser.acceptsAll(Arrays.asList("fat-offline", "gen-offline", "generate-offline", "gf"), "Generate an online fat installer");
+
+        OptionSpec helpOption = parser.acceptsAll(Arrays.asList("h", "help"), "Help with this installer");
         OptionSpec offlineOption = parser.accepts("offline", "Don't attempt any network calls");
         OptionSpec debugOption = parser.accepts("debug", "Run in debug mode -- don't delete any files");
         OptionSpec mirrorOption = parser.accepts("mirror", "Use a specific mirror URL").withRequiredArg().ofType(URL.class);
@@ -107,19 +106,22 @@ public static void main(String[] args) throws IOException, URISyntaxException
             mirror = optionSet.valueOf(mirrorOption);
         }
 
-        if (optionSet.has(offlineOption))
-        {
+        boolean isOffline = optionSet.has(offlineOption);
+        if (Files.isRegularFile(installer.toPath())) {
+            try (JarFile jf = new JarFile(installer)) {
+                isOffline = isOffline | Boolean.parseBoolean(jf.getManifest().getMainAttributes().getValue("Offline"));
+            }
+        }
+        if (isOffline) {
             DownloadUtils.OFFLINE_MODE = true;
             monitor.message("ENABLING OFFLINE MODE");
-        }
-        else
-        {
-            for(String host : new String[] {
-                "maven.neoforged.net",
-                "libraries.minecraft.net",
-                "launchermeta.mojang.com",
-                "piston-meta.mojang.com",
-                "sessionserver.mojang.com",
+        } else {
+            for (String host : new String[] {
+                    "maven.neoforged.net",
+                    "libraries.minecraft.net",
+                    "launchermeta.mojang.com",
+                    "piston-meta.mojang.com",
+                    "sessionserver.mojang.com",
             }) {
                 monitor.message("Host: " + host + " [" + DownloadUtils.getIps(host).stream().collect(Collectors.joining(", ")) + "]");
             }
@@ -131,90 +133,85 @@ public static void main(String[] args) throws IOException, URISyntaxException
         if (optionSet.has(serverInstallOption)) {
             action = Actions.SERVER;
             target = optionSet.valueOf(serverInstallOption);
-        } else if (optionSet.has(extractOption)) {
-            action = Actions.EXTRACT;
-            target = optionSet.valueOf(extractOption);
         } else if (optionSet.has(clientInstallOption)) {
             action = Actions.CLIENT;
             target = optionSet.valueOf(clientInstallOption);
+        } else if (optionSet.has(fatInstallerOption) || optionSet.has(fatOffline)) {
+            action = Actions.FAT_INSTALLER;
+            target = optionSet.valueOf(fatInstallerOption);
+
+            if (optionSet.has(fatIncludeMC) || optionSet.has(fatOffline)) {
+                FatInstallerAction.OPTIONS.add(FatInstallerAction.Options.MC_JAR);
+            }
+            if (optionSet.has(fatIncludeMCLibs) || optionSet.has(fatOffline)) {
+                FatInstallerAction.OPTIONS.add(FatInstallerAction.Options.MC_LIBS);
+            }
+            if (optionSet.has(fatIncludeInstallerLibs) || optionSet.has(fatOffline)) {
+                FatInstallerAction.OPTIONS.add(FatInstallerAction.Options.INSTALLER_LIBS);
+            }
         }
 
-        if (action != null)
-        {
-            try
-            {
+        if (action != null) {
+            try {
                 SimpleInstaller.headless = true;
                 monitor.message("Target Directory: " + target);
                 InstallV1 install = Util.loadInstallProfile();
-                if (!action.getAction(install, monitor).run(target, a -> true, installer))
-                {
+                if (!action.getAction(install, monitor).run(target, a -> true, installer)) {
                     monitor.stage("There was an error during installation");
                     System.exit(1);
-                }
-                else
-                {
+                } else {
                     monitor.message(action.getSuccess());
                     monitor.stage("You can delete this installer file now if you wish");
                 }
                 System.exit(0);
-            }
-            catch (Throwable e)
-            {
+            } catch (Throwable e) {
                 monitor.stage("A problem installing was detected, install cannot continue");
                 System.exit(1);
             }
-        }
-        else
+        } else
             launchGui(monitor, installer);
     }
 
-    public static File getMCDir()
-    {
+    public static File getMCDir() {
         String userHomeDir = System.getProperty("user.home", ".");
         String osType = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
         String mcDir = ".minecraft";
         if (osType.contains("win") && System.getenv("APPDATA") != null)
             return new File(System.getenv("APPDATA"), mcDir);
         else if (osType.contains("mac"))
-            return new File(new File(new File(userHomeDir, "Library"),"Application Support"),"minecraft");
+            return new File(new File(new File(userHomeDir, "Library"), "Application Support"), "minecraft");
         return new File(userHomeDir, mcDir);
     }
 
-    private static void launchGui(ProgressCallback monitor, File installer)
-    {
-        try
-        {
+    private static void launchGui(ProgressCallback monitor, File installer) {
+        try {
             UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
-        }
-        catch (Exception e)
-        {
-        }
+        } catch (Exception e) {}
 
         try {
             InstallV1 profile = Util.loadInstallProfile();
             InstallerPanel panel = new InstallerPanel(getMCDir(), profile, installer);
             panel.run(monitor);
         } catch (Throwable e) {
-            JOptionPane.showMessageDialog(null,"Something went wrong while installing.
Check log for more details:
" + e.toString(), "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, "Something went wrong while installing.
Check log for more details:
" + e.toString(), "Error", JOptionPane.ERROR_MESSAGE); } } - private static OutputStream getLog() throws FileNotFoundException - { + private static OutputStream getLog() throws FileNotFoundException { File f = new File(SimpleInstaller.class.getProtectionDomain().getCodeSource().getLocation().getFile()); File output; if (f.isFile()) output = new File(f.getName() + ".log"); - else output = new File("installer.log"); + else output = new File("installer.log"); System.out.println("Outputting log to file " + output); return new BufferedOutputStream(new FileOutputStream(output)); } - public static void hookStdOut(ProgressCallback monitor) - { + public static void hookStdOut(ProgressCallback monitor) { final Pattern endingWhitespace = Pattern.compile("\\r?\\n$"); final OutputStream monitorStream = new OutputStream() { private StringBuffer buffer = new StringBuffer(); + @Override public void write(byte[] buf, int off, int len) { for (int i = off; i < off + len; i++) { diff --git a/src/main/java/net/minecraftforge/installer/actions/Action.java b/src/main/java/net/minecraftforge/installer/actions/Action.java index 05b107a..2903ca3 100644 --- a/src/main/java/net/minecraftforge/installer/actions/Action.java +++ b/src/main/java/net/minecraftforge/installer/actions/Action.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; @@ -23,9 +20,7 @@ import java.util.Arrays; import java.util.List; import java.util.function.Predicate; - import javax.swing.JOptionPane; - import net.minecraftforge.installer.DownloadUtils; import net.minecraftforge.installer.SimpleInstaller; import net.minecraftforge.installer.json.Artifact; @@ -57,7 +52,9 @@ protected void error(String message) { } public abstract boolean run(File target, Predicate optionals, File installer) throws ActionCanceledException; + public abstract TargetValidator getTargetValidator(); + public abstract TranslatedMessage getSuccessMessage(); public String getSponsorMessage() { @@ -86,8 +83,8 @@ protected boolean downloadLibraries(File librariesDir, Predicate optiona final ProgressCallback targetMonitor = monitor.withoutDownloadProgress(); for (Library lib : libraries) { checkCancel(); - if (!DownloadUtils.downloadLibrary(targetMonitor, profile.getMirror(), lib, librariesDir, optionals, grabbed, additionalLibDirs)) { - LibraryDownload download = lib.getDownloads() == null ? null : lib.getDownloads().getArtifact(); + if (!DownloadUtils.downloadLibrary(targetMonitor, lib, librariesDir, optionals, grabbed, additionalLibDirs)) { + LibraryDownload download = lib.getDownloads() == null ? null : lib.getDownloads().getArtifact(); if (download != null && !download.getUrl().isEmpty()) // If it doesn't have a URL we can't download it, assume we install it later output.append('\n').append(lib.getName()); } diff --git a/src/main/java/net/minecraftforge/installer/actions/ActionCanceledException.java b/src/main/java/net/minecraftforge/installer/actions/ActionCanceledException.java index b100c5c..55bf2a2 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ActionCanceledException.java +++ b/src/main/java/net/minecraftforge/installer/actions/ActionCanceledException.java @@ -1,29 +1,24 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; -public class ActionCanceledException extends Exception -{ +public class ActionCanceledException extends Exception { private static final long serialVersionUID = 1L; - ActionCanceledException(Exception parent) - { + ActionCanceledException(Exception parent) { super(parent); } } diff --git a/src/main/java/net/minecraftforge/installer/actions/Actions.java b/src/main/java/net/minecraftforge/installer/actions/Actions.java index 4896bbe..ee4183b 100644 --- a/src/main/java/net/minecraftforge/installer/actions/Actions.java +++ b/src/main/java/net/minecraftforge/installer/actions/Actions.java @@ -1,59 +1,50 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; import java.util.function.BiFunction; import java.util.function.Supplier; - import net.minecraftforge.installer.json.InstallV1; -public enum Actions -{ +public enum Actions { CLIENT("installer.action.install.client.name", "installer.action.install.client.tooltip", ClientInstall::new, () -> "Successfully installed client into launcher."), SERVER("installer.action.install.server.name", "installer.action.install.server.tooltip", ServerInstall::new, () -> "The server installed successfully"), - EXTRACT("installer.action.extract.name", "installer.action.extract.tooltip", ExtractAction::new, () -> "All files successfully extract."); + FAT_INSTALLER("installer.action.install.fat.name", "installer.action.install.fat.tooltip", FatInstallerAction::new, () -> "Fat installer generated succcesfully"); private String label; private String tooltip; private BiFunction action; private Supplier success; - private Actions(String label, String tooltip, BiFunction action, Supplier success) - { + private Actions(String label, String tooltip, BiFunction action, Supplier success) { this.label = label; this.tooltip = tooltip; this.success = success; this.action = action; } - public String getButtonLabel() - { + public String getButtonLabel() { return label; } - public String getTooltip() - { + public String getTooltip() { return tooltip; } - public String getSuccess() - { + public String getSuccess() { return success.get(); } diff --git a/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java b/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java index ac2f9f2..a0926e4 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java +++ b/src/main/java/net/minecraftforge/installer/actions/ClientInstall.java @@ -1,40 +1,35 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.function.Predicate; -import net.minecraftforge.installer.DownloadUtils; import net.minecraftforge.installer.json.InstallV1; import net.minecraftforge.installer.json.Util; import net.minecraftforge.installer.json.Version; import net.minecraftforge.installer.json.Version.Download; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import net.minecraftforge.installer.ui.TranslatedMessage; public class ClientInstall extends Action { - public ClientInstall(InstallV1 profile, ProgressCallback monitor) { super(profile, monitor, true); } @@ -87,7 +82,7 @@ public boolean run(File target, Predicate optionals, File installer) thr File clientTarget = new File(versionVanilla, profile.getMinecraft() + ".jar"); if (!clientTarget.exists()) { File versionJson = new File(versionVanilla, profile.getMinecraft() + ".json"); - Version vanilla = Util.getVanillaVersion(profile.getMinecraft(), versionJson); + Version vanilla = Util.getVanillaVersion(monitor, profile.getMinecraft(), versionJson); if (vanilla == null) { error("Failed to download version manifest, can not find client jar URL."); return false; @@ -99,10 +94,13 @@ public boolean run(File target, Predicate optionals, File installer) thr return false; } - if (!DownloadUtils.download(monitor, profile.getMirror(), client, clientTarget)) { + if (!monitor.downloader(client.getUrl()) + .sha(client.getSha1()) + .localPath("minecraft/" + profile.getMinecraft() + "/client.jar") + .download(clientTarget)) { clientTarget.delete(); error("Downloading minecraft client failed, invalid checksum.\n" + - "Try again, or use the vanilla launcher to install the vanilla version."); + "Try again, or use the vanilla launcher to install the vanilla version."); return false; } } @@ -115,9 +113,9 @@ public boolean run(File target, Predicate optionals, File installer) thr /* String modListType = VersionInfo.getModListType(); File modListFile = new File(target, "mods/mod_list.json"); - + JsonRootNode versionJson = JsonNodeFactories.object(VersionInfo.getVersionInfo().getFields()); - + if ("absolute".equals(modListType)) { modListFile = new File(versionTarget, "mod_list.json"); @@ -132,7 +130,7 @@ public boolean run(File target, Predicate optionals, File installer) thr e.printStackTrace(); } } - + if (!"none".equals(modListType)) { if (!OptionalLibrary.saveModListJson(librariesDir, modListFile, VersionInfo.getOptionals(), optionals)) diff --git a/src/main/java/net/minecraftforge/installer/actions/ExtractAction.java b/src/main/java/net/minecraftforge/installer/actions/ExtractAction.java deleted file mode 100644 index f8bf873..0000000 --- a/src/main/java/net/minecraftforge/installer/actions/ExtractAction.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Installer - * Copyright (c) 2016-2018. - * - * This library 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 version 2.1 - * of the License. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package net.minecraftforge.installer.actions; - -import java.io.File; -import java.util.function.Predicate; - -import net.minecraftforge.installer.DownloadUtils; -import net.minecraftforge.installer.json.Artifact; -import net.minecraftforge.installer.json.InstallV1; -import net.minecraftforge.installer.ui.TranslatedMessage; - -public class ExtractAction extends Action { - - public ExtractAction(InstallV1 profile, ProgressCallback monitor) { - super(profile, monitor, true); - } - - public static boolean headless; - @Override - public boolean run(File target, Predicate optionals, File Installer) - { - boolean result = true; - String failed = "An error occurred extracting the files:"; - - Artifact contained = profile.getPath(); - if (contained != null) { - File file = new File(target, contained.getFilename()); - - if (!DownloadUtils.extractFile(contained, file, null)) { - result = false; - failed += "\n" + contained.getFilename(); - } - } - - /* - for (OptionalLibrary opt : VersionInfo.getOptionals()) - { - Artifact art = new Artifact(opt.getArtifact()); - InputStream input = ExtractAction.class.getResourceAsStream("/maven/" + art.getPath()); - if (input == null) - continue; - - File path = art.getLocalPath(new File(target, "libraries")); - File outFolder = art.getLocalPath(path).getParentFile(); - - if (!outFolder.exists()) - outFolder.mkdirs(); - - OutputSupplier outputSupplier = Files.newOutputStreamSupplier(path); - try - { - ByteStreams.copy(input, outputSupplier); - } - catch (IOException e) - { - result = false; - failed += "\n" + opt.getArtifact(); - } - } - */ - - if (!result) - error(failed); - - return result; - } - - @Override - public TargetValidator getTargetValidator() { - return TargetValidator.shouldExist(true) - .and(TargetValidator.isDirectory()); - } - - @Override - public TranslatedMessage getSuccessMessage() { - return new TranslatedMessage("installer.action.extract.finished"); - } -} diff --git a/src/main/java/net/minecraftforge/installer/actions/FatInstallerAction.java b/src/main/java/net/minecraftforge/installer/actions/FatInstallerAction.java new file mode 100644 index 0000000..69a57f7 --- /dev/null +++ b/src/main/java/net/minecraftforge/installer/actions/FatInstallerAction.java @@ -0,0 +1,158 @@ +/* + * Installer + * Copyright (c) 2016-2018. + * This library 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 version 2.1 + * of the License. + * This library 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 + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package net.minecraftforge.installer.actions; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import net.minecraftforge.installer.DownloadUtils; +import net.minecraftforge.installer.json.InstallV1; +import net.minecraftforge.installer.json.Util; +import net.minecraftforge.installer.json.Version; +import net.minecraftforge.installer.ui.TranslatedMessage; +import org.jetbrains.annotations.Nullable; + +public class FatInstallerAction extends Action { + public static final EnumSet OPTIONS = EnumSet.noneOf(Options.class); + + protected FatInstallerAction(InstallV1 profile, ProgressCallback monitor) { + super(profile, monitor, true); + } + + @Override + public boolean run(File target, Predicate optionals, File installer) throws ActionCanceledException { + try (final JarFile in = new JarFile(installer); + final JarOutputStream out = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(target)), newManifest(in.getManifest()))) { + Enumeration entries = in.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.getName().equals("META-INF/MANIFEST.MF")) continue; + ZipEntry ze = new ZipEntry(entry.getName()); + out.putNextEntry(ze); + copy(in.getInputStream(entry), out); + out.closeEntry(); + } + + monitor.stage("Downloading metadata"); + writeFromUrl(out, "version_manifest.json", DownloadUtils.MANIFEST_URL); + net.minecraftforge.installer.json.Manifest.Info man = DownloadUtils.downloadManifest(monitor).get(profile.getMinecraft()); + writeFromUrl(out, "minecraft/" + profile.getMinecraft() + ".json", man.getUrl()); + Version version = Util.getVersionUncached(monitor, man.getUrl()); + if (OPTIONS.contains(Options.MC_JAR)) { + monitor.stage("Downloading client jar"); + writeFromUrl(out, "minecraft/" + profile.getMinecraft() + "/client.jar", version.getDownload("client").getUrl()); + monitor.stage("Downloading server jar"); + writeFromUrl(out, "minecraft/" + profile.getMinecraft() + "/server.jar", version.getDownload("server").getUrl()); + + monitor.stage("Downloading client mappings"); + writeFromUrl(out, "minecraft/" + profile.getMinecraft() + "/client_mappings.txt", version.getDownload("client_mappings").getUrl()); + monitor.stage("Downloading server mappings"); + writeFromUrl(out, "minecraft/" + profile.getMinecraft() + "/server_mappings.txt", version.getDownload("server_mappings").getUrl()); + } + + if (OPTIONS.contains(Options.MC_LIBS) || OPTIONS.contains(Options.INSTALLER_LIBS)) { + final List libraries = new ArrayList<>(); + if (OPTIONS.contains(Options.MC_LIBS)) { + libraries.addAll(Arrays.asList(version.getLibraries())); + } + if (OPTIONS.contains(Options.INSTALLER_LIBS)) { + libraries.addAll(Arrays.asList(processors.getLibraries())); + } + + Set duplicates = new HashSet<>(); + libraries.removeIf(library -> !duplicates.add(library.getDownloads() == null ? null : library.getDownloads().getArtifact().getPath())); + monitor.stage("Downloading libraries"); + monitor.getGlobalProgress().setMaxProgress(libraries.size()); + int progress = 0; + for (Version.Library library : libraries) { + Version.LibraryDownload download = library.getDownloads() == null ? null : library.getDownloads().getArtifact(); + if (download != null) { + monitor.message("Downloading " + download.getPath()); + writeFromUrl(out, download.getPath(), download.getUrl(), download.getPath()); + } + monitor.getGlobalProgress().progress(++progress); + } + } + + return true; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void writeFromUrl(JarOutputStream jos, String name, String url) throws IOException { + writeFromUrl(jos, name, url, null); + } + + private void writeFromUrl(JarOutputStream jos, String name, String url, @Nullable String localPath) throws IOException { + JarEntry entry = new JarEntry("maven/" + name); + jos.putNextEntry(entry); + try (InputStream stream = monitor.downloader(url) + .localPath(localPath) + .openStream()) { + copy(stream, jos); + } + jos.closeEntry(); + } + + private Manifest newManifest(Manifest input) { + Manifest man = new Manifest(input); + if (OPTIONS.size() == 3) { + man.getMainAttributes().putValue("Offline", "true"); + } + return man; + } + + @Override + public TargetValidator getTargetValidator() { + return file -> TargetValidator.ValidationResult.valid(); + } + + @Override + public TranslatedMessage getSuccessMessage() { + return new TranslatedMessage("installer.action.install.fat.finished", profile.getVersion()); + } + + public enum Options { + MC_JAR, + MC_LIBS, + INSTALLER_LIBS + } + + private static void copy(InputStream source, OutputStream target) throws IOException { + byte[] buf = new byte[8192]; + int length; + while ((length = source.read(buf)) != -1) { + target.write(buf, 0, length); + } + } +} diff --git a/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java b/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java index c62441a..40d6280 100644 --- a/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java +++ b/src/main/java/net/minecraftforge/installer/actions/PostProcessors.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; @@ -36,17 +33,16 @@ import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.stream.Collectors; - import javax.swing.JOptionPane; - import net.minecraftforge.installer.DownloadUtils; +import net.minecraftforge.installer.Downloader; import net.minecraftforge.installer.SimpleInstaller; import net.minecraftforge.installer.actions.ProgressCallback.MessagePriority; import net.minecraftforge.installer.json.Artifact; import net.minecraftforge.installer.json.Install.Processor; import net.minecraftforge.installer.json.InstallV1; -import net.minecraftforge.installer.json.Version.Library; import net.minecraftforge.installer.json.Util; +import net.minecraftforge.installer.json.Version.Library; public class PostProcessors { private final InstallV1 profile; @@ -70,17 +66,17 @@ public Library[] getLibraries() { } public int getTaskCount() { - return hasTasks ? 0 : - profile.getLibraries().length + - processors.size() + - profile.getData(isClient).size(); + return hasTasks ? 0 + : profile.getLibraries().length + + processors.size() + + profile.getData(isClient).size(); } public boolean process(File librariesDir, File minecraft, File root, File installer) { try { if (!data.isEmpty()) { StringBuilder err = new StringBuilder(); - Path temp = Files.createTempDirectory("forge_installer"); + Path temp = Files.createTempDirectory("forge_installer"); monitor.start("Created Temporary Directory: " + temp); double steps = data.size(); int progress = 1; @@ -89,9 +85,9 @@ public boolean process(File librariesDir, File minecraft, File root, File instal String value = data.get(key); if (value.charAt(0) == '[' && value.charAt(value.length() - 1) == ']') { //Artifact - data.put(key, Artifact.from(value.substring(1, value.length() -1)).getLocalPath(librariesDir).getAbsolutePath()); + data.put(key, Artifact.from(value.substring(1, value.length() - 1)).getLocalPath(librariesDir).getAbsolutePath()); } else if (value.charAt(0) == '\'' && value.charAt(value.length() - 1) == '\'') { //Literal - data.put(key, value.substring(1, value.length() -1)); + data.put(key, value.substring(1, value.length() - 1)); } else { File target = Paths.get(temp.toString(), value).toFile(); monitor.message(" Extracting: " + value); @@ -112,6 +108,14 @@ public boolean process(File librariesDir, File minecraft, File root, File instal data.put("INSTALLER", installer.getAbsolutePath()); data.put("LIBRARY_DIR", librariesDir.getAbsolutePath()); + String localSource = "minecraft/" + profile.getMinecraft() + "/" + data.get("SIDE") + "_mappings.txt"; + boolean mojmapsSuccess = false; + if (Downloader.LOCAL.getArtifact(localSource) != null) { + mojmapsSuccess = monitor.downloader("") + .localPath(localSource) + .download(new File(data.get("MOJMAPS"))); + } + int progress = 0; if (processors.size() == 1) { monitor.stage("Building Processor"); @@ -123,7 +127,13 @@ public boolean process(File librariesDir, File minecraft, File root, File instal log("==============================================================================="); String procName = proc.getJar().getDomain() + ":" + proc.getJar().getName(); if (proc.getJar().getName().equals("installertools")) { - procName += (" -> " + proc.getArgs()[Arrays.asList(proc.getArgs()).indexOf("--task") + 1]); + String task = proc.getArgs()[Arrays.asList(proc.getArgs()).indexOf("--task") + 1]; + procName += (" -> " + task); + if (task.equals("DOWNLOAD_MOJMAPS") && mojmapsSuccess) { + monitor.message("Skipping mojmaps download due to local cache hit."); + monitor.getGlobalProgress().progress(++progress); + continue; + } } monitor.setCurrentStep("Processor: " + procName); @@ -232,7 +242,7 @@ public boolean process(File librariesDir, File minecraft, File root, File instal try { Class cls = Class.forName(mainClass, true, cl); Method main = cls.getDeclaredMethod("main", String[].class); - main.invoke(null, (Object)args.toArray(new String[args.size()])); + main.invoke(null, (Object) args.toArray(new String[args.size()])); } catch (InvocationTargetException ite) { Throwable e = ite.getCause(); e.printStackTrace(); @@ -264,8 +274,8 @@ public boolean process(File librariesDir, File minecraft, File root, File instal log(" Output: " + e.getKey() + " Checksum Validated: " + sha); } else { err.append("\n ").append(e.getKey()) - .append("\n Expected: ").append(e.getValue()) - .append("\n Actual: ").append(sha); + .append("\n Expected: ").append(e.getValue()) + .append("\n Actual: ").append(sha); if (!SimpleInstaller.debug && !artifact.delete()) err.append("\n Could not delete file"); } @@ -293,6 +303,7 @@ private void error(String message) { for (String line : message.split("\n")) monitor.message(line); } + private void log(String message) { for (String line : message.split("\n")) monitor.message(line); @@ -300,6 +311,7 @@ private void log(String message) { private static boolean clChecked = false; private static ClassLoader parentClassLoader = null; + @SuppressWarnings("unused") private synchronized ClassLoader getParentClassloader() { //Reflectively try and get the platform classloader, done this way to prevent hard dep on J9. if (!clChecked) { @@ -307,7 +319,7 @@ private synchronized ClassLoader getParentClassloader() { //Reflectively try and if (!System.getProperty("java.version").startsWith("1.")) { //in 9+ the changed from 1.8 to just 9. So this essentially detects if we're <9 try { Method getPlatform = ClassLoader.class.getDeclaredMethod("getPlatformClassLoader"); - parentClassLoader = (ClassLoader)getPlatform.invoke(null); + parentClassLoader = (ClassLoader) getPlatform.invoke(null); } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { log("No platform classloader: " + System.getProperty("java.version")); } diff --git a/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java b/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java index e2b0cf4..96ec71c 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java +++ b/src/main/java/net/minecraftforge/installer/actions/ProgressCallback.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; @@ -23,44 +20,39 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URLConnection; +import net.minecraftforge.installer.Downloader; -public interface ProgressCallback -{ - enum MessagePriority - { +public interface ProgressCallback { + enum MessagePriority { LOW, NORMAL, /** * Unused so far */ HIGH, } - - default void start(String label) - { + + default void start(String label) { message(label); } /** * Start a new step. */ - default void stage(String message, boolean withProgress) - { + default void stage(String message, boolean withProgress) { message(message); } /** * Start a new step with indefinite progress. */ - default void stage(String message) - { + default void stage(String message) { stage(message, false); } /** * @see #message(String, MessagePriority) */ - default void message(String message) - { + default void message(String message) { message(message, MessagePriority.NORMAL); } @@ -71,6 +63,7 @@ default void message(String message) void message(String message, MessagePriority priority); void setCurrentStep(String step); + String getCurrentStep(); default ProgressBar getGlobalProgress() { @@ -87,6 +80,10 @@ default InputStream wrapStepDownload(URLConnection connection) throws IOExceptio return wrapStepDownload(connection.getInputStream()); } + default Downloader downloader(String url) { + return new Downloader(Downloader.LOCAL, this, url); + } + default InputStream wrapStepDownload(InputStream in) { return new FilterInputStream(in) { private int nread = 0; @@ -98,7 +95,6 @@ public int read() throws IOException { return c; } - @Override public int read(byte[] b) throws IOException { int nr = in.read(b); @@ -106,17 +102,15 @@ public int read(byte[] b) throws IOException { return nr; } - @Override public int read(byte[] b, - int off, - int len) throws IOException { + int off, + int len) throws IOException { int nr = in.read(b, off, len); if (nr > 0) getStepProgress().progress(nread += nr); return nr; } - @Override public long skip(long n) throws IOException { long nr = in.skip(n); @@ -130,8 +124,7 @@ public long skip(long n) throws IOException { private String currentStep; @Override - public void message(String message, MessagePriority priority) - { + public void message(String message, MessagePriority priority) { System.out.println(message); } @@ -146,26 +139,19 @@ public void setCurrentStep(String step) { this.currentStep = step; } }; - - static ProgressCallback withOutputs(OutputStream... streams) - { - return new ProgressCallback() - { + + static ProgressCallback withOutputs(OutputStream... streams) { + return new ProgressCallback() { private String step; @Override - public void message(String message, MessagePriority priority) - { + public void message(String message, MessagePriority priority) { message = message + System.lineSeparator(); - for (OutputStream out : streams) - { - try - { + for (OutputStream out : streams) { + try { out.write(message.getBytes()); out.flush(); - } - catch (IOException e) - { + } catch (IOException e) { throw new RuntimeException(e); } } @@ -247,29 +233,24 @@ public String getCurrentStep() { interface ProgressBar { ProgressBar NOOP = new ProgressBar() { @Override - public void setMaxProgress(int maximum) { - - } + public void setMaxProgress(int maximum) {} @Override - public void progress(int value) { - - } + public void progress(int value) {} @Override - public void percentageProgress(double value) { - - } + public void percentageProgress(double value) {} @Override - public void setIndeterminate(boolean indeterminate) { - - } + public void setIndeterminate(boolean indeterminate) {} }; void setMaxProgress(int maximum); + void progress(int value); + void percentageProgress(double value); + void setIndeterminate(boolean indeterminate); } } diff --git a/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java b/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java index b0a8175..485fbf7 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java +++ b/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; @@ -24,8 +21,6 @@ import java.util.List; import java.util.Map; import java.util.function.Predicate; - -import net.minecraftforge.installer.DownloadUtils; import net.minecraftforge.installer.SimpleInstaller; import net.minecraftforge.installer.json.Artifact; import net.minecraftforge.installer.json.InstallV1; @@ -48,7 +43,7 @@ public boolean run(File target, Predicate optionals, File installer) thr return false; } - File librariesDir = new File(target,"libraries"); + File librariesDir = new File(target, "libraries"); if (!target.exists()) target.mkdirs(); librariesDir.mkdir(); @@ -56,18 +51,6 @@ public boolean run(File target, Predicate optionals, File installer) thr monitor.stage(getSponsorMessage()); checkCancel(); - // Extract main executable jar - Artifact contained = profile.getPath(); - if (contained != null) { - monitor.stage("Extracting main jar:"); - if (!DownloadUtils.extractFile(contained, new File(target, contained.getFilename()), null)) { - error(" Failed to extract main jar: " + contained.getFilename()); - return false; - } else - monitor.stage(" Extracted successfully"); - } - checkCancel(); - //Download MC Server jar monitor.stage("Considering Minecraft server jar", true); Map tokens = new HashMap<>(); @@ -84,7 +67,7 @@ public boolean run(File target, Predicate optionals, File installer) thr } File versionJson = new File(target, profile.getMinecraft() + ".json"); - Version vanilla = Util.getVanillaVersion(profile.getMinecraft(), versionJson); + Version vanilla = Util.getVanillaVersion(monitor, profile.getMinecraft(), versionJson); if (vanilla == null) { error("Failed to download version manifest, can not find server jar URL."); return false; @@ -97,10 +80,13 @@ public boolean run(File target, Predicate optionals, File installer) thr versionJson.delete(); - if (!DownloadUtils.download(monitor, profile.getMirror(), server, serverTarget)) { + if (!monitor.downloader(server.getUrl()) + .sha(server.getSha1()) + .localPath("minecraft/" + profile.getMinecraft() + "/server.jar") + .download(serverTarget)) { serverTarget.delete(); error("Downloading minecraft server failed, invalid checksum.\n" + - "Try again, or manually place server jar to skip download."); + "Try again, or manually place server jar to skip download."); return false; } } diff --git a/src/main/java/net/minecraftforge/installer/actions/TargetValidator.java b/src/main/java/net/minecraftforge/installer/actions/TargetValidator.java index f0ae0db..287bba1 100644 --- a/src/main/java/net/minecraftforge/installer/actions/TargetValidator.java +++ b/src/main/java/net/minecraftforge/installer/actions/TargetValidator.java @@ -1,36 +1,31 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.actions; -import net.minecraftforge.installer.ui.TranslatedMessage; -import org.jetbrains.annotations.NotNull; - import java.io.File; import java.util.Objects; import java.util.function.Supplier; +import net.minecraftforge.installer.ui.TranslatedMessage; +import org.jetbrains.annotations.NotNull; /** * A functional interface responsible for checking whether a target installation directory is valid. */ @FunctionalInterface public interface TargetValidator { - @NotNull ValidationResult validate(File target); diff --git a/src/main/java/net/minecraftforge/installer/json/Artifact.java b/src/main/java/net/minecraftforge/installer/json/Artifact.java index 4691889..560a219 100644 --- a/src/main/java/net/minecraftforge/installer/json/Artifact.java +++ b/src/main/java/net/minecraftforge/installer/json/Artifact.java @@ -1,26 +1,20 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.json; -import java.io.File; -import java.lang.reflect.Type; - import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; @@ -29,6 +23,8 @@ import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import java.io.File; +import java.lang.reflect.Type; public class Artifact { //Descriptor parts: group:name:version[:classifier][@extension] @@ -43,8 +39,7 @@ public class Artifact { private String file; private String descriptor; - public static Artifact from(String descriptor) - { + public static Artifact from(String descriptor) { Artifact ret = new Artifact(); ret.descriptor = descriptor; @@ -76,14 +71,38 @@ public File getLocalPath(File base) { return new File(base, path.replace('/', File.separatorChar)); } - public String getDescriptor(){ return descriptor; } - public String getPath() { return path; } - public String getDomain() { return domain; } - public String getName() { return name; } - public String getVersion() { return version; } - public String getClassifier(){ return classifier; } - public String getExt() { return ext; } - public String getFilename() { return file; } + public String getDescriptor() { + return descriptor; + } + + public String getPath() { + return path; + } + + public String getDomain() { + return domain; + } + + public String getName() { + return name; + } + + public String getVersion() { + return version; + } + + public String getClassifier() { + return classifier; + } + + public String getExt() { + return ext; + } + + public String getFilename() { + return file; + } + @Override public String toString() { return getDescriptor(); diff --git a/src/main/java/net/minecraftforge/installer/json/Install.java b/src/main/java/net/minecraftforge/installer/json/Install.java index 1bc17c4..2f272c6 100644 --- a/src/main/java/net/minecraftforge/installer/json/Install.java +++ b/src/main/java/net/minecraftforge/installer/json/Install.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.json; @@ -25,7 +22,6 @@ import java.util.Map.Entry; import java.util.Random; import java.util.stream.Collectors; - import net.minecraftforge.installer.DownloadUtils; import net.minecraftforge.installer.SimpleInstaller; @@ -207,6 +203,7 @@ public static class DataFile { public String getClient() { return client; } + public String getServer() { return server; } diff --git a/src/main/java/net/minecraftforge/installer/json/InstallV1.java b/src/main/java/net/minecraftforge/installer/json/InstallV1.java index d532d4a..e684892 100644 --- a/src/main/java/net/minecraftforge/installer/json/InstallV1.java +++ b/src/main/java/net/minecraftforge/installer/json/InstallV1.java @@ -1,30 +1,27 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.json; /* * Changes in v1 of the spec: * Adds a new value into the processor argument types: - * {ROOT} the root directory that we are installing to. - * {INSTALLER} the absolute path to the currently running installer. - * {MINECRAFT_VERSION} the version number specified in the config. - * {LIBRARY_DIR} Path to libraries folder. Typically {ROOT}/libraries/ but can be changed in the future. + * {ROOT} the root directory that we are installing to. + * {INSTALLER} the absolute path to the currently running installer. + * {MINECRAFT_VERSION} the version number specified in the config. + * {LIBRARY_DIR} Path to libraries folder. Typically {ROOT}/libraries/ but can be changed in the future. * Expands the token replacement for processors to allow in-line replacements. See Util.replaceTokens */ public class InstallV1 extends Install { @@ -59,5 +56,4 @@ public String getServerJarPath() { } return this.serverJarPath; } - } diff --git a/src/main/java/net/minecraftforge/installer/json/Manifest.java b/src/main/java/net/minecraftforge/installer/json/Manifest.java index afdeb4a..65343a6 100644 --- a/src/main/java/net/minecraftforge/installer/json/Manifest.java +++ b/src/main/java/net/minecraftforge/installer/json/Manifest.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.json; @@ -23,13 +20,14 @@ public class Manifest { private List versions; - public String getUrl(String version) { - return versions == null ? null : versions.stream().filter(v -> version.equals(v.getId())).map(Info::getUrl).findFirst().orElse(null); + public Info get(String version) { + return versions == null ? null : versions.stream().filter(v -> version.equals(v.getId())).findFirst().orElse(null); } public static class Info { private String id; private String url; + public String sha1; public String getId() { return id; diff --git a/src/main/java/net/minecraftforge/installer/json/Mirror.java b/src/main/java/net/minecraftforge/installer/json/Mirror.java index 81e9c89..1c60bc0 100644 --- a/src/main/java/net/minecraftforge/installer/json/Mirror.java +++ b/src/main/java/net/minecraftforge/installer/json/Mirror.java @@ -1,25 +1,21 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.json; import java.net.URL; - import javax.imageio.ImageIO; import javax.swing.Icon; import javax.swing.ImageIcon; @@ -59,15 +55,19 @@ public Icon getImage() { public String getName() { return name; } + public String getImageAddress() { return image; } + public String getHomepage() { return homepage; } + public String getUrl() { return url; } + public boolean isAdvertised() { return advertised; } diff --git a/src/main/java/net/minecraftforge/installer/json/OptionalLibrary.java b/src/main/java/net/minecraftforge/installer/json/OptionalLibrary.java index 8e53e4a..42bcabd 100644 --- a/src/main/java/net/minecraftforge/installer/json/OptionalLibrary.java +++ b/src/main/java/net/minecraftforge/installer/json/OptionalLibrary.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.json; @@ -42,21 +39,45 @@ public boolean isValid() { return this.name != null && this.artifact != null && this.maven != null; } - public String getName() { return this.name; } - public String getArtifact() { return this.artifact; } - public String getMaven() { return this.maven; } - public boolean isClient() { return this.client; } - public boolean isServer() { return this.server; } - public boolean getDefault() { return this._default; } - public boolean isInjected() { return this.inject; } - public String getDesc() { return this.desc; } - public String getURL() { return this.url; } - - public static boolean saveModListJson(File root, File json, List libs, Predicate filter) - { + public String getName() { + return this.name; + } + + public String getArtifact() { + return this.artifact; + } + + public String getMaven() { + return this.maven; + } + + public boolean isClient() { + return this.client; + } + + public boolean isServer() { + return this.server; + } + + public boolean getDefault() { + return this._default; + } + + public boolean isInjected() { + return this.inject; + } + + public String getDesc() { + return this.desc; + } + + public String getURL() { + return this.url; + } + + public static boolean saveModListJson(File root, File json, List libs, Predicate filter) { List artifacts = new ArrayList<>(); - for (OptionalLibrary lib : libs) - { + for (OptionalLibrary lib : libs) { if (filter.test(lib.getArtifact())) artifacts.add(lib.getArtifact()); } @@ -74,8 +95,7 @@ public static boolean saveModListJson(File root, File json, List tokens, String value) { if (x == value.length() - 1) throw new IllegalArgumentException("Illegal pattern (Bad escape): " + value); buf.append(value.charAt(++x)); - } else if (c == '{' || c == '\'') { + } else if (c == '{' || c == '\'') { StringBuilder key = new StringBuilder(); for (int y = x + 1; y <= value.length(); y++) { if (y == value.length()) diff --git a/src/main/java/net/minecraftforge/installer/json/Version.java b/src/main/java/net/minecraftforge/installer/json/Version.java index 9c50c76..0eb7cdb 100644 --- a/src/main/java/net/minecraftforge/installer/json/Version.java +++ b/src/main/java/net/minecraftforge/installer/json/Version.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.json; diff --git a/src/main/java/net/minecraftforge/installer/ui/Images.java b/src/main/java/net/minecraftforge/installer/ui/Images.java index 020000c..a2c5901 100644 --- a/src/main/java/net/minecraftforge/installer/ui/Images.java +++ b/src/main/java/net/minecraftforge/installer/ui/Images.java @@ -1,26 +1,20 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.ui; -import net.minecraftforge.installer.SimpleInstaller; - -import javax.imageio.ImageIO; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -28,10 +22,11 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import javax.imageio.ImageIO; +import net.minecraftforge.installer.SimpleInstaller; final class Images { - private Images() { - } + private Images() {} static List getWindowIcons() { List result = new ArrayList<>(); diff --git a/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java b/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java index 054fce0..bb68840 100644 --- a/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java +++ b/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java @@ -1,34 +1,20 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.ui; -import net.minecraftforge.installer.SimpleInstaller; -import net.minecraftforge.installer.actions.Action; -import net.minecraftforge.installer.actions.ActionCanceledException; -import net.minecraftforge.installer.actions.Actions; -import net.minecraftforge.installer.actions.ProgressCallback; -import net.minecraftforge.installer.actions.TargetValidator; -import net.minecraftforge.installer.json.InstallV1; -import net.minecraftforge.installer.json.OptionalLibrary; - -import javax.swing.*; -import javax.swing.border.LineBorder; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; @@ -43,9 +29,21 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; +import javax.swing.*; +import javax.swing.border.LineBorder; +import net.minecraftforge.installer.DownloadUtils; +import net.minecraftforge.installer.SimpleInstaller; +import net.minecraftforge.installer.actions.Action; +import net.minecraftforge.installer.actions.ActionCanceledException; +import net.minecraftforge.installer.actions.Actions; +import net.minecraftforge.installer.actions.FatInstallerAction; +import net.minecraftforge.installer.actions.ProgressCallback; +import net.minecraftforge.installer.actions.TargetValidator; +import net.minecraftforge.installer.json.InstallV1; +import net.minecraftforge.installer.json.OptionalLibrary; @SuppressWarnings("unused") public class InstallerPanel extends JPanel { @@ -61,7 +59,13 @@ public class InstallerPanel extends JPanel { private JDialog dialog; //private JLabel sponsorLogo; private JPanel sponsorPanel; + + private final AtomicReference action = new AtomicReference<>(Actions.CLIENT); private JPanel fileEntryPanel; + private JPanel fatInstallerOptionsPanel; + + private JCheckBox fatIncludeMC, fatIncludeMCLibs, fatIncludeInstallerLibs, fatOffline; + private List optionals = new ArrayList<>(); private Map> actions = new HashMap<>(); @@ -70,13 +74,11 @@ public class InstallerPanel extends JPanel { private final InstallV1 profile; private final File installer; - private class FileSelectAction extends AbstractAction - { + private class FileSelectAction extends AbstractAction { private static final long serialVersionUID = 1L; @Override - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { JFileChooser dirChooser = new JFileChooser(); dirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); dirChooser.setFileHidingEnabled(false); @@ -93,16 +95,13 @@ public void actionPerformed(ActionEvent e) } } - private class SelectButtonAction extends AbstractAction - { + private class SelectButtonAction extends AbstractAction { private static final long serialVersionUID = 1L; @Override - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { updateFilePath(); } - } //private static final String URL = "89504E470D0A1A0A0000000D4948445200000014000000160803000000F79F4C3400000012504C5445FFFFFFCCFFFF9999996666663333330000009E8B9AE70000000274524E53FF00E5B7304A000000564944415478016DCB410E003108425169E5FE579E98584246593EBF8165C24C5C614BED08455ECABC947929F392584A12CD8021EBEF91B0BD46A13969682BCC45E3706AE04E0DE0E42C819FA3D10F10BE954DC4C4DE07EB6A0497D14F4E8F0000000049454E44AE426082"; @@ -111,13 +110,12 @@ public static byte[] hexToByteArray(String s) { byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i+1), 16)); + + Character.digit(s.charAt(i + 1), 16)); } return data; } - public InstallerPanel(File targetDir, InstallV1 profile, File installer) - { + public InstallerPanel(File targetDir, InstallV1 profile, File installer) { this.profile = profile; this.installer = installer; @@ -152,6 +150,16 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) logoSplash.add(version); } + if (DownloadUtils.OFFLINE_MODE) { + JLabel offline = new JLabel(); + offline.setFont(offline.getFont().deriveFont(Font.BOLD)); + offline.setForeground(Color.RED); + TRANSLATIONS.translate(offline, TranslationTarget.LABEL_TEXT, "installer.welcome.offline"); + offline.setAlignmentX(CENTER_ALIGNMENT); + offline.setAlignmentY(CENTER_ALIGNMENT); + logoSplash.add(offline); + } + logoSplash.setAlignmentX(CENTER_ALIGNMENT); logoSplash.setAlignmentY(TOP_ALIGNMENT); this.add(logoSplash); @@ -177,11 +185,11 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) choicePanel.setLayout(new BoxLayout(choicePanel, BoxLayout.PAGE_AXIS)); boolean first = true; SelectButtonAction sba = new SelectButtonAction(); - for (Actions action : Actions.values()) - { + for (Actions action : Actions.values()) { if (action == Actions.CLIENT && profile.hideClient()) continue; if (action == Actions.SERVER && profile.hideServer()) continue; - if (action == Actions.EXTRACT && profile.hideExtract()) continue; + if (action == Actions.FAT_INSTALLER && DownloadUtils.OFFLINE_MODE) continue; + actions.put(action.name(), prog -> action.getAction(profile, prog)); JRadioButton radioButton = TRANSLATIONS.radioButton(sba, action.getButtonLabel()); TRANSLATIONS.setTooltip(radioButton, action.getTooltip()); @@ -193,21 +201,38 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) // The default gap of 4 is too small for the size of the buttons, so almost triple the gap // to avoid clipping and improve readability radioButton.setIconTextGap(10); - // Pad the button 5 pixels everywhere to avoid overlapping + // Pad the button 3 pixels everywhere to avoid overlapping radioButton.setMargin(new Insets(3, 3, 3, 3)); choiceButtonGroup.add(radioButton); choicePanel.add(radioButton); // Add a pixel between the buttons that ensures vertical separation and prevents clipping choicePanel.add(Box.createRigidArea(new Dimension(1, 1))); first = false; - } + radioButton.addChangeListener(e -> { + if (radioButton.isSelected()) { + this.action.set(action); + } + }); + + if (action == Actions.FAT_INSTALLER) { + radioButton.addChangeListener(e -> { + if (radioButton.isSelected()) { + this.remove(fileEntryPanel); + this.add(fatInstallerOptionsPanel); + } else { + this.add(fileEntryPanel); + this.remove(fatInstallerOptionsPanel); + } + }); + } + } choicePanel.setAlignmentX(CENTER_ALIGNMENT); choicePanel.setAlignmentY(CENTER_ALIGNMENT); add(choicePanel); JPanel entryPanel = new JPanel(); - entryPanel.setLayout(new BoxLayout(entryPanel,BoxLayout.X_AXIS)); + entryPanel.setLayout(new BoxLayout(entryPanel, BoxLayout.X_AXIS)); this.targetDir = targetDir; selectedDirText = new JTextField(); @@ -232,34 +257,61 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) infoLabel.setVisible(false); fileEntryPanel = new JPanel(); - fileEntryPanel.setLayout(new BoxLayout(fileEntryPanel,BoxLayout.Y_AXIS)); + fileEntryPanel.setLayout(new BoxLayout(fileEntryPanel, BoxLayout.Y_AXIS)); fileEntryPanel.add(infoLabel); fileEntryPanel.add(Box.createVerticalGlue()); fileEntryPanel.add(entryPanel); fileEntryPanel.setAlignmentX(CENTER_ALIGNMENT); fileEntryPanel.setAlignmentY(TOP_ALIGNMENT); this.add(fileEntryPanel); + + fatInstallerOptionsPanel = new JPanel(); + fatInstallerOptionsPanel.setLayout(new BoxLayout(fatInstallerOptionsPanel, BoxLayout.Y_AXIS)); + fatInstallerOptionsPanel.setAlignmentX(CENTER_ALIGNMENT); + fatInstallerOptionsPanel.setAlignmentY(TOP_ALIGNMENT); + + this.fatIncludeMC = TRANSLATIONS.checkBox("installer.fat.includemc"); + TRANSLATIONS.setTooltip(fatIncludeMC, "installer.fat.includemc.tooltip"); + this.fatIncludeMCLibs = TRANSLATIONS.checkBox("installer.fat.includemclibs"); + TRANSLATIONS.setTooltip(fatIncludeMCLibs, "installer.fat.includemclibs.tooltip"); + this.fatIncludeInstallerLibs = TRANSLATIONS.checkBox("installer.fat.includeinstallerlibs"); + TRANSLATIONS.setTooltip(fatIncludeInstallerLibs, "installer.fat.includeinstallerlibs.tooltip"); + fatInstallerOptionsPanel.add(fatIncludeMC); + fatInstallerOptionsPanel.add(fatIncludeMCLibs); + fatInstallerOptionsPanel.add(fatIncludeInstallerLibs); + + final List fatOptions = Arrays.asList(fatIncludeMC, fatIncludeMCLibs, fatIncludeInstallerLibs); + + this.fatOffline = TRANSLATIONS.checkBox("installer.fat.offline"); + TRANSLATIONS.setTooltip(fatOffline, "installer.fat.offline.tooltip"); + fatOffline.setMargin(new Insets(3, 0, 0, 0)); + fatOffline.addChangeListener(e -> { + if (fatOffline.isSelected()) { + fatOptions.forEach(box -> { + box.setSelected(true); + box.setEnabled(false); + }); + } else { + fatOptions.forEach(box -> box.setEnabled(true)); + } + }); + fatInstallerOptionsPanel.add(fatOffline); + updateFilePath(); } - - private void updateFilePath() - { - try - { + private void updateFilePath() { + try { targetDir = targetDir.getCanonicalFile(); selectedDirText.setText(targetDir.getPath()); - } - catch (IOException e) - { + } catch (IOException e) { } Action action = actions.get(choiceButtonGroup.getSelection().getActionCommand()).apply(null); TargetValidator.ValidationResult valid = action.getTargetValidator().validate(targetDir); - if (profile.getMirror() != null && profile.getMirror().isAdvertised()) - { + if (profile.getMirror() != null && profile.getMirror().isAdvertised()) { sponsorButton.setText(action.getSponsorMessage()); sponsorButton.setToolTipText(profile.getMirror().getHomepage()); if (profile.getMirror().getImageAddress() != null) @@ -267,9 +319,7 @@ private void updateFilePath() else sponsorButton.setIcon(null); sponsorPanel.setVisible(true); - } - else - { + } else { sponsorPanel.setVisible(false); } @@ -286,15 +336,13 @@ private void updateFilePath() proceedButton.ifPresent(button -> button.setEnabled(!valid.critical)); } - if (dialog!=null) - { + if (dialog != null) { dialog.invalidate(); dialog.pack(); } } - public void run(ProgressCallback monitor) - { + public void run(ProgressCallback monitor) { final JComboBox languageBox = new JComboBox<>(); final List known = TRANSLATIONS.getKnownLocales(); @@ -302,7 +350,7 @@ public void run(ProgressCallback monitor) final Locale current = TRANSLATIONS.getLocale(); languageBox.setSelectedItem(known.stream().filter(locate -> locate.locale.equals(current)).findFirst().orElse(known.get(0))); - languageBox.addActionListener(e -> TRANSLATIONS.setLocale(((L10nManager.LocaleSelection)languageBox.getSelectedItem()).locale, true)); + languageBox.addActionListener(e -> TRANSLATIONS.setLocale(((L10nManager.LocaleSelection) languageBox.getSelectedItem()).locale, true)); JButton proceedButton = TRANSLATIONS.button("installer.button.proceed"); JButton cancelButton = TRANSLATIONS.button("installer.button.cancel"); @@ -318,8 +366,30 @@ public void run(ProgressCallback monitor) dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setVisible(true); int result = (Integer) (optionPane.getValue() != null ? optionPane.getValue() : -1); - if (result == JOptionPane.OK_OPTION) - { + if (result == JOptionPane.OK_OPTION) { + if (action.get() == Actions.FAT_INSTALLER) { + if (fatIncludeMC.isSelected()) { + JOptionPane isOk = new JOptionPane(new JLabel(TRANSLATIONS.translate("installer.fat.includemc.warning")), JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); + JDialog okDialog = isOk.createDialog("WARNING"); + okDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + okDialog.setVisible(true); + + if (isOk.getValue() == null || (Integer) isOk.getValue() == JOptionPane.OK_CANCEL_OPTION) { + System.exit(0); + } + FatInstallerAction.OPTIONS.add(FatInstallerAction.Options.MC_JAR); + + okDialog.dispose(); + } + if (fatIncludeMCLibs.isSelected()) { + FatInstallerAction.OPTIONS.add(FatInstallerAction.Options.MC_LIBS); + } + if (fatIncludeInstallerLibs.isSelected()) { + FatInstallerAction.OPTIONS.add(FatInstallerAction.Options.INSTALLER_LIBS); + } + targetDir = new File(installer.getParent(), installer.getName().replace(".jar", "-fat.jar")); + } + ProgressFrame prog = new ProgressFrame(monitor, Thread.currentThread()::interrupt, "installer.frame.installing", profile.getProfile(), profile.getVersion()); SimpleInstaller.hookStdOut(prog); Predicate optPred = input -> { @@ -350,38 +420,36 @@ public void run(ProgressCallback monitor) dialog.dispose(); } - private void openURL(String url) - { - try - { + private void openURL(String url) { + try { Desktop.getDesktop().browse(new URI(url)); EventQueue.invokeLater(new Runnable() { @Override - public void run() - { + public void run() { InstallerPanel.this.dialog.toFront(); InstallerPanel.this.dialog.requestFocus(); } }); - } - catch (Exception ex) - { + } catch (Exception ex) { JOptionPane.showMessageDialog(InstallerPanel.this, "An error occurred launching the browser", "Error launching browser", JOptionPane.ERROR_MESSAGE); } } - private static class OptionalListEntry - { + private static class OptionalListEntry { OptionalLibrary lib; private boolean enabled = false; - OptionalListEntry(OptionalLibrary lib) - { + OptionalListEntry(OptionalLibrary lib) { this.lib = lib; this.enabled = lib.getDefault(); } - public boolean isEnabled(){ return this.enabled; } - public void setEnabled(boolean v){ this.enabled = v; } + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean v) { + this.enabled = v; + } } } diff --git a/src/main/java/net/minecraftforge/installer/ui/L10nManager.java b/src/main/java/net/minecraftforge/installer/ui/L10nManager.java index 374ae94..a4e29e4 100644 --- a/src/main/java/net/minecraftforge/installer/ui/L10nManager.java +++ b/src/main/java/net/minecraftforge/installer/ui/L10nManager.java @@ -1,33 +1,22 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.ui; -import org.jetbrains.annotations.NotNull; - -import javax.swing.AbstractAction; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JRadioButton; import java.awt.Component; import java.beans.PropertyChangeListener; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -50,6 +39,13 @@ import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JRadioButton; +import org.jetbrains.annotations.NotNull; public class L10nManager { private static final ResourceBundle.Control CONTROL = new ResourceBundle.Control() { @@ -94,7 +90,6 @@ public ResourceBundle newBundle(String baseName, Locale locale, String format, C props.loadFromXML(i); final Map lookup = new HashMap(props); return new ResourceBundle() { - @Override protected Object handleGetObject(@NotNull String key) { return lookup.get(key); @@ -167,6 +162,10 @@ public JRadioButton radioButton(AbstractAction action, String key, Object... arg return translate(button, TranslationTarget.BUTTON_TEXT, key, args); } + public JCheckBox checkBox(String key, Object... args) { + return translate(new JCheckBox(), TranslationTarget.BUTTON_TEXT, key, args); + } + public T setTooltip(T component, String key, Object... args) { return translate(component, TranslationTarget.TOOLTIP, key, args); } @@ -272,7 +271,6 @@ public String toString() { } private static final class MergedEnumeration implements Enumeration { - private final Set set; private final Iterator iterator; private final Enumeration enumeration; diff --git a/src/main/java/net/minecraftforge/installer/ui/ProgressFrame.java b/src/main/java/net/minecraftforge/installer/ui/ProgressFrame.java index a0b40f8..ccd6fab 100644 --- a/src/main/java/net/minecraftforge/installer/ui/ProgressFrame.java +++ b/src/main/java/net/minecraftforge/installer/ui/ProgressFrame.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.ui; @@ -22,15 +19,12 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; - import javax.swing.*; - import net.minecraftforge.installer.actions.ProgressCallback; -public class ProgressFrame extends JFrame implements ProgressCallback -{ +public class ProgressFrame extends JFrame implements ProgressCallback { private static final long serialVersionUID = 1L; - + private final ProgressCallback parent; private final JPanel panel = new JPanel(); @@ -42,12 +36,11 @@ public class ProgressFrame extends JFrame implements ProgressCallback private final ProgressBar stepProgressController; private final JTextArea consoleArea; - public ProgressFrame(ProgressCallback parent, Runnable canceler, String titleKey, Object... titleArgs) - { + public ProgressFrame(ProgressCallback parent, Runnable canceler, String titleKey, Object... titleArgs) { int gridY = 0; this.parent = parent; - + setResizable(false); InstallerPanel.TRANSLATIONS.translate(this, new TranslationTarget<>(JFrame::setTitle), titleKey, titleArgs); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); @@ -58,7 +51,7 @@ public ProgressFrame(ProgressCallback parent, Runnable canceler, String titleKey GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[] { 600, 0 }; - gridBagLayout.rowHeights = new int[] {0, 0, 0, 0, 200}; + gridBagLayout.rowHeights = new int[] { 0, 0, 0, 0, 200 }; gridBagLayout.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; gridBagLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 1.0 }; panel.setLayout(gridBagLayout); @@ -107,15 +100,14 @@ public ProgressFrame(ProgressCallback parent, Runnable canceler, String titleKey gbc_textArea.fill = GridBagConstraints.BOTH; gbc_textArea.gridx = 0; gbc_textArea.gridy = gridY; - + JScrollPane scroll = new JScrollPane(consoleArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scroll.setAutoscrolls(true); panel.add(scroll, gbc_textArea); } @Override - public void start(String label) - { + public void start(String label) { message(label, MessagePriority.HIGH, false); this.globalProgress.setValue(0); this.globalProgress.setIndeterminate(false); @@ -123,8 +115,7 @@ public void start(String label) } @Override - public void stage(String message, boolean withProgress) - { + public void stage(String message, boolean withProgress) { message(message, MessagePriority.HIGH, false); this.globalProgress.setIndeterminate(true); parent.stage(message); @@ -146,6 +137,7 @@ public ProgressBar getGlobalProgress() { } private String step; + @Override public String getCurrentStep() { return step; @@ -158,15 +150,12 @@ public void setCurrentStep(String step) { } @Override - public void message(String message, MessagePriority priority) - { + public void message(String message, MessagePriority priority) { message(message, priority, true); } - public void message(String message, MessagePriority priority, boolean notifyParent) - { - if (priority == MessagePriority.HIGH) - { + public void message(String message, MessagePriority priority, boolean notifyParent) { + if (priority == MessagePriority.HIGH) { this.progressText.setText(message); } consoleArea.append(message + "\n"); diff --git a/src/main/java/net/minecraftforge/installer/ui/TranslatedMessage.java b/src/main/java/net/minecraftforge/installer/ui/TranslatedMessage.java index e947246..f64283b 100644 --- a/src/main/java/net/minecraftforge/installer/ui/TranslatedMessage.java +++ b/src/main/java/net/minecraftforge/installer/ui/TranslatedMessage.java @@ -1,20 +1,17 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.ui; diff --git a/src/main/java/net/minecraftforge/installer/ui/TranslationTarget.java b/src/main/java/net/minecraftforge/installer/ui/TranslationTarget.java index 112bbfd..a9b47f0 100644 --- a/src/main/java/net/minecraftforge/installer/ui/TranslationTarget.java +++ b/src/main/java/net/minecraftforge/installer/ui/TranslationTarget.java @@ -1,30 +1,27 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.ui; -import javax.swing.*; import java.awt.*; import java.util.IdentityHashMap; import java.util.Map; import java.util.function.BiConsumer; +import javax.swing.*; -@SuppressWarnings({"unchecked", "rawtypes"}) +@SuppressWarnings({ "unchecked", "rawtypes" }) public final class TranslationTarget { public static final TranslationTarget LABEL_TEXT = new TranslationTarget<>(JLabel::setText); public static final TranslationTarget BUTTON_TEXT = new TranslationTarget<>(AbstractButton::setText); diff --git a/src/main/resources/neoforged/installer.xml b/src/main/resources/neoforged/installer.xml index 55f1b11..04162ee 100644 --- a/src/main/resources/neoforged/installer.xml +++ b/src/main/resources/neoforged/installer.xml @@ -2,6 +2,7 @@ Version: + Offline mode Path to the Minecraft installation directory Select an alternative Minecraft directory @@ -18,6 +19,10 @@ Successfully downloaded minecraft server and installed %s Successfully downloaded minecraft server, downloaded %d libraries and installed %s + Create fat installer + Create an installer that packs downloaded libs, for eventual offline use + Successfully created fat installer for version %s + Extract Extract the contained jar file Extracted successfully @@ -33,4 +38,16 @@ The specified directory does not exist The specified directory does not exist<br>It will be created The directory is missing a launcher profile. Please run the Minecraft launcher first + + Include Minecraft jars + <html>Include the Minecraft jars. <br> The output jar will include the Minecraft server and client jars, which are licensed as All Rights Reserved. <br> <b> Redistribution of the fat installer is illegal.</b></html> + The output jar will include the Minecraft server and client jars, which are licensed as All Rights Reserved. Redistribution of the fat installer is illegal. + + Include Minecraft libraries + Include the libraries used by Minecraft + Include installer libraries + Include the libraries used by the installer + + Offline installer + <html>Create an installer that can be run without an internet connection.<br> The offline installer will include the Minecraft server and client jars, which are licensed as All Rights Reserved. <br> <b> Redistribution of the offline installer is illegal.</b></html> diff --git a/src/test/java/net/minecraftforge/installer/test/TestTokens.java b/src/test/java/net/minecraftforge/installer/test/TestTokens.java index 991dfad..20efbde 100644 --- a/src/test/java/net/minecraftforge/installer/test/TestTokens.java +++ b/src/test/java/net/minecraftforge/installer/test/TestTokens.java @@ -1,30 +1,26 @@ /* * Installer * Copyright (c) 2016-2018. - * * This library 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 version 2.1 * of the License. - * * This library 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 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.installer.test; +import static org.junit.jupiter.api.Assertions.*; + import java.util.HashMap; import java.util.Map; - -import org.junit.jupiter.api.Test; - import net.minecraftforge.installer.json.Util; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class TestTokens { @Test