From 3c7d6fd87f56d9b0186d3b84279e1e66478cadd7 Mon Sep 17 00:00:00 2001 From: asie Date: Wed, 24 Jul 2019 23:04:45 +0200 Subject: [PATCH] caching/performance improvements --- build.gradle | 2 +- .../loom/providers/MappingsCache.java | 65 +++++++++++++++ .../loom/providers/MappingsProvider.java | 12 +-- .../loom/providers/MinecraftProvider.java | 16 ++-- .../java/net/fabricmc/loom/util/Checksum.java | 5 ++ .../fabricmc/loom/util/StaticPathWatcher.java | 83 +++++++++++++++++++ 6 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/providers/MappingsCache.java create mode 100644 src/main/java/net/fabricmc/loom/util/StaticPathWatcher.java diff --git a/build.gradle b/build.gradle index 6be4362..0bdf0ad 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ targetCompatibility = 1.8 group = 'net.fabricmc' archivesBaseName = project.name -version = '0.2.290-SNAPSHOT' +version = '0.2.5-SNAPSHOT' def build = "local" def ENV = System.getenv() diff --git a/src/main/java/net/fabricmc/loom/providers/MappingsCache.java b/src/main/java/net/fabricmc/loom/providers/MappingsCache.java new file mode 100644 index 0000000..5a3281c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/providers/MappingsCache.java @@ -0,0 +1,65 @@ +/* + * 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.providers; + +import net.fabricmc.loom.util.StaticPathWatcher; +import net.fabricmc.mappings.Mappings; +import org.gradle.api.logging.Logging; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.nio.file.*; +import java.util.HashMap; +import java.util.Map; + +public final class MappingsCache { + public static final MappingsCache INSTANCE = new MappingsCache(); + + private final Map> mappingsCache = new HashMap<>(); + + public Mappings get(Path mappingsPath) { + mappingsPath = mappingsPath.toAbsolutePath(); + if (StaticPathWatcher.INSTANCE.hasFileChanged(mappingsPath)) { + mappingsCache.remove(mappingsPath); + } + + SoftReference ref = mappingsCache.get(mappingsPath); + if (ref != null && ref.get() != null) { + return ref.get(); + } else { + try (InputStream stream = Files.newInputStream(mappingsPath)) { + Logging.getLogger(MappingsCache.class).lifecycle("CACHE MISS! OWO " + mappingsPath); + Mappings mappings = net.fabricmc.mappings.MappingsProvider.readTinyMappings(stream, false); + ref = new SoftReference<>(mappings); + mappingsCache.put(mappingsPath, ref); + return mappings; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/main/java/net/fabricmc/loom/providers/MappingsProvider.java b/src/main/java/net/fabricmc/loom/providers/MappingsProvider.java index 6f43f56..35ee05e 100644 --- a/src/main/java/net/fabricmc/loom/providers/MappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/providers/MappingsProvider.java @@ -56,18 +56,8 @@ public class MappingsProvider extends DependencyProvider { public File MAPPINGS_TINY; public File MAPPINGS_MIXIN_EXPORT; - private SoftReference mappings; - public Mappings getMappings() throws IOException { - if (mappings == null || mappings.get() == null) { - try (FileInputStream stream = new FileInputStream(MAPPINGS_TINY)) { - mappings = new SoftReference<>( - net.fabricmc.mappings.MappingsProvider.readTinyMappings(stream, false) - ); - } - } - - return mappings.get(); + return MappingsCache.INSTANCE.get(MAPPINGS_TINY.toPath()); } @Override diff --git a/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java index 4a7d986..a688fb1 100644 --- a/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java @@ -124,8 +124,10 @@ public class MinecraftProvider extends DependencyProvider { throw new GradleException("Version manifests not found at " + manifests.getAbsolutePath()); } } else { - project.getLogger().debug("Downloading version manifests"); - DownloadUtil.downloadIfChanged(new URL("https://launchermeta.mojang.com/mc/game/version_manifest.json"), manifests, project.getLogger()); + if (StaticPathWatcher.INSTANCE.hasFileChanged(manifests.toPath())) { + project.getLogger().debug("Downloading version manifests"); + DownloadUtil.downloadIfChanged(new URL("https://launchermeta.mojang.com/mc/game/version_manifest.json"), manifests, project.getLogger()); + } } String versionManifest = Files.asCharSource(manifests, StandardCharsets.UTF_8).read(); @@ -142,8 +144,10 @@ public class MinecraftProvider extends DependencyProvider { throw new GradleException("Minecraft " + minecraftVersion + " manifest not found at " + MINECRAFT_JSON.getAbsolutePath()); } } else { - project.getLogger().debug("Downloading Minecraft {} manifest", minecraftVersion); - DownloadUtil.downloadIfChanged(new URL(optionalVersion.get().url), MINECRAFT_JSON, project.getLogger()); + if (StaticPathWatcher.INSTANCE.hasFileChanged(MINECRAFT_JSON.toPath())) { + project.getLogger().debug("Downloading Minecraft {} manifest", minecraftVersion); + DownloadUtil.downloadIfChanged(new URL(optionalVersion.get().url), MINECRAFT_JSON, project.getLogger()); + } } } else { throw new RuntimeException("Failed to find minecraft version: " + minecraftVersion); @@ -152,12 +156,12 @@ public class MinecraftProvider extends DependencyProvider { } private void downloadJars(Logger logger) throws IOException { - if (!MINECRAFT_CLIENT_JAR.exists() || !Checksum.equals(MINECRAFT_CLIENT_JAR, versionInfo.downloads.get("client").sha1)) { + if (!MINECRAFT_CLIENT_JAR.exists() || (!Checksum.equals(MINECRAFT_CLIENT_JAR, versionInfo.downloads.get("client").sha1) && StaticPathWatcher.INSTANCE.hasFileChanged(MINECRAFT_CLIENT_JAR.toPath()))) { logger.debug("Downloading Minecraft {} client jar", minecraftVersion); DownloadUtil.downloadIfChanged(new URL(versionInfo.downloads.get("client").url), MINECRAFT_CLIENT_JAR, logger); } - if (!MINECRAFT_SERVER_JAR.exists() || !Checksum.equals(MINECRAFT_SERVER_JAR, versionInfo.downloads.get("server").sha1)) { + if (!MINECRAFT_SERVER_JAR.exists() || (!Checksum.equals(MINECRAFT_SERVER_JAR, versionInfo.downloads.get("server").sha1) && StaticPathWatcher.INSTANCE.hasFileChanged(MINECRAFT_SERVER_JAR.toPath()))) { logger.debug("Downloading Minecraft {} server jar", minecraftVersion); DownloadUtil.downloadIfChanged(new URL(versionInfo.downloads.get("server").url), MINECRAFT_SERVER_JAR, logger); } diff --git a/src/main/java/net/fabricmc/loom/util/Checksum.java b/src/main/java/net/fabricmc/loom/util/Checksum.java index 0de2ef5..5ad9d0c 100644 --- a/src/main/java/net/fabricmc/loom/util/Checksum.java +++ b/src/main/java/net/fabricmc/loom/util/Checksum.java @@ -27,11 +27,15 @@ package net.fabricmc.loom.util; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; import com.google.common.io.Files; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; import java.io.File; import java.io.IOException; public class Checksum { + private static final Logger log = Logging.getLogger(Checksum.class); + public static boolean equals(File file, String checksum) { if (file == null) { return false; @@ -43,6 +47,7 @@ public class Checksum { for (Byte hashBytes : hash.asBytes()) { builder.append(Integer.toString((hashBytes & 0xFF) + 0x100, 16).substring(1)); } + log.debug("Checksum check: '" + builder.toString() + "' == '" + checksum + "'?"); return builder.toString().equals(checksum); } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/net/fabricmc/loom/util/StaticPathWatcher.java b/src/main/java/net/fabricmc/loom/util/StaticPathWatcher.java new file mode 100644 index 0000000..fd8a27f --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/StaticPathWatcher.java @@ -0,0 +1,83 @@ +/* + * 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.util; + +import net.fabricmc.mappings.Mappings; +import org.gradle.api.logging.Logging; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.nio.file.*; +import java.util.HashMap; +import java.util.Map; + +public final class StaticPathWatcher { + public static final StaticPathWatcher INSTANCE = new StaticPathWatcher(); + + private final Map changeCache = new HashMap<>(); + private final WatchService service; + private final Map pathsObserved = new HashMap<>(); + + private StaticPathWatcher() { + try { + service = FileSystems.getDefault().newWatchService(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public boolean hasFileChanged(Path filePath) { + WatchKey key; + while ((key = service.poll()) != null) { + for (WatchEvent event : key.pollEvents()) { + Object ctx = event.context(); + if (ctx instanceof Path) { + changeCache.put(((Path) ctx).toAbsolutePath(), true); + } + } + } + + filePath = filePath.toAbsolutePath(); + Path parentPath = filePath.getParent(); + if (changeCache.containsKey(filePath)) { + return true; + } else { + if (!pathsObserved.containsKey(parentPath)) { + try { + pathsObserved.put(parentPath, parentPath.register( + service, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE + )); + + return true; + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + return false; + } + } + } +}