Begin 0.11 development

This commit is contained in:
FalsePattern committed 2023-01-14 20:18:48 +01:00
1 parent 4cbda7a568
commit 1c2c3f0cf1
25 files changed
+617 -231

No files matched your search

@@ -34,4 +34,5 @@ import java.lang.annotation.RetentionPolicy;
@StableAPI(since = "0.10.0")
public @interface DeprecationDetails {
@StableAPI.Expose String deprecatedSince();
@StableAPI.Expose(since = "0.11.0") String replacement() default "";
}
@@ -56,7 +56,7 @@ public class ComplexVersion extends Version {
}
} else if (o instanceof SemanticVersion) {
val other = (SemanticVersion) o;
val result = other.compareTo(versions[0]);
val result = versions[0].compareTo(other);
if (result != 0) {
return result;
}
@@ -28,6 +28,13 @@ import lombok.NonNull;
import java.util.concurrent.CompletableFuture;
/**
* DEPRECATED. See the DEPENDENCIES.MD files in the root of the resources for more information.
* <p>
* This class will stay here for legacy compatibility
*/
@Deprecated
@DeprecationDetails(deprecatedSince = "0.11.0")
@StableAPI(since = "0.6.0")
public class DependencyLoader {
@@ -64,8 +71,9 @@ public class DependencyLoader {
return new VoidBuilder();
}
@StableAPI(since = "0.6.0")
@Deprecated
@DeprecationDetails(deprecatedSince = "0.10.0")
@StableAPI(since = "0.6.0")
public static class VoidBuilder {
private String loadingModId;
private String groupId;
@@ -21,9 +21,12 @@
package com.falsepattern.lib.dependencies;
import com.falsepattern.lib.DeprecationDetails;
import com.falsepattern.lib.StableAPI;
import lombok.NonNull;
@Deprecated
@DeprecationDetails(deprecatedSince = "0.11.0")
@StableAPI(since = "0.10.0")
public class Library {
@StableAPI.Expose
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2022 FalsePattern
* All Rights Reserved
*
* The above copyright notice, this permission notice and the word "SNEED"
* shall be included in all copies or substantial portions of the Software.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.lib.dependencies;
import org.jetbrains.annotations.NotNull;
public class RawVersion extends Version {
public final String versionString;
public RawVersion(String versionString) {
this.versionString = versionString;
}
@Override
public int compareTo(@NotNull Version o) {
if (o instanceof RawVersion) {
return versionString.compareTo(((RawVersion) o).versionString);
} else if (o instanceof SemanticVersion) {
return 1;
} else if (o instanceof ComplexVersion) {
return 1;
} else {
throw new IllegalArgumentException("Unknown version type: " + o.getClass().getName());
}
}
}
@@ -20,17 +20,21 @@
*/
package com.falsepattern.lib.internal;
import lombok.Cleanup;
import lombok.val;
import lombok.var;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -53,18 +57,43 @@ public class Internet {
}
}
public static CompletableFuture<byte[]> download(URL URL) {
public static CompletableFuture<byte[]> download(URL... urls) {
return CompletableFuture.supplyAsync(() -> {
val result = new ByteArrayOutputStream();
AtomicReference<Exception> caught = new AtomicReference<>();
connect(URL, caught::set, (input) -> {
try {
transferAndClose(input, result);
} catch (IOException e) {
throw new CompletionException(e);
}
});
return result.toByteArray();
val caught = new AtomicReference<Exception>();
val success = new AtomicBoolean(false);
for (val url : urls) {
connect(url, caught::set, (input) -> {
try {
transferAndClose(input, result);
success.set(true);
} catch (IOException e) {
throw new CompletionException(e);
}
});
if (success.get()) return result.toByteArray();
}
throw new CompletionException(caught.get());
});
}
public static CompletableFuture<Void> downloadFile(File file, URL... urls) {
return CompletableFuture.runAsync(() -> {
val caught = new AtomicReference<Exception>();
val success = new AtomicBoolean(false);
for (val url : urls) {
connect(url, caught::set, (input) -> {
try {
@Cleanup val outputStream = Files.newOutputStream(file.toPath());
transferAndClose(input, outputStream);
success.set(true);
} catch (IOException e) {
throw new CompletionException(e);
}
});
if (success.get()) return;
}
throw new CompletionException(caught.get());
});
}
@@ -21,6 +21,7 @@
package com.falsepattern.lib.internal.asm;
import com.falsepattern.lib.internal.Tags;
import com.falsepattern.lib.internal.impl.dependencies.DependencyLoaderImpl;
import com.falsepattern.lib.mapping.MappingManager;
import lombok.Getter;
import lombok.val;
@@ -51,6 +52,8 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
throw skillIssue("Thermos is not supported by FalsePatternLib, please use a normal forge server.");
} catch (ClassNotFoundException ignored) {
}
//Scan for dependencies now
DependencyLoaderImpl.scanDeps();
//Initializing the rest
MappingManager.initialize();
}
@@ -0,0 +1,69 @@
/*
* Copyright (C) 2022 FalsePattern
* All Rights Reserved
*
* The above copyright notice, this permission notice and the word "SNEED"
* shall be included in all copies or substantial portions of the Software.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.lib.internal.impl.dependencies;
import com.falsepattern.lib.StableAPI;
import com.google.gson.annotations.Expose;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(fluent = true)
public class DepRoot {
private String source;
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private List<String> repositories;
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private Dependencies dependencies;
@Data
@Accessors(fluent = true)
public static class Dependencies {
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private SidedDependencies always;
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private SidedDependencies obf;
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private SidedDependencies dev;
}
@Data
@Accessors(fluent = true)
public static class SidedDependencies {
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private List<String> common;
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private List<String> client;
@Expose
@StableAPI.Expose(since = "__INTERNAL__")
private List<String> server;
}
}
@@ -20,14 +20,18 @@
*/
package com.falsepattern.lib.internal.impl.dependencies;
import com.falsepattern.lib.dependencies.DependencyLoader;
import com.falsepattern.lib.DeprecationDetails;
import com.falsepattern.lib.dependencies.Library;
import com.falsepattern.lib.dependencies.RawVersion;
import com.falsepattern.lib.dependencies.SemanticVersion;
import com.falsepattern.lib.dependencies.Version;
import com.falsepattern.lib.internal.Internet;
import com.falsepattern.lib.internal.Share;
import com.falsepattern.lib.internal.Tags;
import com.falsepattern.lib.internal.config.LibraryConfig;
import com.falsepattern.lib.util.FileUtil;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser;
import io.netty.util.internal.ConcurrentSet;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@@ -37,19 +41,27 @@ import lombok.var;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@@ -57,6 +69,12 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipFile;
public class DependencyLoaderImpl {
@@ -67,7 +85,7 @@ public class DependencyLoaderImpl {
private static final Map<String, Version> loadedLibraries = new ConcurrentHashMap<>();
private static final Map<String, String> loadedLibraryMods = new ConcurrentHashMap<>();
private static final Set<String> mavenRepositories = new ConcurrentSet<>();
private static final Logger log = LogManager.getLogger(Tags.MODNAME + " Library Downloader");
private static final Logger LOG = LogManager.getLogger(Tags.MODNAME + " Library Loader");
private static final AtomicLong counter = new AtomicLong(0);
private static final ExecutorService executor = Executors.newCachedThreadPool(r -> {
@@ -126,16 +144,16 @@ public class DependencyLoaderImpl {
private static void checkedDelete(File file) {
if (!file.delete()) {
log.fatal("Failed to delete file {}", file);
LOG.fatal("Failed to delete file {}", file);
throw new RuntimeException("Failed to delete file " + file);
}
}
private static synchronized void addToClasspath(File file) {
try {
val cl = (LaunchClassLoader) DependencyLoader.class.getClassLoader();
val cl = (LaunchClassLoader) DependencyLoaderImpl.class.getClassLoader();
cl.addURL(file.toURI().toURL());
log.debug("Injected file {} into classpath!", file.getPath());
LOG.debug("Injected file {} into classpath!", file.getPath());
} catch (Exception e) {
throw new RuntimeException("Failed to add library to classpath: " + file.getAbsolutePath(), e);
}
@@ -149,10 +167,14 @@ public class DependencyLoaderImpl {
Internet.transferAndClose(is, new BufferedOutputStream(Files.newOutputStream(target.toPath())));
}
@Deprecated
@DeprecationDetails(deprecatedSince = "0.11.0")
public static void loadLibraries(Library... libraries) {
loadLibrariesAsync(libraries).join();
}
@Deprecated
@DeprecationDetails(deprecatedSince = "0.11.0")
public static CompletableFuture<Void> loadLibrariesAsync(Library... libraries) {
val futures = new ArrayList<CompletableFuture<Void>>();
for (val library : libraries) {
@@ -164,6 +186,206 @@ public class DependencyLoaderImpl {
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
private static Stream<URL> scanSourceMetaInf(URL source) {
if (!source.getProtocol().equals("file")) {
LOG.warn("Skipping non-file source: {}", source);
return Stream.empty();
}
LOG.debug("Scanning {} for dependencies", source);
val path = source.getPath();
val output = new ArrayList<URL>();
if (path.endsWith(".jar") || path.endsWith(".zip")) {
//Scan jar file for json in META-INF, add them to the list
try (val jarFile = (ZipFile)(path.endsWith("jar") ? new JarFile(path) : new ZipFile(path))) {
val entries = jarFile.entries();
while (entries.hasMoreElements()) {
val entry = entries.nextElement();
if (!entry.getName().startsWith("META-INF") || !entry.getName().endsWith(".json")) {
continue;
}
try {
output.add(new URL("jar:" + source + "!/" + entry.getName()));
} catch (MalformedURLException e) {
LOG.error("Failed to add json source {} to dependency source list: {}", entry.getName(), e);
}
}
} catch (IOException e) {
LOG.error("Failed to open jar file {}", path);
}
} else {
val dir = new File(path);
if (!dir.exists() || !dir.isDirectory()) {
LOG.warn("Skipping non-directory, nor jar, nor zip source: {}", source);
return Stream.empty();
}
//Scan directory for json in META-INF, add them to the list
val metaInf = new File(dir, "META-INF");
if (!metaInf.exists() || !metaInf.isDirectory()) {
return Stream.empty();
}
val files = metaInf.listFiles();
if (files == null) {
return Stream.empty();
}
for (val file : files) {
if (!file.getName().endsWith(".json")) {
continue;
}
try {
output.add(file.toURI().toURL());
} catch (MalformedURLException e) {
LOG.error("Failed to add json source {} to dependency source list: {}", file.getName(), e);
}
}
}
return output.stream();
}
private static Stream<URL> grabSourceCandidatesFromFolder(File folder) {
if (!folder.exists() || !folder.isDirectory()) {
LOG.warn("File does not exist or is not a directory: {}", folder);
} else {
val files = folder.listFiles();
if (files == null) {
LOG.warn("Folder is not readable: {}", folder);
} else {
return Arrays.stream(files).map(file -> {
try {
LOG.trace("Adding {} to dependency source list", file);
return file.toURI().toURL();
} catch (MalformedURLException e) {
LOG.error("Failed to add mod file {} to dependency source list: {}", file.getName(), e);
return null;
}
}).filter(Objects::nonNull);
}
}
return Stream.empty();
}
private static <A, B, C> Stream<Pair<A, B>> flatMap(Pair<A, C> pair, Function<C, Stream<B>> mapper) {
val a = pair.a;
return mapper.apply(pair.b).map(b -> new Pair<>(a, b));
}
private static final Pattern VERSION_PATTERN = Pattern.compile(""
+ "(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:\\.(0|[1-9]\\d*))?"
+ "(?:-((?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0)"
+ "(?:\\.(?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0))*))?"
+ "(?:\\+([\\w-]+(\\.[\\w-]+)*))?");
public static void scanDeps() {
LOG.debug("Discovering dependency source candidates...");
val modsDir = new File(FileUtil.getMinecraftHome(), "mods");
val mods1710Dir = new File(modsDir, "1.7.10");
val dependencySpecs = Stream.of(Launch.classLoader.getSources().stream(),
grabSourceCandidatesFromFolder(modsDir),
grabSourceCandidatesFromFolder(mods1710Dir))
.parallel()
.flatMap((i) -> i)
.flatMap(DependencyLoaderImpl::scanSourceMetaInf)
.map((source) -> {
//Convert source to GSON json
try (val is = source.openStream()) {
val json = new JsonParser().parse(new InputStreamReader(is)).getAsJsonObject();
if (!(json.isJsonObject() &&
json.has("identifier") &&
json.get("identifier")
.getAsString()
.equals("falsepatternlib_dependencies")
)) {
return null;
}
val builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
val gson = builder.create();
json.remove("identifier");
val root = gson.fromJson(json, DepRoot.class);
root.source(source.toString());
return root;
} catch (IOException e) {
LOG.error("Failed to read json from source {}: {}", source, e);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
mavenRepositories.addAll(dependencySpecs.stream()
.flatMap((dep) -> dep.repositories().stream())
.collect(Collectors.toSet()));
val artifacts = dependencySpecs.parallelStream()
.map((root) -> new Pair<>(root.source(), root.dependencies()))
.flatMap(pair -> flatMap(pair, (dep) -> Stream.of(dep.always(), Share.DEV_ENV ? dep.dev() : dep.obf()).parallel()))
.flatMap(pair -> flatMap(pair, (dep) -> Stream.concat(dep.common().parallelStream(),
FMLLaunchHandler.side().isClient() ?
dep.client().parallelStream() :
dep.server().parallelStream()).parallel()))
.map((pair) -> {
val source = pair.a;
val dep = pair.b;
val parts = dep.split(":");
if (parts.length < 3) {
LOG.error("Invalid dependency: {}", dep);
return null;
}
val groupId = parts[0];
val artifactId = parts[1];
Version version;
try {
val matcher = VERSION_PATTERN.matcher(parts[2]);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid version: " + parts[2]);
}
val major = Integer.parseInt(matcher.group(1));
val minor = matcher.group(2) == null ? -1 : Integer.parseInt(matcher.group(2));
val patch = matcher.group(3) == null ? -1 : Integer.parseInt(matcher.group(3));
val preRelease = matcher.group(4);
val build = matcher.group(5);
version = new SemanticVersion(major, minor, patch, preRelease, build);
} catch (IllegalArgumentException e) {
LOG.warn("Unparseable dependency version {}:{}:{} from {}", groupId, artifactId, parts[2], source);
version = new RawVersion(parts[2]);
}
final String classifier = parts.length > 3 ? parts[3] : null;
if (classifier != null) {
LOG.info("Found dependency: {}:{}:{}:{} from {}", groupId, artifactId,
version, classifier, source);
} else {
LOG.info("Found dependency: {}:{}:{} from {}", groupId, artifactId,
version, source);
}
return new DependencyLoadTask(source, groupId, artifactId,
version, null, version,
classifier, classifier);
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
val artifactMap = new HashMap<String, DependencyLoadTask>();
for (val artifact: artifacts) {
val id = artifact.getGroupArtifact();
if (artifactMap.containsKey(id)) {
val otherArtifact = artifactMap.get(id);
//TODO: Check for conflicts
if (artifact.preferredVersion.compareTo(otherArtifact.preferredVersion) > 0) {
LOG.info("Replacing dependency {}:{} from {} with version {} from {}",
otherArtifact.getGroupArtifact(), otherArtifact.preferredVersion,
otherArtifact.loadingModId,
artifact.preferredVersion,
artifact.loadingModId);
artifactMap.put(id, artifact);
}
} else {
artifactMap.put(id, artifact);
}
}
val futures = new ArrayList<CompletableFuture<Void>>();
for (val task : artifactMap.values()) {
futures.add(CompletableFuture.runAsync(task::load, executor));
}
val theFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
theFuture.join();
}
@RequiredArgsConstructor
private static class DependencyLoadTask {
@NonNull
@@ -189,29 +411,33 @@ public class DependencyLoaderImpl {
private File file;
private void load() {
setupLibraryNames();
if (loadedLibraries.containsKey(artifact)) {
alreadyLoaded();
return;
}
setupPaths();
if (tryLoadingExistingFile()) {
return;
}
validateDownloadsAllowed();
for (var repo : mavenRepositories) {
if (tryDownloadFromMaven(repo)) {
try {
setupLibraryNames();
if (loadedLibraries.containsKey(artifact)) {
alreadyLoaded();
return;
}
setupPaths();
if (tryLoadingExistingFile()) {
return;
}
validateDownloadsAllowed();
for (var repo : mavenRepositories) {
if (tryDownloadFromMaven(repo)) {
return;
}
}
crashCouldNotDownload();
} catch (Exception e) {
LOG.fatal(e);
}
crashCouldNotDownload();
}
private void crashCouldNotDownload() {
val errorMessage = "Failed to download library " + groupId + ":" + artifactId + ":" + preferredVersion +
((suffix != null) ? ":" + suffix : "") + " from any repository! Requested by mod: " +
loadingModId;
log.fatal(errorMessage);
LOG.fatal(errorMessage);
throw new IllegalStateException(errorMessage);
}
@@ -219,7 +445,7 @@ public class DependencyLoaderImpl {
suffix = Share.DEV_ENV ? devSuffix : regularSuffix;
artifactLogName = String.format("%s:%s:%s%s", groupId, artifactId, preferredVersion,
suffix != null ? "-" + suffix : "");
log.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId);
LOG.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId);
artifact = groupId + ":" + artifactId + ":" + suffix;
}
@@ -231,18 +457,18 @@ public class DependencyLoaderImpl {
val rangeString = "(minimum: " + minVersion + (maxVersion == null ? "" : ", maximum: " + maxVersion) + ")";
if (minVersion.compareTo(currentVer) > 0 || (maxVersion != null && maxVersion.compareTo(currentVer) < 0)) {
for (int i = 0; i < 16; i++) {
log.fatal("ALERT VVVVVVVVVVVV ALERT");
LOG.fatal("ALERT VVVVVVVVVVVV ALERT");
}
log.fatal("Library {}:{}{} already loaded with version {}, " +
LOG.fatal("Library {}:{}{} already loaded with version {}, " +
"but a version in the range {} was requested! Thing may go horribly wrong! " +
"Requested by mod: {}, previously loaded by mod: {}", groupId, artifactId,
suffix != null ? ":" + suffix : "", currentVer, rangeString, loadingModId,
loadedLibraryMods.get(artifact));
for (int i = 0; i < 16; i++) {
log.fatal("ALERT ^^^^^^^^^^^^ ALERT");
LOG.fatal("ALERT ^^^^^^^^^^^^ ALERT");
}
} else {
log.info("Attempted loading of library {}:{}{} with preferred version {}, " +
LOG.info("Attempted loading of library {}:{}{} with preferred version {}, " +
"but version {} was already loaded, which matched the range {}. This is not an " + "error. " +
"Requested by mod: {}, previously loaded by mod: {}", groupId, artifactId,
suffix != null ? ":" + suffix : "", preferredVersion, currentVer, rangeString, loadingModId,
@@ -266,23 +492,23 @@ public class DependencyLoaderImpl {
jarName = groupId + "-" + mavenJarName;
if (!libDir.exists()) {
if (!libDir.mkdirs()) {
log.fatal("Failed to create directory {}", libDir);
LOG.fatal("Failed to create directory {}", libDir);
throw new RuntimeException("Failed to create directory " + libDir);
}
}
if (oldLibDir.exists()) {
log.info("Migrating old library folder. From: " + oldLibDir.getAbsolutePath() + ", To: " + libDir.getAbsolutePath());
LOG.info("Migrating old library folder. From: " + oldLibDir.getAbsolutePath() + ", To: " + libDir.getAbsolutePath());
val oldFiles = oldLibDir.listFiles();
if (oldFiles != null) {
for (val file: oldFiles) {
try {
Files.move(file.toPath(), libDir.toPath().resolve(oldLibDir.toPath().relativize(file.toPath())), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
log.warn("Failed to move file " + file.getName() + " to new dir! Deleting instead.");
LOG.warn("Failed to move file " + file.getName() + " to new dir! Deleting instead.");
try {
Files.deleteIfExists(file.toPath());
} catch (IOException ex) {
log.warn("Failed to delete file " + file.getPath() + "!");
LOG.warn("Failed to delete file " + file.getPath() + "!");
file.deleteOnExit();
}
}
@@ -291,7 +517,7 @@ public class DependencyLoaderImpl {
try {
Files.deleteIfExists(oldLibDir.toPath());
} catch (IOException e) {
log.warn("Failed to delete old library directory!");
LOG.warn("Failed to delete old library directory!");
oldLibDir.deleteOnExit();
}
}
@@ -307,21 +533,21 @@ public class DependencyLoaderImpl {
if (status == ChecksumStatus.FAILED) {
return false;
} else if (status == ChecksumStatus.MISSING) {
log.debug("Library {} is missing checksum data! Either it was manually deleted, " +
LOG.debug("Library {} is missing checksum data! Either it was manually deleted, " +
"or the source repo didn't have it in the first place", artifactLogName);
}
} catch (IOException e) {
log.error("Failed to execute validation check for " + artifactLogName, e);
LOG.error("Failed to execute validation check for " + artifactLogName, e);
checkedDelete(file);
return false;
}
try {
addToClasspath(file);
loadedLibraries.put(artifact, preferredVersion);
log.debug("Library {} successfully loaded from disk!", artifactLogName);
LOG.debug("Library {} successfully loaded from disk!", artifactLogName);
return true;
} catch (RuntimeException e) {
log.warn("Failed to load library {} from file! Re-downloading...", artifactLogName);
LOG.warn("Failed to load library {} from file! Re-downloading...", artifactLogName);
checkedDelete(file);
return false;
}
@@ -333,7 +559,7 @@ public class DependencyLoaderImpl {
((suffix != null) ? ":" + suffix : "") + ": " + Tags.MODNAME +
" library downloading has been disabled in the config, and the library is not present " +
"on disk! Requested by mod: " + loadingModId;
log.fatal(errorMessage);
LOG.fatal(errorMessage);
throw new IllegalStateException(errorMessage);
}
}
@@ -354,15 +580,15 @@ public class DependencyLoaderImpl {
}
val success = new AtomicBoolean(false);
Internet.connect(new URL(url),
(ex) -> log.debug("Artifact {} could not be downloaded from repo {}: {}",
(ex) -> LOG.debug("Artifact {} could not be downloaded from repo {}: {}",
artifactLogName, finalRepo, ex.getMessage()), (input) -> {
log.debug("Downloading {} from {}", artifactLogName, finalRepo);
LOG.debug("Downloading {} from {}", artifactLogName, finalRepo);
download(input, file);
log.debug("Downloaded {} from {}", artifactLogName, finalRepo);
LOG.debug("Downloaded {} from {}", artifactLogName, finalRepo);
success.set(true);
});
if (success.get()) {
log.debug("Validating checksum for {}", artifactLogName);
LOG.debug("Validating checksum for {}", artifactLogName);
val hadChecksum = validateChecksum(url);
switch (hadChecksum) {
case FAILED:
@@ -370,7 +596,7 @@ public class DependencyLoaderImpl {
case OK:
break;
case MISSING:
log.warn("The library {} had no checksum available on the repository.\n" +
LOG.warn("The library {} had no checksum available on the repository.\n" +
"There's a chance it might have gotten corrupted during download,\n" +
"but we're loading it anyways.", artifactLogName);
}
@@ -389,14 +615,14 @@ public class DependencyLoaderImpl {
for (val checksumType : CHECKSUM_TYPES) {
val checksumURL = url + "." + checksumType;
val checksumFile = new File(libDir, jarName + "." + checksumType);
log.debug("Attempting to get {} checksum...", checksumType);
LOG.debug("Attempting to get {} checksum...", checksumType);
val success = new AtomicBoolean(false);
Internet.connect(new URL(checksumURL),
(ex) -> log.debug("Could not get {} checksum for {}: {}", checksumType,
(ex) -> LOG.debug("Could not get {} checksum for {}: {}", checksumType,
artifactLogName, ex.getMessage()), (input) -> {
log.debug("Downloading {} checksum for {}", checksumType, artifactLogName);
LOG.debug("Downloading {} checksum for {}", checksumType, artifactLogName);
download(input, checksumFile);
log.debug("Downloaded {} checksum for {}", checksumType, artifactLogName);
LOG.debug("Downloaded {} checksum for {}", checksumType, artifactLogName);
success.set(true);
});
if (success.get()) {
@@ -409,7 +635,7 @@ public class DependencyLoaderImpl {
private ChecksumStatus validateChecksum(File file) throws IOException {
for (val checksumType : CHECKSUM_TYPES) {
val checksumFile = new File(libDir, jarName + "." + checksumType);
log.debug("Attempting to read {} checksum from file...", checksumType);
LOG.debug("Attempting to read {} checksum from file...", checksumType);
if (checksumFile.exists()) {
return getChecksumStatus(file, checksumType, checksumFile);
}
@@ -421,15 +647,19 @@ public class DependencyLoaderImpl {
val fileHash = hash(checksumType, file);
val referenceHash = new String(Files.readAllBytes(checksumFile.toPath()));
if (!fileHash.equals(referenceHash)) {
log.error("Failed {} checksum validation for {}.", checksumType, artifactLogName);
LOG.error("Failed {} checksum validation for {}.", checksumType, artifactLogName);
checkedDelete(file);
checkedDelete(checksumFile);
return ChecksumStatus.FAILED;
}
log.debug("Successfully validated {} checksum for {}.", checksumType, artifactLogName);
LOG.debug("Successfully validated {} checksum for {}.", checksumType, artifactLogName);
return ChecksumStatus.OK;
}
public String getGroupArtifact() {
return groupId + ":" + artifactId;
}
private enum ChecksumStatus {
OK,
FAILED,
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 FalsePattern
* All Rights Reserved
*
* The above copyright notice, this permission notice and the word "SNEED"
* shall be included in all copies or substantial portions of the Software.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.falsepattern.lib.internal.impl.dependencies;
public class Pair<A, B> {
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
}
@@ -21,16 +21,14 @@
package com.falsepattern.lib.internal.impl.updates;
import com.falsepattern.json.node.JsonNode;
import com.falsepattern.lib.dependencies.DependencyLoader;
import com.falsepattern.lib.dependencies.Library;
import com.falsepattern.lib.dependencies.SemanticVersion;
import com.falsepattern.lib.internal.Internet;
import com.falsepattern.lib.internal.Tags;
import com.falsepattern.lib.internal.config.LibraryConfig;
import com.falsepattern.lib.text.FormattedText;
import com.falsepattern.lib.updates.ModUpdateInfo;
import com.falsepattern.lib.updates.UpdateCheckException;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import lombok.val;
import net.minecraft.client.resources.I18n;
@@ -97,65 +95,49 @@ public final class UpdateCheckerImpl {
} catch (MalformedURLException e) {
throw new CompletionException(new UpdateCheckException("Invalid URL: " + url, e));
}
if (!jsonLibraryLoaded.get()) {
try {
DependencyLoader.addMavenRepo("https://mvn.falsepattern.com/releases/");
DependencyLoader.loadLibraries(Library.builder()
.loadingModId(Tags.MODID)
.groupId("com.falsepattern")
.artifactId("json")
.minVersion(new SemanticVersion(0, 4, 0))
.maxVersion(new SemanticVersion(0, Integer.MAX_VALUE,
Integer.MAX_VALUE))
.preferredVersion(new SemanticVersion(0, 4, 1))
.build());
} catch (Exception e) {
throw new CompletionException(
new UpdateCheckException("Failed to load json library for update checker!", e));
}
jsonLibraryLoaded.set(true);
}
val result = new ArrayList<ModUpdateInfo>();
JsonNode parsed;
JsonElement parsed;
try {
parsed = JsonNode.parse(Internet.download(URL).thenApply(String::new).join());
parsed = new JsonParser().parse(Internet.download(URL).thenApply(String::new).join());
} catch (CompletionException e) {
throw new CompletionException(new UpdateCheckException("Failed to download update checker JSON file!",
e.getCause() == null ? e : e.getCause()));
}
List<JsonNode> modList;
if (parsed.isList()) {
modList = parsed.getJavaList();
JsonArray modList;
if (parsed.isJsonArray()) {
modList = parsed.getAsJsonArray();
} else {
modList = Collections.singletonList(parsed);
modList = new JsonArray();
modList.add(parsed);
}
val installedMods = Loader.instance().getIndexedModList();
for (val node : modList) {
if (!node.isObject()) {
if (!node.isJsonObject()) {
continue;
}
if (!node.containsKey("modid")) {
val obj = node.getAsJsonObject();
if (!obj.has("modid")) {
continue;
}
if (!node.containsKey("latestVersion")) {
if (!obj.has("latestVersion")) {
continue;
}
val modid = node.getString("modid");
val modid = obj.get("modid").getAsString();
if (!installedMods.containsKey(modid)) {
continue;
}
val mod = installedMods.get(modid);
val latestVersionsNode = node.get("latestVersion");
val latestVersionsNode = obj.get("latestVersion");
List<String> latestVersions;
if (latestVersionsNode.isString()) {
latestVersions = Collections.singletonList(latestVersionsNode.stringValue());
} else if (latestVersionsNode.isList()) {
if (latestVersionsNode.isJsonPrimitive() && (latestVersionsNode.getAsJsonPrimitive().isString())) {
latestVersions = Collections.singletonList(latestVersionsNode.getAsString());
} else if (latestVersionsNode.isJsonArray()) {
latestVersions = new ArrayList<>();
for (val version : latestVersionsNode.getJavaList()) {
if (!version.isString()) {
for (val version : latestVersionsNode.getAsJsonArray()) {
if (!version.isJsonPrimitive() || !version.getAsJsonPrimitive().isString()) {
continue;
}
latestVersions.add(version.stringValue());
latestVersions.add(version.getAsString());
}
} else {
continue;
@@ -164,9 +146,11 @@ public final class UpdateCheckerImpl {
if (latestVersions.contains(currentVersion)) {
continue;
}
val updateURL =
node.containsKey("updateURL") && node.get("updateURL").isString() ? node.getString("updateURL")
: "";
val updateURL = obj.has("updateURL") &&
obj.get("updateURL").isJsonPrimitive() &&
obj.get("updateURL").getAsJsonPrimitive().isString()
? obj.get("updateURL").getAsString()
: "";
result.add(new ModUpdateInfo(modid, currentVersion, latestVersions.get(0), updateURL));
}
return result;
@@ -21,10 +21,6 @@
package com.falsepattern.lib.mapping;
import com.falsepattern.lib.StableAPI;
import com.falsepattern.lib.dependencies.DependencyLoader;
import com.falsepattern.lib.dependencies.Library;
import com.falsepattern.lib.dependencies.SemanticVersion;
import com.falsepattern.lib.internal.Tags;
import com.falsepattern.lib.internal.asm.CoreLoadingPlugin;
import com.falsepattern.lib.mapping.storage.Lookup;
import com.falsepattern.lib.mapping.types.MappingType;
@@ -38,9 +34,7 @@ import lombok.val;
import lombok.var;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.tukaani.xz.LZMA2Options;
import java.io.DataInputStream;
import java.util.HashMap;
import java.util.Map;
@@ -59,54 +53,24 @@ public class MappingManager {
}
initialized = true;
}
DependencyLoader.addMavenRepo("https://repo1.maven.org/maven2/");
DependencyLoader.loadLibraries(Library.builder()
.groupId("org.tukaani")
.artifactId("xz")
.minVersion(SemanticVersion.builder()
.majorVersion(1)
.minorVersion(9)
.patchVersion(-1)
.build())
.preferredVersion(SemanticVersion.builder()
.majorVersion(1)
.minorVersion(9)
.patchVersion(-1)
.build())
.loadingModId(Tags.MODID)
.build());
val input = new DataInputStream(new LZMA2Options(6).getInputStream(
ResourceUtil.getResourceFromJar("/mappings.lzma2", CoreLoadingPlugin.class)));
{
var classBytes = new byte[input.readInt()];
input.readFully(classBytes);
val classMappings = new String(classBytes).split("\n");
for (int i = 1; i < classMappings.length; i++) {
val line = classMappings[i].split(",");
val clazz = new UniversalClass(line, stringPool);
internalLookup.unwrap(clazz.internalName, clazz);
regularLookup.unwrap(clazz.regularName, clazz);
}
val classMappings = new String(ResourceUtil.getResourceBytesFromJar("/classes.csv", CoreLoadingPlugin.class)).split("\n");
for (int i = 1; i < classMappings.length; i++) {
val line = classMappings[i].split(",");
val clazz = new UniversalClass(line, stringPool);
internalLookup.unwrap(clazz.internalName, clazz);
regularLookup.unwrap(clazz.regularName, clazz);
}
{
var fieldBytes = new byte[input.readInt()];
input.readFully(fieldBytes);
var fieldMappings = new String(fieldBytes).split("\n");
for (int i = 1; i < fieldMappings.length; i++) {
val line = fieldMappings[i].split(",");
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
UniversalField.createAndAddToParent(clazz, line, stringPool);
}
var fieldMappings = new String(ResourceUtil.getResourceBytesFromJar("/fields.csv", CoreLoadingPlugin.class)).split("\n");
for (int i = 1; i < fieldMappings.length; i++) {
val line = fieldMappings[i].split(",");
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
UniversalField.createAndAddToParent(clazz, line, stringPool);
}
{
var methodBytes = new byte[input.readInt()];
input.readFully(methodBytes);
val methodMappings = new String(methodBytes).split("\n");
for (int i = 1; i < methodMappings.length; i++) {
val line = methodMappings[i].split(",");
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
UniversalMethod.createAndAddToParent(clazz, line, stringPool);
}
val methodMappings = new String(ResourceUtil.getResourceBytesFromJar("/methods.csv", CoreLoadingPlugin.class)).split("\n");
for (int i = 1; i < methodMappings.length; i++) {
val line = methodMappings[i].split(",");
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
UniversalMethod.createAndAddToParent(clazz, line, stringPool);
}
}