feat: Recursive deploader and mod dep loading

This commit is contained in:
FalsePattern committed 2025-11-13 00:22:29 +01:00
1 parent 6777344f03
commit b95c54a623
8 files changed
+280 -103

No files matched your search

@@ -39,7 +39,7 @@ public class DependencyLoader {
* @since 0.10.0
*/
public static CompletableFuture<Void> loadLibrariesAsync(Library... libraries) {
return DependencyLoaderImpl.loadLibrariesAsync(libraries);
return DependencyLoaderImpl.loadLibrariesAsync(libraries).thenApply(list -> null);
}
/**
@@ -34,6 +34,7 @@ import java.io.IOException;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class Share {
public static final boolean DEV_ENV = PreShare.devEnv();
public static final boolean CLIENT = PreShare.client();
public static boolean EARLY_INIT_DONE = false;
}
@@ -36,10 +36,12 @@ import lombok.val;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.Name;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin.SortingIndex;
import cpw.mods.fml.relauncher.Side;
import java.util.List;
import java.util.Map;
@@ -57,6 +59,7 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
static {
PreShare.initDevState(Launch.classLoader.findResource("net/minecraft/world/World.class") != null);
PreShare.initClientState(FMLLaunchHandler.side() == Side.CLIENT);
LetsEncryptHelper.replaceSSLContext();
FPLog.LOG.info("Removing skill issues...");
try {
@@ -22,20 +22,27 @@
package com.falsepattern.lib.internal.asm;
import lombok.Getter;
public class PreShare {
private static volatile boolean devEnv = false;
private static volatile boolean inited = false;
private static volatile int devEnv = -1;
private static volatile int client = -1;
public static synchronized void initDevState(boolean state) {
if (inited) {
if (devEnv >= 0) {
return;
}
inited = true;
devEnv = state;
devEnv = state ? 1 : 0;
}
public static synchronized void initClientState(boolean state) {
if (client >= 0) {
return;
}
client = state ? 1 : 0;
}
public static synchronized boolean devEnv() {
return devEnv;
return devEnv == 1;
}
public static synchronized boolean client() {
return client == 1;
}
}
@@ -48,6 +48,8 @@ public class RFBLoadingPlugin implements RfbPlugin {
exc.invoke(loader, "com.falsepattern.lib.internal.core.LowLevelCallMultiplexer");
} catch (Exception ignored) {}
PreShare.initDevState(((URLClassLoader)loader).findResource("net/minecraft/world/World.class") != null);
PreShare.initClientState(((URLClassLoader)loader).findResource("net/minecraft/client/Minecraft.class") != null ||
((URLClassLoader)loader).findResource("bao.class") != null);
LetsEncryptHelper.replaceSSLContext();
LowLevelCallMultiplexer.rfbDetected();
DependencyLoaderImpl.executeDependencyLoading();
@@ -37,9 +37,13 @@ public class DepRoot {
@Expose
private Integer maxJava;
@Expose
private List<String> bundledArtifacts;
@Expose
private List<String> repositories;
@Expose
private Dependencies dependencies;
@Expose
private Dependencies modDependencies;
@Data
@Accessors(fluent = true)
@@ -37,16 +37,16 @@ import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.val;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.Side;
import org.jetbrains.annotations.Nullable;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.WindowConstants;
import java.awt.Dimension;
@@ -94,6 +94,7 @@ public class DependencyLoaderImpl {
* Library checksum formats in a decreasing order of quality.
*/
private static final String[] CHECKSUM_TYPES = new String[]{"sha512", "sha256", "sha1", "md5"};
private static final Set<Path> alreadyScannedSources = new ConcurrentSet<>();
private static final Map<String, Version> loadedLibraries = new ConcurrentHashMap<>();
private static final Map<String, String> loadedLibraryMods = new ConcurrentHashMap<>();
private static final Set<String> remoteMavenRepositories = new ConcurrentSet<>();
@@ -108,8 +109,11 @@ public class DependencyLoaderImpl {
return thread;
});
private static final Path libDir;
private static final Path modsDir;
private static final Path tempDir;
private static AtomicBoolean modDownloaded = new AtomicBoolean(false);
private static void ensureExists(Path directory) {
if (!Files.exists(directory)) {
try {
@@ -127,14 +131,17 @@ public class DependencyLoaderImpl {
if (homeDirStr == null) {
homeDirStr = System.getenv("MINECRAFT_SHARED_DATA_DIR");
}
Path mcHomeDir = FileUtil.getMinecraftHomePath();
if (homeDirStr == null) {
homeDir = FileUtil.getMinecraftHome().toPath();
homeDir = mcHomeDir;
} else {
homeDir = Paths.get(homeDirStr);
}
libDir = homeDir.resolve("falsepattern");
modsDir = mcHomeDir.resolve(Paths.get("mods", "1.7.10"));
tempDir = homeDir.resolve(Paths.get("logs", "falsepattern_tmp"));
ensureExists(libDir);
ensureExists(modsDir);
ensureExists(tempDir);
val oldLibDir = homeDir.resolve(Paths.get("mods", "falsepattern"));
if (Files.exists(oldLibDir)) {
@@ -248,7 +255,7 @@ public class DependencyLoaderImpl {
}
public static CompletableFuture<Void> loadLibrariesAsync(Library... libraries) {
val futures = new ArrayList<CompletableFuture<Void>>();
val futures = new ArrayList<CompletableFuture<URL>>();
for (val library : libraries) {
val task = new DependencyLoadTask(library.loadingModId,
library.groupId,
@@ -257,10 +264,19 @@ public class DependencyLoaderImpl {
library.maxVersion,
library.preferredVersion,
library.regularSuffix,
library.devSuffix);
futures.add(CompletableFuture.runAsync(task::load, executor));
library.devSuffix,
false);
futures.add(CompletableFuture.supplyAsync(task::load, executor));
}
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(ignored -> {
var tasks = futures.stream().map(it -> it.getNow(null)).filter(Objects::nonNull).collect(Collectors.toSet());
if (!tasks.isEmpty()) {
var deps = scanDeps(tasks.stream());
while (deps != null) {
deps = executeArtifactLoading(new ArrayList<>(deps), true);
}
}
});
}
private static boolean scanForDepSpecs(URL source, List<URL> output, List<URL> jijURLs) {
@@ -268,6 +284,17 @@ public class DependencyLoaderImpl {
return false;
}
val fileName = source.getFile();
Path path;
try {
path = Paths.get(source.toURI());
} catch (URISyntaxException e) {
LOG.error("Could not scan URL " + source + " for dependencies", e);
return false;
}
if (alreadyScannedSources.contains(path))
return true;
alreadyScannedSources.add(path);
boolean found = false;
if (fileName.endsWith(".jar")) {
//Scan jar file for json in META-INF, add them to the list
@@ -297,19 +324,12 @@ public class DependencyLoaderImpl {
LOG.error("Failed to open jar file {}", source.getPath());
}
} else {
Path dir;
try {
dir = Paths.get(source.toURI());
} catch (URISyntaxException e) {
LOG.error("Could not scan URL " + source + " for dependencies", e);
return false;
}
if (!Files.exists(dir) || !Files.isDirectory(dir)) {
if (!Files.exists(path) || !Files.isDirectory(path)) {
LOG.warn("Skipping non-directory, nor jar source: {}", source);
return false;
}
//Scan directory for json in META-INF, add them to the list
val metaInf = dir.resolve("META-INF");
val metaInf = path.resolve("META-INF");
if (!Files.exists(metaInf) || !Files.isDirectory(metaInf)) {
return false;
}
@@ -397,30 +417,50 @@ public class DependencyLoaderImpl {
}
}
private static Pair<DependencyScope, DepRoot.SidedDependencies> scopedDependency(DependencyScope scope, DepRoot.SidedDependencies deps) {
return new Pair<>(scope, deps);
}
private static Stream<Pair<ScopeSide, String>> sidedDependency(DependencySide side, Stream<Pair<DependencyScope, String>> deps) {
return deps.map(dep -> new Pair<>(new ScopeSide(dep.a, side), dep.b));
}
private static boolean initialScan = false;
private static List<Pair<ScopeSide, DependencyLoadTask>> tasks;
private static final List<Pair<ScopeSide, DependencyLoadTask>> baseTasks = new ArrayList<>();
public static void executeDependencyLoading() {
if (!initialScan) {
initialScan = true;
scanDeps();
val tasks = scanDeps();
if (tasks != null) {
baseTasks.addAll(tasks);
}
}
executeArtifactLoading();
var tasks = executeArtifactLoading(baseTasks, false);
while (tasks != null) {
baseTasks.addAll(tasks);
tasks = executeArtifactLoading(baseTasks, false);
}
if (modDownloaded.get()) {
if (!SystemUtils.IS_OS_MAC) {
try {
JOptionPane.showMessageDialog(null, "A minecraft mod has been downloaded by the FalsePatternLib dependency downloader.\nYou must restart the game to apply these changes.", "Mod Dependency Download notice", JOptionPane.WARNING_MESSAGE);
} catch (Exception ignored) {}
}
throw new ModDependencyDownloaded("A minecraft mod has been downloaded by the FalsePatternLib dependency downloader, and requires a game restart to get installed properly.");
}
}
private static void scanDeps() {
private static class ModDependencyDownloaded extends Error {
public ModDependencyDownloaded(String message) {
super(message);
}
}
private static @Nullable Set<Pair<ScopeSide, DependencyLoadTask>> scanDeps() {
LOG.debug("Discovering dependency source candidates...");
val modsDir = FileUtil.getMinecraftHomePath().resolve("mods");
val mods1710Dir = modsDir.resolve("1.7.10");
return scanDeps(Stream.of(LowLevelCallMultiplexer.getClassPathSources().stream(),
grabSourceCandidatesFromFolder(modsDir),
grabSourceCandidatesFromFolder(mods1710Dir))
.flatMap(i -> i));
}
private static @Nullable Set<Pair<ScopeSide, DependencyLoadTask>> scanDeps(Stream<URL> candidatesUnfiltered) {
long start = System.currentTimeMillis();
val urlsWithoutDeps = new HashSet<String>();
val depCache = tempDir.resolve(".depscan_cache");
@@ -431,12 +471,10 @@ public class DependencyLoaderImpl {
LOG.error("Could not read dependency scanner cache", e);
}
}
val candidates = Stream.of(LowLevelCallMultiplexer.getClassPathSources().stream(),
grabSourceCandidatesFromFolder(modsDir),
grabSourceCandidatesFromFolder(mods1710Dir))
.flatMap((i) -> i)
.filter((url) -> !urlsWithoutDeps.contains(url.toString()))
.collect(Collectors.toList());
val candidates = candidatesUnfiltered
.filter(Objects::nonNull)
.filter((url) -> !urlsWithoutDeps.contains(url.toString()))
.collect(Collectors.toList());
val urls = new ArrayList<URL>();
val jijURLs = new ArrayList<URL>();
for (val candidate : candidates) {
@@ -489,23 +527,62 @@ public class DependencyLoaderImpl {
.map(repo -> repo.endsWith("/") ? repo : repo + "/")
.collect(Collectors.toSet()));
localMavenRepositories.addAll(jijURLs.stream().map(URL::toString).map(repo -> repo.endsWith("/") ? repo : repo + "/").collect(Collectors.toSet()));
for (DepRoot dependencySpec : dependencySpecs) {
val bundled = dependencySpec.bundledArtifacts();
if (bundled == null) {
continue;
}
for (String artifact : bundled) {
val parts = artifact.split(":");
if (parts.length < 3) {
LOG.error("Invalid bundled artifact: {}", dependencySpec);
continue;
}
val groupId = parts[0];
val artifactId = parts[1];
Version version = parseVersion(parts[2]);
final String classifier = parts.length > 3 ? parts[3] : null;
if (classifier != null) {
LOG.info("Found bundled artifact: {}:{}:{}:{} from {}",
groupId,
artifactId,
version,
classifier,
dependencySpec.source());
} else {
LOG.info("Found bundled artifact: {}:{}:{} from {}",
groupId,
artifactId,
version,
dependencySpec.source());
}
val id = groupId + ":" + artifactId + ":" + classifier;
if (!loadedLibraries.containsKey(id)) {
loadedLibraries.put(id, version);
}
if (!loadedLibraryMods.containsKey(id)) {
loadedLibraryMods.put(id, dependencySpec.source());
}
}
}
val artifacts = dependencySpecs.stream()
.map((root) -> new Pair<>(root.source(), root.dependencies()))
.flatMap(pair -> flatMap(pair,
(dep) -> Stream.of(scopedDependency(DependencyScope.ALWAYS, dep.always()),
scopedDependency(DependencyScope.DEV, dep.dev()),
scopedDependency(DependencyScope.OBF, dep.obf())
)))
.flatMap(pair -> flatMap(pair,
(dep) -> Stream.concat(sidedDependency(DependencySide.COMMON, flatMap(dep, d -> d.common().stream())),
Stream.concat(sidedDependency(DependencySide.CLIENT, flatMap(dep, d -> d.client().stream())),
sidedDependency(DependencySide.SERVER, flatMap(dep, d -> d.server().stream())))
)))
.map((pair) -> {
val source = pair.a;
val scopedDep = pair.b;
val dep = scopedDep.b;
val scope = scopedDep.a;
.flatMap(it -> Stream.of(
new SourcedDep(it.source(), it.dependencies(), false),
new SourcedDep(it.source(), it.modDependencies(), true)))
.filter(it -> it.dep != null)
.flatMap(it -> Stream.of(
new ScopedDep(it.source, it.mod, DependencyScope.ALWAYS, it.dep.always()),
new ScopedDep(it.source, it.mod, DependencyScope.DEV, it.dep.dev()),
new ScopedDep(it.source, it.mod, DependencyScope.OBF, it.dep.obf())))
.flatMap(it -> Stream.concat(it.deps.common().stream().map(dep -> new ScopedSidedDep(it.source, it.mod, new ScopeSide(it.scope, DependencySide.COMMON), dep)),
Stream.concat(
it.deps.client().stream().map(dep -> new ScopedSidedDep(it.source, it.mod, new ScopeSide(it.scope, DependencySide.CLIENT), dep)),
it.deps.server().stream().map(dep -> new ScopedSidedDep(it.source, it.mod, new ScopeSide(it.scope, DependencySide.SERVER), dep))
)))
.map((scopedSidedDep) -> {
val source = scopedSidedDep.source;
val scope = scopedSidedDep.scope;
val dep = scopedSidedDep.dep;
val parts = dep.split(":");
if (parts.length < 3) {
LOG.error("Invalid dependency: {}", dep);
@@ -537,11 +614,38 @@ public class DependencyLoaderImpl {
null,
version,
classifier,
classifier));
classifier,
scopedSidedDep.mod));
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
tasks = new ArrayList<>(artifacts);
if (artifacts.isEmpty()) {
return null;
}
return artifacts;
}
@RequiredArgsConstructor
private static class SourcedDep {
public final String source;
public final DepRoot.Dependencies dep;
public final boolean mod;
}
@RequiredArgsConstructor
private static class ScopedDep {
public final String source;
public final boolean mod;
public final DependencyScope scope;
public final DepRoot.SidedDependencies deps;
}
@RequiredArgsConstructor
private static class ScopedSidedDep {
public final String source;
public final boolean mod;
public final ScopeSide scope;
public final String dep;
}
public static Version parseVersion(String versionString) {
@@ -565,11 +669,11 @@ public class DependencyLoaderImpl {
private static class SideAwareAssistant {
static ScopeSide current() {
return new ScopeSide(Share.DEV_ENV ? DependencyScope.DEV : DependencyScope.OBF, FMLLaunchHandler.side() == Side.CLIENT ? DependencySide.CLIENT : DependencySide.SERVER);
return new ScopeSide(Share.DEV_ENV ? DependencyScope.DEV : DependencyScope.OBF, Share.CLIENT ? DependencySide.CLIENT : DependencySide.SERVER);
}
}
private static void executeArtifactLoading() {
private static @Nullable Set<Pair<ScopeSide, DependencyLoadTask>> executeArtifactLoading(List<Pair<ScopeSide, DependencyLoadTask>> tasks, boolean silent) {
val scopeSide = SideAwareAssistant.current();
val iter = tasks.iterator();
val artifactMap = new HashMap<String, DependencyLoadTask>();
@@ -598,15 +702,19 @@ public class DependencyLoaderImpl {
}
}
if (artifactMap.isEmpty())
return;
return null;
val futures = new ArrayList<CompletableFuture<Void>>();
LOG.info("-----------------------------------------------------------");
LOG.info("FalsePatternLib is downloading dependencies. Please wait...");
LOG.info("-----------------------------------------------------------");
val futures = new ArrayList<CompletableFuture<URL>>();
if (!silent) {
LOG.info("-----------------------------------------------------------");
LOG.info("FalsePatternLib is downloading dependencies. Please wait...");
LOG.info("-----------------------------------------------------------");
}
JFrame theFrame = null;
val progresses = new HashMap<DependencyLoadTask, JProgressBar>();
if (SystemUtils.IS_OS_MAC) {
if (silent) {
} else if (SystemUtils.IS_OS_MAC) {
LOG.info("MacOS detected, not creating progress window (your OS is buggy)");
} else {
try {
@@ -637,29 +745,36 @@ public class DependencyLoaderImpl {
}
if (theFrame != null) {
for (val task : artifactMap.values()) {
futures.add(CompletableFuture.runAsync(() -> {
futures.add(CompletableFuture.supplyAsync(() -> {
val bar = progresses.get(task);
bar.setString("Downloading...");
task.load();
val res = task.load();
bar.setMinimum(0);
bar.setMaximum(1);
bar.setValue(1);
bar.setString("Completed!");
return res;
}, executor));
}
} else {
for (val task : artifactMap.values()) {
futures.add(CompletableFuture.runAsync(task::load, executor));
futures.add(CompletableFuture.supplyAsync(task::load, executor));
}
}
val theFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
val theFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(ignored ->
futures.stream()
.map(it -> it.getNow(null))
.collect(Collectors.toList())
);
AtomicBoolean doViz = new AtomicBoolean(true);
if (theFrame != null) {
final var vizThread = getVizThread(doViz, progresses, theFrame);
vizThread.start();
}
List<URL> res;
try {
theFuture.join();
res = theFuture.join();
} catch (Error e) {
throw e;
} catch (Throwable t) {
@@ -669,6 +784,7 @@ public class DependencyLoaderImpl {
doViz.set(false);
}
}
return scanDeps(res.stream());
}
@NotNull
@@ -722,6 +838,7 @@ public class DependencyLoaderImpl {
private final Version preferredVersion;
private final String regularSuffix;
private final String devSuffix;
private final boolean isMod;
private String suffix;
private String artifactLogName;
@@ -733,35 +850,38 @@ public class DependencyLoaderImpl {
public volatile long contentLength = -1;
public volatile long downloaded = 0;
private void load() {
private @Nullable URL load() {
setupLibraryNames();
if (loadedLibraries.containsKey(artifact)) {
alreadyLoaded();
return;
return null;
}
setupPaths();
for (val repo: localMavenRepositories) {
if (tryDownloadFromMaven(repo, true)) {
return;
val url = tryDownloadFromMaven(repo, true);
if (url != null) {
return url;
}
}
if (tryLoadingExistingFile()) {
return;
val existingUrl = tryLoadingExistingFile();
if (existingUrl != null) {
return existingUrl;
}
validateDownloadsAllowed();
for (var repo : remoteMavenRepositories) {
if (tryDownloadFromMaven(repo, false)) {
return;
val url = tryDownloadFromMaven(repo, false);
if (url != null) {
return url;
}
}
crashCouldNotDownload();
throw crashCouldNotDownload();
}
private void crashCouldNotDownload() {
private IllegalStateException crashCouldNotDownload() {
val errorMessage = "Failed to download library " + groupId + ":" + artifactId + ":" + preferredVersion + (
(suffix != null) ? ":" + suffix : "") + " from any repository! Requested by mod: " + loadingModId;
LOG.fatal(errorMessage);
throw new IllegalStateException(errorMessage);
return new IllegalStateException(errorMessage);
}
private void setupLibraryNames() {
@@ -771,7 +891,9 @@ public class DependencyLoaderImpl {
artifactId,
preferredVersion,
suffix != null ? "-" + suffix : "");
LOG.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId);
if (!isMod) {
LOG.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId);
}
artifact = groupId + ":" + artifactId + ":" + suffix;
}
@@ -818,17 +940,17 @@ public class DependencyLoaderImpl {
mavenJarName =
String.format("%s-%s%s.jar", artifactId, preferredVersion, (suffix != null) ? ("-" + suffix) : "");
jarName = groupId + "-" + mavenJarName;
file = libDir.resolve(jarName);
file = (isMod ? modsDir : libDir).resolve(jarName);
}
private boolean tryLoadingExistingFile() {
private @Nullable URL tryLoadingExistingFile() {
if (!Files.exists(file)) {
return false;
return null;
}
try {
val status = validateChecksum(file);
if (status == ChecksumStatus.FAILED) {
return false;
return null;
} else if (status == ChecksumStatus.MISSING) {
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);
@@ -836,17 +958,20 @@ public class DependencyLoaderImpl {
} catch (IOException e) {
LOG.error("Failed to execute validation check for " + artifactLogName, e);
checkedDelete(file);
return false;
return null;
}
try {
addToClasspath(file.toUri().toURL());
val theUrl = file.toUri().toURL();
if (!isMod) {
addToClasspath(theUrl);
}
loadedLibraries.put(artifact, preferredVersion);
LOG.debug("Library {} successfully loaded from disk!", artifactLogName);
return true;
return theUrl;
} catch (Exception e) {
LOG.warn("Failed to load library {} from file! Re-downloading...", artifactLogName);
checkedDelete(file);
return false;
return null;
}
}
@@ -870,7 +995,7 @@ public class DependencyLoaderImpl {
}
private static final Object mutex = new Object();
private boolean tryDownloadFromMaven(String repo, boolean local) {
private @Nullable URL tryDownloadFromMaven(String repo, boolean local) {
synchronized (mutex) {
try {
if (!repo.endsWith("/")) {
@@ -907,10 +1032,17 @@ public class DependencyLoaderImpl {
},
contentLength -> this.contentLength = contentLength);
if (success.get()) {
try {
Files.move(tmpFile, file, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException ignored) {
Files.move(tmpFile, file);
if (FileUtils.contentEquals(tmpFile.toFile(), file.toFile())) {
Files.delete(tmpFile);
} else {
if (isMod) {
modDownloaded.set(true);
}
try {
Files.move(tmpFile, file, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException ignored) {
Files.move(tmpFile, file);
}
}
LOG.debug("Validating checksum for {}", artifactLogName);
val hadChecksum = validateChecksum(url);
@@ -928,13 +1060,16 @@ public class DependencyLoaderImpl {
}
loadedLibraries.put(artifact, preferredVersion);
loadedLibraryMods.put(artifact, loadingModId);
addToClasspath(file.toUri().toURL());
return true;
val diskUrl = file.toUri().toURL();
if (!isMod) {
addToClasspath(diskUrl);
}
return diskUrl;
}
}
} catch (IOException ignored) {
}
return false;
return null;
}
}
+25
View File
@@ -9,6 +9,9 @@ DependencyLoader api. This file needs to reside inside the META-INF directory.
"identifier": "falsepatternlib_dependencies",
"minJava": 8,
"maxJava": 8,
"bundledArtifacts": [
"com.example:myjar:1.2.3"
],
"repositories": [
"https://example.com/"
],
@@ -30,6 +33,25 @@ DependencyLoader api. This file needs to reside inside the META-INF directory.
"client": [],
"server": []
}
},
"modDependencies": {
"always": {
"common": [
"com.example:mymod:3.0.0"
],
"client": [],
"server": []
},
"obf": {
"common": [],
"client": [],
"server": []
},
"dev": {
"common": [],
"client": [],
"server": []
}
}
}
```
@@ -45,6 +67,7 @@ Explanation:
for any java versions above or equal minJava.
- If both `minJava` and `maxJava` are unset, dependencies from the file will be loaded regardless of java version.
- If you want to load dependencies for multiple different java ranges, use multiple dependency jsons.
- `bundledArtifacts`: A list of maven artifacts which are "shaded" into this jar without relocation. Useful for recursive deploading of mc mods.
- `repositories`: A list of maven repositories to use when downloading dependencies. These are used in addition to the
default maven repositories. This is just a list of strings, each string being a https repository url.
Additionally, every jar with a dependencies json file inside of it is treated as a "jar in jar" maven repository.
@@ -57,6 +80,8 @@ Explanation:
here.
- The `dev` category gets downloaded only in the dev environment. Usually not needed, as gradle will automatically
download dependencies.
- `modDependencies`: Same as `dependencies`, but files from here get downloaded into `.minecraft/mods/1.7.10`. Useful
if you want to download third party mods as well.
Each of these categories also have 3 more subcategories: