diff --git a/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java b/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java index de5481b..c3f7838 100644 --- a/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java +++ b/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java @@ -39,7 +39,7 @@ public class DependencyLoader { * @since 0.10.0 */ public static CompletableFuture loadLibrariesAsync(Library... libraries) { - return DependencyLoaderImpl.loadLibrariesAsync(libraries); + return DependencyLoaderImpl.loadLibrariesAsync(libraries).thenApply(list -> null); } /** diff --git a/src/main/java/com/falsepattern/lib/internal/Share.java b/src/main/java/com/falsepattern/lib/internal/Share.java index 35a6657..abd7715 100644 --- a/src/main/java/com/falsepattern/lib/internal/Share.java +++ b/src/main/java/com/falsepattern/lib/internal/Share.java @@ -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; } diff --git a/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java b/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java index cd7f230..e60cdef 100644 --- a/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java +++ b/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java @@ -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 { diff --git a/src/main/java/com/falsepattern/lib/internal/asm/PreShare.java b/src/main/java/com/falsepattern/lib/internal/asm/PreShare.java index d9c40dc..0109aac 100644 --- a/src/main/java/com/falsepattern/lib/internal/asm/PreShare.java +++ b/src/main/java/com/falsepattern/lib/internal/asm/PreShare.java @@ -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; } } diff --git a/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java b/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java index 493a640..704053e 100644 --- a/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java +++ b/src/main/java/com/falsepattern/lib/internal/asm/RFBLoadingPlugin.java @@ -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(); diff --git a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DepRoot.java b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DepRoot.java index fb98205..a766ab6 100644 --- a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DepRoot.java +++ b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DepRoot.java @@ -37,9 +37,13 @@ public class DepRoot { @Expose private Integer maxJava; @Expose + private List bundledArtifacts; + @Expose private List repositories; @Expose private Dependencies dependencies; + @Expose + private Dependencies modDependencies; @Data @Accessors(fluent = true) diff --git a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java index 7565ef9..7e59b62 100644 --- a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java +++ b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java @@ -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 alreadyScannedSources = new ConcurrentSet<>(); private static final Map loadedLibraries = new ConcurrentHashMap<>(); private static final Map loadedLibraryMods = new ConcurrentHashMap<>(); private static final Set 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 loadLibrariesAsync(Library... libraries) { - val futures = new ArrayList>(); + val futures = new ArrayList>(); 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 output, List 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 scopedDependency(DependencyScope scope, DepRoot.SidedDependencies deps) { - return new Pair<>(scope, deps); - } - - private static Stream> sidedDependency(DependencySide side, Stream> deps) { - return deps.map(dep -> new Pair<>(new ScopeSide(dep.a, side), dep.b)); - } - private static boolean initialScan = false; - private static List> tasks; + private static final List> 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> 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> scanDeps(Stream candidatesUnfiltered) { long start = System.currentTimeMillis(); val urlsWithoutDeps = new HashSet(); 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(); val jijURLs = new ArrayList(); 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> executeArtifactLoading(List> tasks, boolean silent) { val scopeSide = SideAwareAssistant.current(); val iter = tasks.iterator(); val artifactMap = new HashMap(); @@ -598,15 +702,19 @@ public class DependencyLoaderImpl { } } if (artifactMap.isEmpty()) - return; + return null; - val futures = new ArrayList>(); - LOG.info("-----------------------------------------------------------"); - LOG.info("FalsePatternLib is downloading dependencies. Please wait..."); - LOG.info("-----------------------------------------------------------"); + val futures = new ArrayList>(); + if (!silent) { + LOG.info("-----------------------------------------------------------"); + LOG.info("FalsePatternLib is downloading dependencies. Please wait..."); + LOG.info("-----------------------------------------------------------"); + } JFrame theFrame = null; val progresses = new HashMap(); - 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 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; } } diff --git a/src/main/resources/DEPENDENCIES.md b/src/main/resources/DEPENDENCIES.md index ae418f0..a37bcac 100644 --- a/src/main/resources/DEPENDENCIES.md +++ b/src/main/resources/DEPENDENCIES.md @@ -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: