diff --git a/build.gradle b/build.gradle
index 2d3137fff..aa8f5763f 100755
--- a/build.gradle
+++ b/build.gradle
@@ -37,6 +37,8 @@ buildscript {
}
}
+import baritone.gradle.ProguardTask
+
apply plugin: 'java'
apply plugin: 'net.minecraftforge.gradle.tweaker-client'
apply plugin: 'org.spongepowered.mixin'
@@ -99,3 +101,11 @@ jar {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
+
+task proguard(type: ProguardTask) {
+ url 'https://downloads.sourceforge.net/project/proguard/proguard/6.0/proguard6.0.3.zip'
+ extract 'proguard6.0.3/lib/proguard.jar'
+ versionManifest 'https://launchermeta.mojang.com/mc/game/version_manifest.json'
+}
+
+build.finalizedBy(proguard)
diff --git a/buildSrc/.gitignore b/buildSrc/.gitignore
new file mode 100644
index 000000000..daf21d569
--- /dev/null
+++ b/buildSrc/.gitignore
@@ -0,0 +1,3 @@
+build/
+.gradle/
+out/
\ No newline at end of file
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 000000000..2ac49af0f
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,25 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone 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 Baritone. If not, see .
+ */
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
+ compile group: 'commons-io', name: 'commons-io', version: '2.6'
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/java/baritone/gradle/ProguardTask.java b/buildSrc/src/main/java/baritone/gradle/ProguardTask.java
new file mode 100644
index 000000000..7450d4717
--- /dev/null
+++ b/buildSrc/src/main/java/baritone/gradle/ProguardTask.java
@@ -0,0 +1,314 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone 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 Baritone. If not, see .
+ */
+
+package baritone.gradle;
+
+import baritone.gradle.util.Determinizer;
+import com.google.gson.*;
+import javafx.util.Pair;
+import org.apache.commons.io.IOUtils;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.*;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+/**
+ * @author Brady
+ * @since 10/11/2018
+ */
+public class ProguardTask extends DefaultTask {
+
+ private static final JsonParser PARSER = new JsonParser();
+
+ private static final Pattern TEMP_LIBRARY_PATTERN = Pattern.compile("-libraryjars 'tempLibraries\\/([a-zA-Z0-9/_\\-\\.]+)\\.jar'");
+
+ private static final String
+ PROGUARD_ZIP = "proguard.zip",
+ PROGUARD_JAR = "proguard.jar",
+ PROGUARD_CONFIG_TEMPLATE = "scripts/proguard.pro",
+ PROGUARD_CONFIG_DEST = "template.pro",
+ PROGUARD_API_CONFIG = "api.pro",
+ PROGUARD_STANDALONE_CONFIG = "standalone.pro",
+ PROGUARD_EXPORT_PATH = "proguard_out.jar",
+
+ VERSION_MANIFEST = "version_manifest.json",
+
+ TEMP_LIBRARY_DIR = "tempLibraries/",
+
+ ARTIFACT_UNOPTIMIZED = "%s-unoptimized-%s.jar",
+ ARTIFACT_API = "%s-api-%s.jar",
+ ARTIFACT_STANDALONE = "%s-standalone-%s.jar";
+
+ @Input
+ private String url;
+
+ @Input
+ private String extract;
+
+ @Input
+ private String versionManifest;
+
+ private String artifactName, artifactVersion;
+ private Path artifactPath, artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, proguardOut;
+ private Map versionDownloadMap;
+ private List requiredLibraries;
+
+ @TaskAction
+ private void exec() throws Exception {
+ // "Haha brady why don't you make separate tasks"
+ verifyArtifacts();
+ processArtifact();
+ downloadProguard();
+ extractProguard();
+ generateConfigs();
+ downloadVersionManifest();
+ acquireDependencies();
+ proguardApi();
+ proguardStandalone();
+ cleanup();
+ }
+
+ private void verifyArtifacts() throws Exception {
+ this.artifactName = getProject().getName();
+ this.artifactVersion = getProject().getVersion().toString();
+
+ // The compiled baritone artifact that is exported when the build task is ran
+ String artifactName = String.format("%s-%s.jar", this.artifactName, this.artifactVersion);
+
+ this.artifactPath = this.getBuildFile(artifactName);
+ this.artifactUnoptimizedPath = this.getBuildFile(String.format(ARTIFACT_UNOPTIMIZED, this.artifactName, this.artifactVersion));
+ this.artifactApiPath = this.getBuildFile(String.format(ARTIFACT_API, this.artifactName, this.artifactVersion));
+ this.artifactStandalonePath = this.getBuildFile(String.format(ARTIFACT_STANDALONE, this.artifactName, this.artifactVersion));
+
+ this.proguardOut = this.getTemporaryFile(PROGUARD_EXPORT_PATH);
+
+ if (!Files.exists(this.artifactPath)) {
+ throw new Exception("Artifact not found! Run build first!");
+ }
+ }
+
+ private void processArtifact() throws Exception {
+ if (Files.exists(this.artifactUnoptimizedPath)) {
+ Files.delete(this.artifactUnoptimizedPath);
+ }
+
+ Determinizer.main(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString());
+ }
+
+ private void downloadProguard() throws Exception {
+ Path proguardZip = getTemporaryFile(PROGUARD_ZIP);
+ if (!Files.exists(proguardZip)) {
+ write(new URL(this.url).openStream(), proguardZip);
+ }
+ }
+
+ private void extractProguard() throws Exception {
+ Path proguardJar = getTemporaryFile(PROGUARD_JAR);
+ if (!Files.exists(proguardJar)) {
+ ZipFile zipFile = new ZipFile(getTemporaryFile(PROGUARD_ZIP).toFile());
+ ZipEntry zipJarEntry = zipFile.getEntry(this.extract);
+ write(zipFile.getInputStream(zipJarEntry), proguardJar);
+ zipFile.close();
+ }
+ }
+
+ private void generateConfigs() throws Exception {
+ Files.copy(getRelativeFile(PROGUARD_CONFIG_TEMPLATE), getTemporaryFile(PROGUARD_CONFIG_DEST), REPLACE_EXISTING);
+
+ // Setup the template that will be used to derive the API and Standalone configs
+ List template = Files.readAllLines(getTemporaryFile(PROGUARD_CONFIG_DEST));
+ template.removeIf(s -> s.endsWith("# this is the rt jar") || s.startsWith("-injars") || s.startsWith("-outjars"));
+ template.add(0, "-injars " + this.artifactPath.toString());
+ template.add(1, "-outjars " + this.getTemporaryFile(PROGUARD_EXPORT_PATH));
+
+ // Acquire the RT jar using "java -verbose". This doesn't work on Java 9+
+ Process p = new ProcessBuilder("java", "-verbose").start();
+ String out = IOUtils.toString(p.getInputStream(), "UTF-8").split("\n")[0].split("Opened ")[1].replace("]", "");
+ template.add(2, "-libraryjars '" + out + "'");
+
+ // API config doesn't require any changes from the changes that we made to the template
+ Files.write(getTemporaryFile(PROGUARD_API_CONFIG), template);
+
+ // For the Standalone config, don't keep the API package
+ List standalone = new ArrayList<>(template);
+ standalone.removeIf(s -> s.contains("# this is the keep api"));
+ Files.write(getTemporaryFile(PROGUARD_STANDALONE_CONFIG), standalone);
+
+ // Discover all of the libraries that we will need to acquire from gradle
+ this.requiredLibraries = new ArrayList<>();
+ template.forEach(line -> {
+ if (!line.startsWith("#")) {
+ Matcher m = TEMP_LIBRARY_PATTERN.matcher(line);
+ if (m.find()) {
+ this.requiredLibraries.add(m.group(1));
+ }
+ }
+ });
+ }
+
+ private void downloadVersionManifest() throws Exception {
+ Path manifestJson = getTemporaryFile(VERSION_MANIFEST);
+ write(new URL(this.versionManifest).openStream(), manifestJson);
+
+ // Place all the versions in the map with their download URL
+ this.versionDownloadMap = new HashMap<>();
+ JsonObject json = readJson(Files.readAllLines(manifestJson)).getAsJsonObject();
+ JsonArray versions = json.getAsJsonArray("versions");
+ versions.forEach(element -> {
+ JsonObject object = element.getAsJsonObject();
+ this.versionDownloadMap.put(object.get("id").getAsString(), object.get("url").getAsString());
+ });
+ }
+
+ private void acquireDependencies() throws Exception {
+
+ // Create a map of all of the dependencies that we are able to access in this project
+ // Likely a better way to do this, I just pair the dependency with the first valid configuration
+ Map> dependencyLookupMap = new HashMap<>();
+ getProject().getConfigurations().stream().filter(Configuration::isCanBeResolved).forEach(config ->
+ config.getAllDependencies().forEach(dependency ->
+ dependencyLookupMap.putIfAbsent(dependency.getName() + "-" + dependency.getVersion(), new Pair<>(config, dependency))));
+
+ // Create the directory if it doesn't already exist
+ Path tempLibraries = getTemporaryFile(TEMP_LIBRARY_DIR);
+ if (!Files.exists(tempLibraries)) {
+ Files.createDirectory(tempLibraries);
+ }
+
+ // Iterate the required libraries to copy them to tempLibraries
+ for (String lib : this.requiredLibraries) {
+ // Download the version jar from the URL acquired from the version manifest
+ if (lib.startsWith("minecraft")) {
+ String version = lib.split("-")[1];
+ Path versionJar = getTemporaryFile("tempLibraries/" + lib + ".jar");
+ if (!Files.exists(versionJar)) {
+ write(new URL(this.versionDownloadMap.get(version)).openStream(), versionJar);
+ }
+ continue;
+ }
+
+ // Find a configuration/dependency pair that matches the desired library
+ Pair pair = null;
+ for (Map.Entry> entry : dependencyLookupMap.entrySet()) {
+ if (entry.getKey().startsWith(lib)) {
+ pair = entry.getValue();
+ }
+ }
+
+ // The pair must be non-null
+ Objects.requireNonNull(pair);
+
+ // Find the library jar file, and copy it to tempLibraries
+ for (File file : pair.getKey().files(pair.getValue())) {
+ if (file.getName().startsWith(lib)) {
+ Files.copy(file.toPath(), getTemporaryFile("tempLibraries/" + lib + ".jar"), REPLACE_EXISTING);
+ }
+ }
+ }
+ }
+
+ private void proguardApi() throws Exception {
+ runProguard(getTemporaryFile(PROGUARD_API_CONFIG));
+ Determinizer.main(this.proguardOut.toString(), this.artifactApiPath.toString());
+ }
+
+ private void proguardStandalone() throws Exception {
+ runProguard(getTemporaryFile(PROGUARD_STANDALONE_CONFIG));
+ Determinizer.main(this.proguardOut.toString(), this.artifactStandalonePath.toString());
+ }
+
+ private void cleanup() {
+ try {
+ Files.delete(this.proguardOut);
+ } catch (IOException ignored) {}
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public void setExtract(String extract) {
+ this.extract = extract;
+ }
+
+ public void setVersionManifest(String versionManifest) {
+ this.versionManifest = versionManifest;
+ }
+
+ /*
+ * A LOT OF SHITTY UTIL METHODS ARE BELOW.
+ *
+ * PROCEED WITH CAUTION
+ */
+
+ private void runProguard(Path config) throws Exception {
+ // Delete the existing proguard output file. Proguard probably handles this already, but why not do it ourselves
+ if (Files.exists(this.proguardOut)) {
+ Files.delete(this.proguardOut);
+ }
+
+ Path proguardJar = getTemporaryFile(PROGUARD_JAR);
+ Process p = new ProcessBuilder("java", "-jar", proguardJar.toString(), "@" + config.toString())
+ .directory(getTemporaryFile("").toFile()) // Set the working directory to the temporary folder
+ .redirectOutput(ProcessBuilder.Redirect.INHERIT)
+ .redirectError(ProcessBuilder.Redirect.INHERIT)
+ .start();
+
+ // Halt the current thread until the process is complete, if the exit code isn't 0, throw an exception
+ int exitCode;
+ if ((exitCode = p.waitFor()) != 0) {
+ throw new Exception("Proguard exited with code " + exitCode);
+ }
+ }
+
+ private void write(InputStream stream, Path file) throws Exception {
+ if (Files.exists(file)) {
+ Files.delete(file);
+ }
+ Files.copy(stream, file);
+ }
+
+ private Path getRelativeFile(String file) {
+ return Paths.get(new File(file).getAbsolutePath());
+ }
+
+ private Path getTemporaryFile(String file) {
+ return Paths.get(new File(getTemporaryDir(), file).getAbsolutePath());
+ }
+
+ private Path getBuildFile(String file) {
+ return getRelativeFile("build/libs/" + file);
+ }
+
+ private JsonElement readJson(List lines) {
+ return PARSER.parse(String.join("\n", lines));
+ }
+}
diff --git a/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java
new file mode 100644
index 000000000..4b4f4748f
--- /dev/null
+++ b/buildSrc/src/main/java/baritone/gradle/util/Determinizer.java
@@ -0,0 +1,85 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone 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 Baritone. If not, see .
+ */
+
+package baritone.gradle.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Comparator;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.stream.Collectors;
+
+/**
+ * Make a .jar file deterministic by sorting all entries by name, and setting all the last modified times to 0.
+ * This makes the build 100% reproducible since the timestamp when you built it no longer affects the final file.
+ *
+ * @author leijurv
+ */
+public class Determinizer {
+
+ public static void main(String... args) throws IOException {
+ /*
+ haha yeah can't relate
+
+ unzip -p build/libs/baritone-$VERSION.jar "mixins.baritone.refmap.json" | jq --sort-keys -c -M '.' > mixins.baritone.refmap.json
+ zip -u build/libs/baritone-$VERSION.jar mixins.baritone.refmap.json
+ rm mixins.baritone.refmap.json
+ */
+
+ System.out.println("Running Determinizer");
+ System.out.println(" Input path: " + args[0]);
+ System.out.println(" Output path: " + args[1]);
+
+ JarFile jarFile = new JarFile(new File(args[0]));
+ JarOutputStream jos = new JarOutputStream(new FileOutputStream(new File(args[1])));
+
+ List entries = jarFile.stream()
+ .sorted(Comparator.comparing(JarEntry::getName))
+ .collect(Collectors.toList());
+
+ for (JarEntry entry : entries) {
+ if (entry.getName().equals("META-INF/fml_cache_annotation.json")) {
+ continue;
+ }
+ if (entry.getName().equals("META-INF/fml_cache_class_versions.json")) {
+ continue;
+ }
+ JarEntry clone = new JarEntry(entry.getName());
+ clone.setTime(0);
+ jos.putNextEntry(clone);
+ copy(jarFile.getInputStream(entry), jos);
+ }
+
+ jos.finish();
+ jos.close();
+ jarFile.close();
+ }
+
+ private static void copy(InputStream is, OutputStream os) throws IOException {
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = is.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ }
+}
diff --git a/scripts/proguard.pro b/scripts/proguard.pro
index 8b406e69c..261b47d1a 100644
--- a/scripts/proguard.pro
+++ b/scripts/proguard.pro
@@ -49,7 +49,6 @@
-libraryjars 'tempLibraries/httpclient-4.3.3.jar'
-libraryjars 'tempLibraries/httpcore-4.3.2.jar'
-libraryjars 'tempLibraries/icu4j-core-mojang-51.2.jar'
--libraryjars 'tempLibraries/java-objc-bridge-1.0.0.jar'
-libraryjars 'tempLibraries/jinput-2.0.5.jar'
-libraryjars 'tempLibraries/jna-4.4.0.jar'
-libraryjars 'tempLibraries/jopt-simple-5.0.3.jar'
@@ -60,13 +59,10 @@
-libraryjars 'tempLibraries/log4j-api-2.8.1.jar'
-libraryjars 'tempLibraries/log4j-core-2.8.1.jar'
-# linux / travis
--libraryjars 'tempLibraries/lwjgl-2.9.4-nightly-20150209.jar'
--libraryjars 'tempLibraries/lwjgl_util-2.9.4-nightly-20150209.jar'
-
-# mac
-#-libraryjars 'tempLibraries/lwjgl_util-2.9.2-nightly-20140822.jar'
-#-libraryjars 'tempLibraries/lwjgl-2.9.2-nightly-20140822.jar'
+# startsWith is used to check the library, and mac/linux differ in which version they use
+# this is FINE
+-libraryjars 'tempLibraries/lwjgl-.jar'
+-libraryjars 'tempLibraries/lwjgl_util-.jar'
-libraryjars 'tempLibraries/netty-all-4.1.9.Final.jar'
-libraryjars 'tempLibraries/oshi-core-1.1.jar'