Refactor native resolving.

This commit is contained in:
burningtnt 2025-09-13 18:01:03 +08:00
parent 8becd371a1
commit 592614698b
No known key found for this signature in database
GPG Key ID: 18A43F21F9ACE8C4
6 changed files with 101 additions and 129 deletions

View File

@ -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)) {

View File

@ -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<String> legacy;
private final String version;
private final Map<String, String> classifiers;
private final List<String> downloads;
private Config(List<String> legacy, String version, Map<String, String> classifiers, List<String> 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<String> recent,
@SerializedName("version_latest") String latest,
@SerializedName("classifiers") Map<String, String> classifiers,
@SerializedName("downloads") List<String> downloads
) {
private TerracottaNative of(String classifier) {
List<URI> 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<Path> 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<Path> LEGACY_PATH;
public static final ITerracottaProvider PROVIDER;
private static final Pattern LEGACY;
private static final List<String> 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<Path> 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<Path> 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<Path> 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;
}
}

View File

@ -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<URI> links;
private final FileDownloadTask.IntegrityCheck checking;
private final Path path;
private final List<Path> legacy;
public TerracottaNative(List<URI> links, Path path, List<Path> legacy, FileDownloadTask.IntegrityCheck checking) {
public TerracottaNative(List<URI> 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;
}

View File

@ -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 {

View File

@ -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

View File

@ -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",