From 592614698bf2371ff7334073135ed5c680a4d1da Mon Sep 17 00:00:00 2001 From: burningtnt Date: Sat, 13 Sep 2025 18:01:03 +0800 Subject: [PATCH] Refactor native resolving. --- .../hmcl/terracotta/TerracottaManager.java | 6 +- .../hmcl/terracotta/TerracottaMetadata.java | 175 ++++++++---------- .../hmcl/terracotta/TerracottaNative.java | 11 +- .../terracotta/provider/GeneralProvider.java | 11 +- .../terracotta/provider/MacOSProvider.java | 14 +- .../src/main/resources/assets/terracotta.json | 13 +- 6 files changed, 101 insertions(+), 129 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaManager.java index 1b0a50538..72b611b9a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaManager.java @@ -143,7 +143,11 @@ public final class TerracottaManager { Objects.requireNonNull(TerracottaMetadata.PROVIDER).install(progress) ).whenComplete(exception -> { if (exception == null) { - TerracottaMetadata.removeLegacy(); + try { + TerracottaMetadata.removeLegacyVersionFiles(); + } catch (IOException e) { + LOG.warning("Unable to remove legacy terracotta files.", e); + } TerracottaState.Launching launching = new TerracottaState.Launching(); if (compareAndSet(preparing, launching)) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaMetadata.java b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaMetadata.java index 4f0639c18..3ad261204 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaMetadata.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaMetadata.java @@ -1,11 +1,13 @@ package org.jackhuang.hmcl.terracotta; +import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.terracotta.provider.GeneralProvider; import org.jackhuang.hmcl.terracotta.provider.ITerracottaProvider; import org.jackhuang.hmcl.terracotta.provider.MacOSProvider; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jetbrains.annotations.NotNull; @@ -14,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.nio.file.DirectoryStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -24,6 +27,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.regex.Pattern; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -31,34 +35,18 @@ public final class TerracottaMetadata { private TerracottaMetadata() { } - public static final TerracottaNative WINDOWS_X86_64; - public static final TerracottaNative WINDOWS_ARM64; - - public static final TerracottaNative LINUX_X86_64; - public static final TerracottaNative LINUX_ARM64; - - public static final TerracottaNative MACOS_INSTALLER_X86_64; - public static final TerracottaNative MACOS_INSTALLER_ARM64; - public static final TerracottaNative MACOS_BIN_X86_64; - public static final TerracottaNative MACOS_BIN_ARM64; - - private static final class Config { - private final List legacy; - private final String version; - private final Map classifiers; - private final List downloads; - - private Config(List legacy, String version, Map classifiers, List downloads) { - this.legacy = legacy; - this.version = version; - this.classifiers = classifiers; - this.downloads = downloads; - } + private record Config( + @SerializedName("version_legacy") String legacy, + @SerializedName("version_recent") List recent, + @SerializedName("version_latest") String latest, + @SerializedName("classifiers") Map classifiers, + @SerializedName("downloads") List downloads + ) { private TerracottaNative of(String classifier) { List links = new ArrayList<>(this.downloads.size()); for (String download : this.downloads) { - links.add(URI.create(download.replace("${version}", this.version).replace("${classifier}", classifier))); + links.add(URI.create(download.replace("${version}", this.latest).replace("${classifier}", classifier))); } String hash = Objects.requireNonNull(this.classifiers.get(classifier), String.format("Classifier %s doesn't exist.", classifier)); @@ -67,25 +55,21 @@ public final class TerracottaMetadata { } hash = hash.substring("sha256:".length()); - List legacyPath = new ArrayList<>(this.legacy.size()); - for (String legacy : this.legacy) { - legacyPath.add(Metadata.DEPENDENCIES_DIRECTORY.resolve( - String.format("terracota/%s/terracotta-%s", legacy, classifier) - ).toAbsolutePath()); - } - return new TerracottaNative( Collections.unmodifiableList(links), Metadata.DEPENDENCIES_DIRECTORY.resolve( - String.format("terracota/%s/terracotta-%s", this.version, classifier) + String.format("terracotta/%s/terracotta-%s", this.latest, classifier) ).toAbsolutePath(), - Collections.unmodifiableList(legacyPath), new FileDownloadTask.IntegrityCheck("SHA-256", hash) ); } + } - private static final List LEGACY_PATH; + public static final ITerracottaProvider PROVIDER; + private static final Pattern LEGACY; + private static final List RECENT; + private static final String LATEST; static { Config config; @@ -95,78 +79,75 @@ public final class TerracottaMetadata { throw new ExceptionInInitializerError(e); } - WINDOWS_X86_64 = config.of("windows-x86_64.exe"); - WINDOWS_ARM64 = config.of("windows-arm64.exe"); - LINUX_X86_64 = config.of("linux-x86_64"); - LINUX_ARM64 = config.of("linux-arm64"); - MACOS_INSTALLER_X86_64 = config.of("macos-x86_64.pkg"); - MACOS_INSTALLER_ARM64 = config.of("macos-arm64.pkg"); - MACOS_BIN_X86_64 = config.of("macos-x86_64"); - MACOS_BIN_ARM64 = config.of("macos-arm64"); - - int LEGACY_BUT_NOT_OUT_OF_DATE_COUNT = 3; - if (config.legacy.size() < LEGACY_BUT_NOT_OUT_OF_DATE_COUNT) { - LEGACY_PATH = List.of(); - } else { - int count = config.legacy.size() - LEGACY_BUT_NOT_OUT_OF_DATE_COUNT; - - List legacyPath = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - legacyPath.add(Metadata.DEPENDENCIES_DIRECTORY.resolve(String.format("terracota/%s", config.legacy.get(i))).toAbsolutePath()); - } - LEGACY_PATH = legacyPath; - } + PROVIDER = locateProvider(config); + LEGACY = Pattern.compile(config.legacy); + RECENT = config.recent; + LATEST = config.latest; } - public static void removeLegacy() { - for (Path path : LEGACY_PATH) { - if (!Files.exists(path)) { - continue; - } - - try { - Files.walkFileTree(path, new SimpleFileVisitor<>() { - @Override - public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException { - Files.delete(file); - return super.visitFile(file, attrs); - } - - @Override - public @NotNull FileVisitResult postVisitDirectory(@NotNull Path dir, @Nullable IOException exc) throws IOException { - Files.delete(dir); - return super.postVisitDirectory(dir, exc); - } - }); - } catch (IOException e) { - LOG.warning(String.format("Unable to delete legacy version: %s", path), e); - } - } - } - - public static final ITerracottaProvider PROVIDER = locateProvider(); - - private static ITerracottaProvider locateProvider() { - if (Architecture.SYSTEM_ARCH != Architecture.X86_64 && Architecture.SYSTEM_ARCH != Architecture.ARM64) { + private static ITerracottaProvider locateProvider(Config config) { + String architecture = switch (Architecture.SYSTEM_ARCH) { + case X86_64 -> "x86_64"; + case ARM64 -> "arm64"; + default -> null; + }; + if (architecture == null) { return null; } - switch (OperatingSystem.CURRENT_OS) { - case WINDOWS: { + return switch (OperatingSystem.CURRENT_OS) { + case WINDOWS -> { if (OperatingSystem.isWindows81OrLater()) { - return new GeneralProvider(); + yield new GeneralProvider(config.of(String.format("windows-%s.exe", architecture))); } - return null; + yield null; } - case LINUX: { - return new GeneralProvider(); - } - case MACOS: { - return new MacOSProvider(); - } - default: { - return null; + case LINUX -> new GeneralProvider(config.of(String.format("linux-%s", architecture))); + case MACOS -> + new MacOSProvider(config.of(String.format("macos-%s.pkg", architecture)), config.of(String.format("macos-%s", architecture))); + default -> null; + }; + } + + public static void removeLegacyVersionFiles() throws IOException { + try (DirectoryStream terracotta = Files.newDirectoryStream(Metadata.DEPENDENCIES_DIRECTORY.resolve("terracotta").toAbsolutePath())) { + for (Path path : terracotta) { + String name = FileUtils.getName(path); + if (LATEST.equals(name) || RECENT.contains(name) || !LEGACY.matcher(name).matches()) { + continue; + } + + try { + Files.walkFileTree(path, new SimpleFileVisitor<>() { + @Override + public @NotNull FileVisitResult visitFile(@NotNull Path file, @NotNull BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return super.visitFile(file, attrs); + } + + @Override + public @NotNull FileVisitResult postVisitDirectory(@NotNull Path dir, @Nullable IOException exc) throws IOException { + Files.delete(dir); + return super.postVisitDirectory(dir, exc); + } + }); + } catch (IOException e) { + LOG.warning(String.format("Unable to remove legacy terracotta files: %s", path), e); + } } } } + + public static boolean hasLegacyVersionFiles() throws IOException { + try (DirectoryStream terracotta = Files.newDirectoryStream(Metadata.DEPENDENCIES_DIRECTORY.resolve("terracotta").toAbsolutePath())) { + for (Path path : terracotta) { + String name = FileUtils.getName(path); + if (!LATEST.equals(name) && (RECENT.contains(name) || LEGACY.matcher(name).matches())) { + return true; + } + } + } + + return false; + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java index 316160416..569e8a1ec 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/TerracottaNative.java @@ -4,6 +4,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.terracotta.provider.ITerracottaProvider; import org.jackhuang.hmcl.util.DigestUtils; +import org.jackhuang.hmcl.util.logging.Logger; import java.io.IOException; import java.io.InputStream; @@ -16,13 +17,11 @@ public final class TerracottaNative { private final List links; private final FileDownloadTask.IntegrityCheck checking; private final Path path; - private final List legacy; - public TerracottaNative(List links, Path path, List legacy, FileDownloadTask.IntegrityCheck checking) { + public TerracottaNative(List links, Path path, FileDownloadTask.IntegrityCheck checking) { this.links = links; this.path = path; this.checking = checking; - this.legacy = legacy; } public Path getPath() { @@ -44,10 +43,12 @@ public final class TerracottaNative { } } - for (Path legacy : legacy) { - if (Files.exists(legacy)) { + try { + if (TerracottaMetadata.hasLegacyVersionFiles()) { return ITerracottaProvider.Status.LEGACY_VERSION; } + } catch (IOException e) { + Logger.LOG.warning("Cannot determine whether legacy versions exist."); } return ITerracottaProvider.Status.NOT_EXIST; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/GeneralProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/GeneralProvider.java index e05a1252e..6f5cdfa95 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/GeneralProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/GeneralProvider.java @@ -16,12 +16,11 @@ import java.util.Map; import java.util.Set; public final class GeneralProvider implements ITerracottaProvider { - private final TerracottaNative target = Map.of( - Platform.WINDOWS_X86_64, TerracottaMetadata.WINDOWS_X86_64, - Platform.WINDOWS_ARM64, TerracottaMetadata.WINDOWS_ARM64, - Platform.LINUX_X86_64, TerracottaMetadata.LINUX_X86_64, - Platform.LINUX_ARM64, TerracottaMetadata.LINUX_ARM64 - ).get(Platform.SYSTEM_PLATFORM); + private final TerracottaNative target; + + public GeneralProvider(TerracottaNative target) { + this.target = target; + } @Override public Status status() throws IOException { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java index 90deee5ba..1b36081e6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/terracotta/provider/MacOSProvider.java @@ -20,17 +20,9 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; public final class MacOSProvider implements ITerracottaProvider { public final TerracottaNative installer, binary; - { - if (Architecture.SYSTEM_ARCH == Architecture.X86_64) { - installer = TerracottaMetadata.MACOS_INSTALLER_X86_64; - binary = TerracottaMetadata.MACOS_BIN_X86_64; - } else if (Architecture.SYSTEM_ARCH == Architecture.ARM64) { - installer = TerracottaMetadata.MACOS_INSTALLER_ARM64; - binary = TerracottaMetadata.MACOS_BIN_ARM64; - } else { - installer = null; - binary = null; - } + public MacOSProvider(TerracottaNative installer, TerracottaNative binary) { + this.installer = installer; + this.binary = binary; } @Override diff --git a/HMCL/src/main/resources/assets/terracotta.json b/HMCL/src/main/resources/assets/terracotta.json index c1c32d27f..62e2c0d90 100644 --- a/HMCL/src/main/resources/assets/terracotta.json +++ b/HMCL/src/main/resources/assets/terracotta.json @@ -1,17 +1,12 @@ { - "legacy": [ - "0.3.8-rc.1", - "0.3.8-rc.2", - "0.3.8-rc.3", - "0.3.8-rc.4", - "0.3.8-rc.5", - "0.3.9-rc.1", - "0.3.9-rc.2", + "version_legacy": "0\\.3\\.[89]-rc.[0-9]", + "version_recent": [ "0.3.9-rc.3", "0.3.9-rc.4", "0.3.9-rc.5" ], - "version": "0.3.9-rc.6", + "version_latest": "0.3.9-rc.6", + "classifiers": { "linux-arm64": "sha256:a47b4ae837d71206631b59fc699d02cad984d3e2eb565a4598ff6affb75d5684", "linux-x86_64": "sha256:949b8fa9c31e03b3301264642d844fecd1cabbdca912fdacd20d1e8f800ed4f9",