From 0d1f40aee4cf8667fc4a2ff6efad50fec9bed81c Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Fri, 12 Feb 2021 17:50:54 +0000 Subject: [PATCH] Rewrite minecraft game data handling. Much faster asset and native validation logic. Simplfied game metadata model. --- .../ide/SetupIntelijRunConfigs.java | 2 +- .../providers/LaunchProvider.java | 2 +- .../providers/MinecraftProvider.java | 16 +- .../mappings/MojangMappingsDependency.java | 10 +- .../minecraft/MinecraftLibraryProvider.java | 17 +- .../minecraft/MinecraftNativesProvider.java | 114 +++++-- .../minecraft/MinecraftVersionInfo.java | 198 ------------- .../minecraft/MinecraftVersionMeta.java | 277 ++++++++++++++++++ .../assets/MinecraftAssetsProvider.java | 147 ++++------ .../loom/task/DownloadAssetsTask.java | 2 +- 10 files changed, 442 insertions(+), 343 deletions(-) delete mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionInfo.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java b/src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java index a838faf..14576ec 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java @@ -59,7 +59,7 @@ public class SetupIntelijRunConfigs { if (extension.ideSync()) { //Ensures the assets are downloaded when idea is syncing a project MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project); - MinecraftNativesProvider.provide(extension.getMinecraftProvider(), project); + MinecraftNativesProvider.provide(project); } String projectPath = project == rootProject ? "" : project.getPath().replace(':', '_'); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java index 07b8c04..c809e7a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java @@ -64,7 +64,7 @@ public class LaunchProvider extends DependencyProvider { .property("client", "org.lwjgl.librarypath", getExtension().getNativesDirectory().getAbsolutePath()) .argument("client", "--assetIndex") - .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex.getFabricId(getExtension().getMinecraftProvider().getMinecraftVersion())) + .argument("client", getExtension().getMinecraftProvider().getVersionInfo().getAssetIndex().getFabricId(getExtension().getMinecraftProvider().getMinecraftVersion())) .argument("client", "--assetsDir") .argument("client", new File(getExtension().getUserCache(), "assets").getAbsolutePath()); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java index 8e6af5f..e25b8ca 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java @@ -43,7 +43,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.DependencyProvider; import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftLibraryProvider; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionInfo; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.DownloadUtil; import net.fabricmc.loom.util.HashedDownloadUtil; @@ -52,7 +52,7 @@ import net.fabricmc.stitch.merge.JarMerger; public class MinecraftProvider extends DependencyProvider { private String minecraftVersion; - private MinecraftVersionInfo versionInfo; + private MinecraftVersionMeta versionInfo; private MinecraftLibraryProvider libraryProvider; private File minecraftJson; @@ -76,7 +76,7 @@ public class MinecraftProvider extends DependencyProvider { downloadMcJson(offline); try (FileReader reader = new FileReader(minecraftJson)) { - versionInfo = LoomGradlePlugin.GSON.fromJson(reader, MinecraftVersionInfo.class); + versionInfo = LoomGradlePlugin.GSON.fromJson(reader, MinecraftVersionMeta.class); } // Add Loom as an annotation processor @@ -209,11 +209,11 @@ public class MinecraftProvider extends DependencyProvider { return; } - MinecraftVersionInfo.Downloads client = versionInfo.downloads.get("client"); - MinecraftVersionInfo.Downloads server = versionInfo.downloads.get("server"); + MinecraftVersionMeta.Download client = versionInfo.getDownload("client"); + MinecraftVersionMeta.Download server = versionInfo.getDownload("server"); - HashedDownloadUtil.downloadIfInvalid(new URL(client.url), minecraftClientJar, client.sha1, logger, false); - HashedDownloadUtil.downloadIfInvalid(new URL(server.url), minecraftServerJar, server.sha1, logger, false); + HashedDownloadUtil.downloadIfInvalid(new URL(client.getUrl()), minecraftClientJar, client.getSha1(), logger, false); + HashedDownloadUtil.downloadIfInvalid(new URL(server.getUrl()), minecraftServerJar, server.getSha1(), logger, false); } private void mergeJars(Logger logger) throws IOException { @@ -233,7 +233,7 @@ public class MinecraftProvider extends DependencyProvider { return minecraftVersion; } - public MinecraftVersionInfo getVersionInfo() { + public MinecraftVersionMeta getVersionInfo() { return versionInfo; } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java index 2266495..96c4c30 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java @@ -55,7 +55,7 @@ import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionInfo; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.util.DownloadUtil; import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mapping.tree.TinyMappingFactory; @@ -121,14 +121,14 @@ public class MojangMappingsDependency implements SelfResolvingDependency { } private MappingSet getMappingsSet(Path clientMappings, Path serverMappings) throws IOException { - MinecraftVersionInfo versionInfo = extension.getMinecraftProvider().getVersionInfo(); + MinecraftVersionMeta versionInfo = extension.getMinecraftProvider().getVersionInfo(); - if (versionInfo.downloads.get(MANIFEST_CLIENT_MAPPINGS) == null) { + if (versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS) == null) { throw new RuntimeException("Failed to find official mojang mappings for " + getVersion()); } - String clientMappingsUrl = versionInfo.downloads.get(MANIFEST_CLIENT_MAPPINGS).url; - String serverMappingsUrl = versionInfo.downloads.get(MANIFEST_SERVER_MAPPINGS).url; + String clientMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl(); + String serverMappingsUrl = versionInfo.getDownload(MANIFEST_CLIENT_MAPPINGS).getUrl(); DownloadUtil.downloadIfChanged(new URL(clientMappingsUrl), clientMappings.toFile(), project.getLogger()); DownloadUtil.downloadIfChanged(new URL(serverMappingsUrl), serverMappings.toFile(), project.getLogger()); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java index 8822140..dcf846e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java @@ -36,22 +36,13 @@ public class MinecraftLibraryProvider { public File MINECRAFT_LIBS; public void provide(MinecraftProvider minecraftProvider, Project project) { - MinecraftVersionInfo versionInfo = minecraftProvider.getVersionInfo(); + MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); initFiles(project, minecraftProvider); - for (MinecraftVersionInfo.Library library : versionInfo.libraries) { - if (library.allowed() && !library.isNative() && library.getFile(MINECRAFT_LIBS) != null) { - // TODO: Add custom library locations - - // By default, they are all available on all sides - /* boolean isClientOnly = false; - - if (library.name.contains("java3d") || library.name.contains("paulscode") || library.name.contains("lwjgl") || library.name.contains("twitch") || library.name.contains("jinput") || library.name.contains("text2speech") || library.name.contains("objc")) { - isClientOnly = true; - } */ - - project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.getArtifactName())); + for (MinecraftVersionMeta.Library library : versionInfo.getLibraries()) { + if (library.isValidForOS() && !library.hasNatives() && library.getArtifact() != null) { + project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.getName())); } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java index 9f338ef..08c4c6d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java @@ -27,24 +27,39 @@ package net.fabricmc.loom.configuration.providers.minecraft; import java.io.File; import java.io.IOException; import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.apache.commons.io.FileUtils; import org.gradle.api.GradleException; import org.gradle.api.Project; import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; -import net.fabricmc.loom.util.DownloadUtil; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.util.HashedDownloadUtil; public class MinecraftNativesProvider { - public static void provide(MinecraftProvider minecraftProvider, Project project) throws IOException { - LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); - MinecraftVersionInfo versionInfo = minecraftProvider.getVersionInfo(); - boolean offline = project.getGradle().getStartParameter().isOffline(); + private final Project project; + private final LoomGradleExtension extension; + private final File nativesDir; + private final File jarStore; - File nativesDir = extension.getNativesDirectory(); - File jarStore = extension.getNativesJarStore(); + public MinecraftNativesProvider(Project project) { + this.project = project; + extension = project.getExtensions().getByType(LoomGradleExtension.class); + nativesDir = extension.getNativesDirectory(); + jarStore = extension.getNativesJarStore(); + } + + public static void provide(Project project) throws IOException { + new MinecraftNativesProvider(project).provide(); + } + + private void provide() throws IOException { if (extension.hasCustomNatives()) { if (!nativesDir.exists()) { throw new RuntimeException("Could no find custom natives directory at " + nativesDir.getAbsolutePath()); @@ -53,21 +68,82 @@ public class MinecraftNativesProvider { return; } - for (MinecraftVersionInfo.Library library : versionInfo.libraries) { - File libJarFile = library.getFile(jarStore); + if (!LoomGradlePlugin.refreshDeps && !requiresExtract()) { + project.getLogger().info("Natives do no need extracting, skipping"); + return; + } - if (library.allowed() && library.isNative() && libJarFile != null) { - if (!offline) { - DownloadUtil.downloadIfChanged(new URL(library.getURL()), libJarFile, project.getLogger()); - } + extractNatives(); + } - if (!libJarFile.exists()) { - throw new GradleException("Native jar not found at " + libJarFile.getAbsolutePath()); - } + private void extractNatives() throws IOException { + boolean offline = project.getGradle().getStartParameter().isOffline(); - // TODO possibly find a way to prevent needing to re-extract after each run, doesnt seem too slow - ZipUtil.unpack(libJarFile, nativesDir); + if (nativesDir.exists()) { + try { + FileUtils.deleteDirectory(nativesDir); + } catch (IOException e) { + throw new IOException("Failed to delete the natives directory, is the game running?", e); } } + + nativesDir.mkdirs(); + + for (MinecraftVersionMeta.Classifier library : getNatives()) { + File libJarFile = library.getRelativeFile(jarStore); + + if (!offline) { + HashedDownloadUtil.downloadIfInvalid(new URL(library.getUrl()), libJarFile, library.getSha1(), project.getLogger(), false); + } + + if (!libJarFile.exists()) { + throw new GradleException("Native jar not found at " + libJarFile.getAbsolutePath()); + } + + ZipUtil.unpack(libJarFile, nativesDir); + + // Store a file containing the hash of the extracted natives, used on subsequent runs to skip extracting all the natives if they haven't changed + File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1"); + FileUtils.writeStringToFile(libSha1File, library.getSha1(), StandardCharsets.UTF_8); + } + } + + private boolean requiresExtract() { + List natives = getNatives(); + + if (natives.isEmpty()) { + throw new IllegalStateException("No natives found for the current system"); + } + + for (MinecraftVersionMeta.Classifier library : natives) { + File libJarFile = library.getRelativeFile(jarStore); + File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1"); + + if (!libSha1File.exists()) { + return true; + } + + try { + String sha1 = FileUtils.readFileToString(libSha1File, StandardCharsets.UTF_8); + + if (!sha1.equalsIgnoreCase(library.getSha1())) { + return true; + } + } catch (IOException e) { + project.getLogger().error("Failed to read " + libSha1File.getAbsolutePath(), e); + return true; + } + } + + // All looks good, no need to re-extract + return false; + } + + private List getNatives() { + return extension.getMinecraftProvider().getVersionInfo().getLibraries().stream() + .filter((MinecraftVersionMeta.Library::hasNativesForOS)) + .map(MinecraftVersionMeta.Library::getClassifierForOS) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionInfo.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionInfo.java deleted file mode 100644 index 0994b4c..0000000 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionInfo.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016, 2017, 2018 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration.providers.minecraft; - -import java.io.File; -import java.util.List; -import java.util.Map; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.OperatingSystem; - -public class MinecraftVersionInfo { - public List libraries; - public Map downloads; - public AssetIndex assetIndex; - - public class Downloads { - public String url; - public String sha1; - } - - public class AssetIndex { - private String id; - public String sha1; - public String url; - - public String getId() { - return id; - } - - public String getFabricId(String version) { - return id.equals(version) ? version : version + "-" + id; - } - } - - public class Library { - public String name; - public JsonObject natives; - public JsonObject downloads; - private Artifact artifact; - public Rule[] rules; - - public String getURL() { - String path; - String[] parts = this.name.split(":", 3); - path = parts[0].replace(".", "/") + "/" + parts[1] + "/" + parts[2] + "/" + parts[1] + "-" + parts[2] + getClassifier() + ".jar"; - return Constants.LIBRARIES_BASE + path; - } - - public File getFile(File baseDir) { - String[] parts = this.name.split(":", 3); - return new File(baseDir, parts[0].replace(".", File.separator) + File.separator + parts[1] + File.separator + parts[2] + File.separator + parts[1] + "-" + parts[2] + getClassifier() + ".jar"); - } - - public String getSha1() { - if (this.downloads == null) { - return ""; - } else if (this.downloads.getAsJsonObject("artifact") == null) { - return ""; - } else if (this.downloads.getAsJsonObject("artifact").get("sha1") == null) { - return ""; - } else { - return this.downloads.getAsJsonObject("artifact").get("sha1").getAsString(); - } - } - - public String getClassifier() { - if (natives == null) { - return ""; - } else { - JsonElement element = natives.get(OperatingSystem.getOS().replace("${arch}", OperatingSystem.getArch())); - - if (element == null) { - return ""; - } - - return "-" + element.getAsString().replace("\"", "").replace("${arch}", OperatingSystem.getArch()); - } - } - - public boolean isNative() { - return getClassifier().contains("natives"); - } - - public boolean allowed() { - if (this.rules == null || this.rules.length <= 0) { - return true; - } - - boolean success = false; - - for (Rule rule : this.rules) { - if (rule.os != null && rule.os.name != null) { - if (rule.os.name.equalsIgnoreCase(OperatingSystem.getOS())) { - return rule.action.equalsIgnoreCase("allow"); - } - } else { - success = rule.action.equalsIgnoreCase("allow"); - } - } - - return success; - } - - public String getArtifactName() { - if (artifact == null) { - artifact = new Artifact(name); - } - - if (natives != null) { - JsonElement jsonElement = natives.get(OperatingSystem.getOS()); - - if (jsonElement != null) { - return artifact.getArtifact(jsonElement.getAsString()); - } - } - - return artifact.getArtifact(artifact.classifier); - } - - private class Artifact { - private final String domain, name, version, classifier, ext; - - Artifact(String name) { - String[] splitedArtifact = name.split(":"); - int idx = splitedArtifact[splitedArtifact.length - 1].indexOf('@'); - - if (idx != -1) { - ext = splitedArtifact[splitedArtifact.length - 1].substring(idx + 1); - splitedArtifact[splitedArtifact.length - 1] = splitedArtifact[splitedArtifact.length - 1].substring(0, idx); - } else { - ext = "jar"; - } - - this.domain = splitedArtifact[0]; - this.name = splitedArtifact[1]; - this.version = splitedArtifact[2]; - this.classifier = splitedArtifact.length > 3 ? splitedArtifact[3] : null; - } - - public String getArtifact(String classifier) { - String ret = domain + ":" + name + ":" + version; - - if (classifier != null && classifier.indexOf('$') > -1) { - classifier = classifier.replace("${arch}", Constants.SYSTEM_ARCH); - } - - if (classifier != null) { - ret += ":" + classifier; - } - - if (!"jar".equals(ext)) { - ret += "@" + ext; - } - - return ret; - } - - public String getClassifier() { - return classifier; - } - } - } - - private class Rule { - public String action; - public OS os; - - private class OS { - String name; - } - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java new file mode 100644 index 0000000..f667628 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java @@ -0,0 +1,277 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.minecraft; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import com.google.gson.JsonObject; + +import net.fabricmc.loom.util.OperatingSystem; + +@SuppressWarnings("unused") +public final class MinecraftVersionMeta { + private JsonObject arguments; + private AssetIndex assetIndex; + private String assets; + private int complianceLevel; + private Map downloads; + private String id; + private List libraries; + private JsonObject logging; + private String mainClass; + private int minimumLauncherVersion; + private String releaseTime; + private String time; + private String type; + + public Download getDownload(String key) { + return getDownloads().get(key); + } + + public JsonObject getArguments() { + return arguments; + } + + public AssetIndex getAssetIndex() { + return assetIndex; + } + + public String getAssets() { + return assets; + } + + public int getComplianceLevel() { + return complianceLevel; + } + + public Map getDownloads() { + return downloads; + } + + public String getId() { + return id; + } + + public List getLibraries() { + return libraries; + } + + public JsonObject getLogging() { + return logging; + } + + public String getMainClass() { + return mainClass; + } + + public int getMinimumLauncherVersion() { + return minimumLauncherVersion; + } + + public String getReleaseTime() { + return releaseTime; + } + + public String getTime() { + return time; + } + + public String getType() { + return type; + } + + public final class AssetIndex extends Downloadable { + private String id; + private long totalSize; + + public String getFabricId(String version) { + return id.equals(version) ? version : version + "-" + id; + } + + public String getId() { + return id; + } + + public long getTotalSize() { + return totalSize; + } + } + + public final class Download extends Downloadable { + } + + public final class Library { + private Downloads downloads; + private String name; + private Map natives; + private List rules; + + public boolean isValidForOS() { + if (rules == null || rules.isEmpty()) { + return true; + } + + for (Rule rule : rules) { + if (rule.appliesToOS() && !rule.isAllowed()) { + return false; + } + } + + return true; + } + + public boolean hasNatives() { + return this.natives != null; + } + + public boolean hasNativesForOS() { + if (!hasNatives()) { + return false; + } + + if (natives.get(OperatingSystem.getOS()) == null) { + return false; + } + + return isValidForOS(); + } + + public Classifier getClassifierForOS() { + return getDownloads().getClassifier(natives.get(OperatingSystem.getOS())); + } + + public Downloads getDownloads() { + return downloads; + } + + public Artifact getArtifact() { + if (getDownloads() == null) { + return null; + } + + return getDownloads().getArtifact(); + } + + public String getName() { + return name; + } + + public Map getNatives() { + return natives; + } + + public List getRules() { + return rules; + } + } + + public final class Downloads { + private Artifact artifact; + private Map classifiers; + + public Classifier getClassifier(String os) { + return classifiers.get(os); + } + + public Artifact getArtifact() { + return artifact; + } + + public Map getClassifiers() { + return classifiers; + } + } + + public final class Artifact extends Downloadable { + } + + public final class Classifier extends Downloadable { + } + + public final class Rule { + private String action; + private OS os; + + public boolean appliesToOS() { + return getOS() == null || getOS().isValidForOS(); + } + + public boolean isAllowed() { + return getAction().equals("allow"); + } + + public String getAction() { + return action; + } + + public OS getOS() { + return os; + } + } + + public final class OS { + private String name; + + public boolean isValidForOS() { + return getName() == null || getName().equalsIgnoreCase(OperatingSystem.getOS()); + } + + public String getName() { + return name; + } + } + + // A base class for everything that can be downloaded + public abstract class Downloadable { + private String path; + private String sha1; + private long size; + private String url; + + public File getRelativeFile(File baseDirectory) { + Objects.requireNonNull(getPath(), "Cannot get relative file from a null path"); + return new File(baseDirectory, getPath()); + } + + public String getPath() { + return path; + } + + public String getSha1() { + return sha1; + } + + public long getSize() { + return size; + } + + public String getUrl() { + return url; + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java index 55e07c1..64722c6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java @@ -26,32 +26,25 @@ package net.fabricmc.loom.configuration.providers.minecraft.assets; import java.io.File; import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.net.URL; import java.util.Deque; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import com.google.common.base.Stopwatch; -import com.google.common.hash.Hashing; -import com.google.common.io.Files; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; import org.gradle.api.GradleException; import org.gradle.api.Project; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.providers.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionInfo; -import net.fabricmc.loom.util.Checksum; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.DownloadUtil; +import net.fabricmc.loom.util.HashedDownloadUtil; import net.fabricmc.loom.util.gradle.ProgressLogger; public class MinecraftAssetsProvider { @@ -59,8 +52,8 @@ public class MinecraftAssetsProvider { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); boolean offline = project.getGradle().getStartParameter().isOffline(); - MinecraftVersionInfo versionInfo = minecraftProvider.getVersionInfo(); - MinecraftVersionInfo.AssetIndex assetIndex = versionInfo.assetIndex; + MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); + MinecraftVersionMeta.AssetIndex assetIndex = versionInfo.getAssetIndex(); // get existing cache files File assets = new File(extension.getUserCache(), "assets"); @@ -70,42 +63,28 @@ public class MinecraftAssetsProvider { } File assetsInfo = new File(assets, "indexes" + File.separator + assetIndex.getFabricId(minecraftProvider.getMinecraftVersion()) + ".json"); - File checksumInfo = new File(assets, "checksum" + File.separator + minecraftProvider.getMinecraftVersion() + ".json"); - if (!assetsInfo.exists() || !Checksum.equals(assetsInfo, assetIndex.sha1)) { - project.getLogger().info(":downloading asset index"); + project.getLogger().info(":downloading asset index"); - if (offline) { - if (assetsInfo.exists()) { - //We know it's outdated but can't do anything about it, oh well - project.getLogger().warn("Asset index outdated"); - } else { - //We don't know what assets we need, just that we don't have any - throw new GradleException("Asset index not found at " + assetsInfo.getAbsolutePath()); - } + if (offline) { + if (assetsInfo.exists()) { + //We know it's outdated but can't do anything about it, oh well + project.getLogger().warn("Asset index outdated"); } else { - DownloadUtil.downloadIfChanged(new URL(assetIndex.url), assetsInfo, project.getLogger()); - } - } - - Gson gson = new Gson(); - Map checksumInfos = new ConcurrentHashMap<>(); - - if (checksumInfo.exists()) { - try (FileReader reader = new FileReader(checksumInfo)) { - checksumInfos.putAll(gson.fromJson(reader, new TypeToken>() { - }.getType())); + //We don't know what assets we need, just that we don't have any + throw new GradleException("Asset index not found at " + assetsInfo.getAbsolutePath()); } + } else { + HashedDownloadUtil.downloadIfInvalid(new URL(assetIndex.getUrl()), assetsInfo, assetIndex.getSha1(), project.getLogger(), false); } Deque loggers = new ConcurrentLinkedDeque<>(); ExecutorService executor = Executors.newFixedThreadPool(Math.min(10, Math.max(Runtime.getRuntime().availableProcessors() / 2, 1))); - int toDownload = 0; AssetIndex index; try (FileReader fileReader = new FileReader(assetsInfo)) { - index = gson.fromJson(fileReader, AssetIndex.class); + index = LoomGradlePlugin.GSON.fromJson(fileReader, AssetIndex.class); } Stopwatch stopwatch = Stopwatch.createStarted(); @@ -118,75 +97,49 @@ public class MinecraftAssetsProvider { String filename = "objects" + File.separator + sha1.substring(0, 2) + File.separator + sha1; File file = new File(assets, filename); - String localFileChecksum = !file.exists() ? null : checksumInfos.computeIfAbsent(entry.getKey(), path -> { - try { - return Files.asByteSource(file).hash(Hashing.sha1()).toString(); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - }); - - if (LoomGradlePlugin.refreshDeps || localFileChecksum == null || !localFileChecksum.equals(sha1)) { - if (offline) { - if (file.exists()) { - project.getLogger().warn("Outdated asset " + entry.getKey()); - } else { - throw new GradleException("Asset " + entry.getKey() + " not found at " + file.getAbsolutePath()); - } + if (offline) { + if (file.exists()) { + project.getLogger().warn("Outdated asset " + entry.getKey()); } else { - toDownload++; - executor.execute(() -> { - ProgressLogger progressLogger; - - if (loggers.isEmpty()) { - //Create a new logger if we need one - progressLogger = ProgressLogger.getProgressFactory(project, MinecraftAssetsProvider.class.getName()); - progressLogger.start("Downloading assets...", "assets"); - } else { - // use a free logger if we can - progressLogger = loggers.pop(); - } - - String assetName = entry.getKey(); - int end = assetName.lastIndexOf("/") + 1; - - if (end > 0) { - assetName = assetName.substring(end); - } - - project.getLogger().debug(":downloading asset " + assetName); - progressLogger.progress(String.format("%-30.30s", assetName) + " - " + sha1); - - try { - DownloadUtil.downloadIfChanged(new URL(Constants.RESOURCES_BASE + sha1.substring(0, 2) + "/" + sha1), file, project.getLogger(), true); - } catch (IOException e) { - throw new RuntimeException("Failed to download: " + assetName, e); - } - - if (localFileChecksum == null) { - checksumInfos.put(entry.getKey(), sha1); - } - - //Give this logger back - loggers.add(progressLogger); - }); + throw new GradleException("Asset " + entry.getKey() + " not found at " + file.getAbsolutePath()); } + } else { + executor.execute(() -> { + ProgressLogger progressLogger; + + if (loggers.isEmpty()) { + //Create a new logger if we need one + progressLogger = ProgressLogger.getProgressFactory(project, MinecraftAssetsProvider.class.getName()); + progressLogger.start("Downloading assets...", "assets"); + } else { + // use a free logger if we can + progressLogger = loggers.pop(); + } + + String assetName = entry.getKey(); + int end = assetName.lastIndexOf("/") + 1; + + if (end > 0) { + assetName = assetName.substring(end); + } + + project.getLogger().debug(":downloading asset " + assetName); + progressLogger.progress(String.format("%-30.30s", assetName) + " - " + sha1); + + try { + HashedDownloadUtil.downloadIfInvalid(new URL(Constants.RESOURCES_BASE + sha1.substring(0, 2) + "/" + sha1), file, sha1, project.getLogger(), true); + } catch (IOException e) { + throw new RuntimeException("Failed to download: " + assetName, e); + } + + //Give this logger back + loggers.add(progressLogger); + }); } } project.getLogger().info("Took " + stopwatch.stop() + " to iterate " + parent.size() + " asset index."); - if (toDownload > 0) { - project.getLogger().lifecycle(":downloading " + toDownload + " asset" + (toDownload == 1 ? "" : "s") + "..."); - } - - checksumInfo.getParentFile().mkdirs(); - - try (FileWriter writer = new FileWriter(checksumInfo)) { - gson.toJson(checksumInfos, writer); - } - //Wait for the assets to all download executor.shutdown(); diff --git a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java index 8607dbe..3a5dba8 100644 --- a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java +++ b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java @@ -40,6 +40,6 @@ public class DownloadAssetsTask extends AbstractLoomTask { LoomGradleExtension extension = getExtension(); MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project); - MinecraftNativesProvider.provide(extension.getMinecraftProvider(), project); + MinecraftNativesProvider.provide(project); } }