From 31f9719be3e3f9de3a6edd69532219471e4b162f Mon Sep 17 00:00:00 2001 From: FalsePattern <30945458+FalsePattern@users.noreply.github.com> Date: Sun, 24 Jul 2022 23:57:03 +0200 Subject: [PATCH] split up the dependency loader method into smaller parts --- .../dependencies/DependencyLoaderImpl.java | 366 +++++++++++------- 1 file changed, 219 insertions(+), 147 deletions(-) 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 94e5506..55ad5f6 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 @@ -8,6 +8,7 @@ import com.falsepattern.lib.internal.LibraryConfig; import com.falsepattern.lib.internal.Tags; import com.falsepattern.lib.util.FileUtil; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.val; import lombok.var; @@ -48,153 +49,8 @@ public class DependencyLoaderImpl { public static void loadLibrary(@NonNull String loadingModId, @NonNull String groupId, @NonNull String artifactId, @NonNull Version minVersion, Version maxVersion, @NonNull Version preferredVersion, String regularSuffix, String devSuffix) { - val suffix = FalsePatternLib.isDeveloperEnvironment() ? devSuffix : regularSuffix; - val artifactLogName = - String.format("%s:%s:%s%s", groupId, artifactId, preferredVersion, suffix != null ? "-" + suffix : ""); - log.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId); - var artifact = groupId + ":" + artifactId + ":" + suffix; - if (loadedLibraries.containsKey(artifact)) { - val currentVer = loadedLibraries.get(artifact); - if (currentVer.equals(preferredVersion)) { - return; - } - 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("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"); - } - } else { - 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, - loadedLibraryMods.get(artifact)); - } - return; - } - String homeDir = System.getProperty("minecraft.sharedDataDir"); - if (homeDir == null) { - homeDir = System.getenv("MINECRAFT_SHARED_DATA_DIR"); - if (homeDir == null) { - homeDir = FileUtil.getMinecraftHome().getAbsolutePath(); - } - } - val modsDir = Paths.get(homeDir, "mods").toFile(); - val mavenJarName = - String.format("%s-%s%s.jar", artifactId, preferredVersion, (suffix != null) ? ("-" + suffix) : ""); - val jarName = groupId + "-" + mavenJarName; - val libDir = new File(modsDir, "falsepattern"); - if (!libDir.exists()) { - if (!libDir.mkdirs()) { - log.fatal("Failed to create directory {}", libDir); - throw new RuntimeException("Failed to create directory " + libDir); - } - } - val file = new File(libDir, jarName); - if (file.exists()) { - try { - addToClasspath(file); - loadedLibraries.put(artifact, preferredVersion); - log.debug("Library {} successfully loaded from disk!", artifactLogName); - return; - } catch (RuntimeException e) { - log.warn("Failed to load library {} from file! Re-downloading...", artifactLogName); - checkedDelete(file); - } - } - if (!LibraryConfig.ENABLE_LIBRARY_DOWNLOADS) { - val errorMessage = "Failed to load library " + groupId + ":" + artifactId + ":" + preferredVersion + - ((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); - throw new IllegalStateException(errorMessage); - } - for (var repo : mavenRepositories) { - try { - if (!repo.endsWith("/")) { - repo = repo + "/"; - } - val url = String.format("%s%s/%s/%s/%s", repo, groupId.replace('.', '/'), artifactId, preferredVersion, - mavenJarName); - String finalRepo = repo; - boolean retry = true; - int retryCount = 0; - redownload: - while (retry) { - retry = false; - retryCount++; - if (retryCount >= 3) { - break; - } - val success = new AtomicBoolean(false); - Internet.connect(new URL(url), - (ex) -> log.debug("Artifact {} could not be downloaded from repo {}: {}", - artifactLogName, finalRepo, ex.getMessage()), (input) -> { - log.debug("Downloading {} from {}", artifactLogName, finalRepo); - download(input, file); - log.debug("Downloaded {} from {}", artifactLogName, finalRepo); - success.set(true); - }); - if (success.get()) { - log.debug("Validating checksum for {}", artifactLogName); - boolean hadChecksum = false; - for (val checksumType : CHECKSUM_TYPES) { - val checksumURL = url + "." + checksumType; - val checksumFile = new File(libDir, jarName + "." + checksumType); - log.debug("Attempting to get {} checksum...", checksumType); - success.set(false); - Internet.connect(new URL(checksumURL), - (ex) -> log.debug("Could not get {} checksum for {}: {}", checksumType, - artifactLogName, ex.getMessage()), (input) -> { - log.debug("Downloading {} checksum for {}", checksumType, artifactLogName); - download(input, checksumFile); - log.debug("Downloaded {} checksum for {}", checksumType, artifactLogName); - success.set(true); - }); - if (success.get()) { - val fileHash = hash(checksumType, file); - val referenceHash = new String(Files.readAllBytes(checksumFile.toPath())); - if (!fileHash.equals(referenceHash)) { - log.error("Failed {} checksum validation for {}. Retrying download...", - checksumType, artifactLogName); - checkedDelete(file); - checkedDelete(checksumFile); - retry = true; - continue redownload; - } - log.debug("Successfully validated {} checksum for {}", checksumType, artifactLogName); - hadChecksum = true; - break; - } - } - if (!hadChecksum) { - 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); - } - loadedLibraries.put(artifact, preferredVersion); - loadedLibraryMods.put(artifact, loadingModId); - addToClasspath(file); - return; - } - } - } catch (IOException ignored) { - } - } - 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); + new DependencyLoadTask(loadingModId, groupId, artifactId, minVersion, maxVersion, preferredVersion, regularSuffix, devSuffix) + .load(); } private static String bytesToHex(byte[] hash) { @@ -258,4 +114,220 @@ public class DependencyLoaderImpl { } Internet.transferAndClose(is, new BufferedOutputStream(Files.newOutputStream(target.toPath()))); } + + @RequiredArgsConstructor + private static class DependencyLoadTask { + @NonNull + private final String loadingModId; + @NonNull + private final String groupId; + @NonNull + private final String artifactId; + @NonNull + private final Version minVersion; + private final Version maxVersion; + @NonNull + private final Version preferredVersion; + private final String regularSuffix; + private final String devSuffix; + + private String suffix; + private String artifactLogName; + private String artifact; + private String mavenJarName; + private String jarName; + private File libDir; + 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)) { + return; + } + } + 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); + throw new IllegalStateException(errorMessage); + } + + private void setupLibraryNames() { + suffix = FalsePatternLib.isDeveloperEnvironment() ? devSuffix : regularSuffix; + artifactLogName = + String.format("%s:%s:%s%s", groupId, artifactId, preferredVersion, suffix != null ? "-" + suffix : ""); + log.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId); + artifact = groupId + ":" + artifactId + ":" + suffix; + } + + private void alreadyLoaded() { + val currentVer = loadedLibraries.get(artifact); + if (currentVer.equals(preferredVersion)) { + return; + } + 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("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"); + } + } else { + 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, + loadedLibraryMods.get(artifact)); + } + } + + private void setupPaths() { + var homeDir = System.getProperty("minecraft.sharedDataDir"); + if (homeDir == null) { + homeDir = System.getenv("MINECRAFT_SHARED_DATA_DIR"); + if (homeDir == null) { + homeDir = FileUtil.getMinecraftHome().getAbsolutePath(); + } + } + val modsDir = Paths.get(homeDir, "mods").toFile(); + mavenJarName = String.format("%s-%s%s.jar", artifactId, preferredVersion, + (suffix != null) ? ("-" + suffix) : ""); + jarName = groupId + "-" + mavenJarName; + libDir = new File(modsDir, "falsepattern"); + if (!libDir.exists()) { + if (!libDir.mkdirs()) { + log.fatal("Failed to create directory {}", libDir); + throw new RuntimeException("Failed to create directory " + libDir); + } + } + file = new File(libDir, jarName); + } + + private boolean tryLoadingExistingFile() { + if (!file.exists()) return false; + try { + addToClasspath(file); + loadedLibraries.put(artifact, preferredVersion); + log.debug("Library {} successfully loaded from disk!", artifactLogName); + return true; + } catch (RuntimeException e) { + log.warn("Failed to load library {} from file! Re-downloading...", artifactLogName); + checkedDelete(file); + return false; + } + } + + private void validateDownloadsAllowed() { + if (!LibraryConfig.ENABLE_LIBRARY_DOWNLOADS) { + val errorMessage = "Failed to load library " + groupId + ":" + artifactId + ":" + preferredVersion + + ((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); + throw new IllegalStateException(errorMessage); + } + } + + private boolean tryDownloadFromMaven(String repo) { + try { + if (!repo.endsWith("/")) { + repo = repo + "/"; + } + val url = String.format("%s%s/%s/%s/%s", repo, groupId.replace('.', '/'), artifactId, + preferredVersion, mavenJarName); + String finalRepo = repo; + int retryCount = 0; + while (true) { + retryCount++; + if (retryCount > 3) { + break; + } + val success = new AtomicBoolean(false); + Internet.connect(new URL(url), (ex) -> log.debug("Artifact {} could not be downloaded from repo {}: {}", + artifactLogName, finalRepo, ex.getMessage()), + (input) -> { + log.debug("Downloading {} from {}", artifactLogName, finalRepo); + download(input, file); + log.debug("Downloaded {} from {}", artifactLogName, finalRepo); + success.set(true); + }); + if (success.get()) { + log.debug("Validating checksum for {}", artifactLogName); + val hadChecksum = validateChecksum(url); + switch (hadChecksum) { + case FAILED: + continue; + case OK: + break; + case MISSING: + 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); + } + loadedLibraries.put(artifact, preferredVersion); + loadedLibraryMods.put(artifact, loadingModId); + addToClasspath(file); + return true; + } + } + } catch (IOException ignored) { + } + return false; + } + + private ChecksumStatus validateChecksum(String url) throws IOException { + for (val checksumType : CHECKSUM_TYPES) { + val checksumURL = url + "." + checksumType; + val checksumFile = new File(libDir, jarName + "." + 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, artifactLogName, ex.getMessage()), + (input) -> { + log.debug("Downloading {} checksum for {}", checksumType, artifactLogName); + download(input, checksumFile); + log.debug("Downloaded {} checksum for {}", checksumType, artifactLogName); + success.set(true); + }); + if (success.get()) { + val fileHash = hash(checksumType, file); + val referenceHash = new String(Files.readAllBytes(checksumFile.toPath())); + if (!fileHash.equals(referenceHash)) { + log.error("Failed {} checksum validation for {}. Retrying download...", checksumType, + artifactLogName); + checkedDelete(file); + checkedDelete(checksumFile); + return ChecksumStatus.FAILED; + } + log.debug("Successfully validated {} checksum for {}", checksumType, artifactLogName); + return ChecksumStatus.OK; + } + } + return ChecksumStatus.MISSING; + } + + private enum ChecksumStatus { + OK, FAILED, MISSING + } + } }