Skip to content

Commit

Permalink
Add an option to create fat installers
Browse files Browse the repository at this point in the history
Fat installers may be used as offline installers, as they pack libraries
  • Loading branch information
Matyrobbrt committed Apr 12, 2024
1 parent 030b2cb commit 3df82a1
Show file tree
Hide file tree
Showing 33 changed files with 951 additions and 844 deletions.
6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ 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'
id 'eclipse'
id 'idea'
}

spotlessUtils.configure(spotless)

repositories {
mavenCentral()
maven gradleutils.maven
Expand Down
293 changes: 28 additions & 265 deletions src/main/java/net/minecraftforge/installer/DownloadUtils.java

Large diffs are not rendered by default.

245 changes: 245 additions & 0 deletions src/main/java/net/minecraftforge/installer/Downloader.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}
26 changes: 10 additions & 16 deletions src/main/java/net/minecraftforge/installer/FixSSL.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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
Expand All @@ -49,6 +44,7 @@
* <a href="https://letsencrypt.org/certificates/">https://letsencrypt.org/certificates/</a>
*
* To create the keystore, the following commands were run:
*
* <pre>
* 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
Expand All @@ -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_")) {
Expand All @@ -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<String, Certificate> jdkTrustStore = Collections.list(keyStore.aliases()).stream().collect(Collectors.toMap(a -> a, (String alias) -> {
try {
Expand All @@ -104,7 +98,7 @@ static void fixup(ProgressCallback callback) {
for (Map.Entry<String, Certificate> entry : jdkTrustStore.entrySet()) {
mergedTrustStore.setCertificateEntry(entry.getKey(), entry.getValue());
}
for (Map.Entry<String , Certificate> entry : leTrustStore.entrySet()) {
for (Map.Entry<String, Certificate> entry : leTrustStore.entrySet()) {
mergedTrustStore.setCertificateEntry(entry.getKey(), entry.getValue());
}

Expand Down
11 changes: 4 additions & 7 deletions src/main/java/net/minecraftforge/installer/HashFunction.java
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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),
Expand All @@ -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() {
Expand Down
Loading

0 comments on commit 3df82a1

Please sign in to comment.