From 6a497df0d1cd873698100707a25f7272d344416e Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 16 Sep 2025 21:22:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=20org.jackhuang.hmcl.downloa?= =?UTF-8?q?d.game=20(#4495)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 将 `java.io.File` 迁移至 NIO; 2. 将部分类迁移至 record。 --- .../jackhuang/hmcl/game/LauncherHelper.java | 2 +- .../hmcl/download/DefaultCacheRepository.java | 6 +- .../hmcl/download/game/GameLibrariesTask.java | 22 +++---- .../game/GameRemoteLatestVersions.java | 30 ++------- .../hmcl/download/game/GameRemoteVersion.java | 19 +++--- .../download/game/GameRemoteVersionInfo.java | 61 +++---------------- .../download/game/GameRemoteVersions.java | 34 ++--------- .../game/GameVerificationFixTask.java | 8 +-- .../hmcl/download/game/GameVersionList.java | 24 ++++---- .../download/game/LibraryDownloadTask.java | 21 +++---- 10 files changed, 65 insertions(+), 162 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 32f4e82a2..5b9cd51d7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -173,7 +173,7 @@ public final class LauncherHelper { String agent = file.getAbsolutePath() + "=" + renderer.name().toLowerCase(Locale.ROOT); if (GameLibrariesTask.shouldDownloadLibrary(repository, version.get(), lib, integrityCheck)) { - return new LibraryDownloadTask(dependencyManager, file, lib) + return new LibraryDownloadTask(dependencyManager, file.toPath(), lib) .thenRunAsync(() -> javaAgents.add(agent)); } else { javaAgents.add(agent); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultCacheRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultCacheRepository.java index d044305fa..88c3e1bd9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultCacheRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultCacheRepository.java @@ -104,7 +104,7 @@ public class DefaultCacheRepository extends CacheRepository { if (hash.equalsIgnoreCase(checksum)) cacheLibrary(library, jar, false); } else if (library.getChecksums() != null && !library.getChecksums().isEmpty()) { - if (LibraryDownloadTask.checksumValid(jar.toFile(), library.getChecksums())) + if (LibraryDownloadTask.checksumValid(jar, library.getChecksums())) cacheLibrary(library, jar, true); } else { // or we will not cache the library @@ -139,7 +139,7 @@ public class DefaultCacheRepository extends CacheRepository { if (fileExists(SHA1, libIndex.getHash())) { Path file = getFile(SHA1, libIndex.getHash()); if (libIndex.getType().equalsIgnoreCase(LibraryIndex.TYPE_FORGE)) { - if (LibraryDownloadTask.checksumValid(file.toFile(), library.getChecksums())) + if (LibraryDownloadTask.checksumValid(file, library.getChecksums())) return Optional.of(file); } } @@ -157,7 +157,7 @@ public class DefaultCacheRepository extends CacheRepository { if (hash.equalsIgnoreCase(checksum)) return Optional.of(restore(jar, () -> cacheLibrary(library, jar, false))); } else if (library.getChecksums() != null && !library.getChecksums().isEmpty()) { - if (LibraryDownloadTask.checksumValid(jar.toFile(), library.getChecksums())) + if (LibraryDownloadTask.checksumValid(jar, library.getChecksums())) return Optional.of(restore(jar, () -> cacheLibrary(library, jar, true))); } else { return Optional.of(jar); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java index 084e5e7be..a27fb5b31 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java @@ -32,7 +32,6 @@ import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; @@ -89,30 +88,29 @@ public final class GameLibrariesTask extends Task { } public static boolean shouldDownloadLibrary(GameRepository gameRepository, Version version, Library library, boolean integrityCheck) { - File file = gameRepository.getLibraryFile(version, library); - Path jar = file.toPath(); - if (!file.isFile()) return true; + Path file = gameRepository.getLibraryFile(version, library).toPath(); + if (!Files.isRegularFile(file)) return true; if (!integrityCheck) { return false; } try { - if (!library.getDownload().validateChecksum(jar, true)) { + if (!library.getDownload().validateChecksum(file, true)) { return true; } if (library.getChecksums() != null && !library.getChecksums().isEmpty() && !LibraryDownloadTask.checksumValid(file, library.getChecksums())) { return true; } - if (FileUtils.getExtension(file.getName()).equals("jar")) { + if (FileUtils.getExtension(file).equals("jar")) { try { - FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar); + FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(file, file); } catch (IOException ignored) { // the Jar file is malformed, so re-download it. return true; } } } catch (IOException e) { - LOG.warning("Unable to calc hash value of file " + jar, e); + LOG.warning("Unable to calc hash value of file " + file, e); } return false; @@ -160,13 +158,13 @@ public final class GameLibrariesTask extends Task { } } - File file = gameRepository.getLibraryFile(version, library); - if ("optifine".equals(library.getGroupId()) && file.exists() && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(version)).compareTo("1.20.4") == 0) { + Path file = gameRepository.getLibraryFile(version, library).toPath(); + if ("optifine".equals(library.getGroupId()) && Files.exists(file) && GameVersionNumber.asGameVersion(gameRepository.getGameVersion(version)).compareTo("1.20.4") == 0) { String forgeVersion = LibraryAnalyzer.analyze(version, "1.20.4") .getVersion(LibraryAnalyzer.LibraryType.FORGE) .orElse(null); if (forgeVersion != null && LibraryAnalyzer.FORGE_OPTIFINE_BROKEN_RANGE.contains(VersionNumber.asVersion(forgeVersion))) { - try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(file.toPath())) { + try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(file)) { Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml")); } catch (IOException e) { throw new IOException("Cannot fix optifine", e); @@ -176,7 +174,7 @@ public final class GameLibrariesTask extends Task { if (shouldDownloadLibrary(gameRepository, version, library, integrityCheck) && (library.hasDownloadURL() || !"optifine".equals(library.getGroupId()))) { dependencies.add(new LibraryDownloadTask(dependencyManager, file, library).withCounter("hmcl.install.libraries")); } else { - dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath()); + dependencyManager.getCacheRepository().tryCacheLibrary(library, file); } updateProgress(++progress, libraries.size()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteLatestVersions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteLatestVersions.java index 70ab693a4..3e72b1785 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteLatestVersions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteLatestVersions.java @@ -19,34 +19,14 @@ package org.jackhuang.hmcl.download.game; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.gson.JsonSerializable; /** - * * @author huangyuhui */ @Immutable -public final class GameRemoteLatestVersions { - - @SerializedName("snapshot") - private final String snapshot; - - @SerializedName("release") - private final String release; - - public GameRemoteLatestVersions() { - this(null, null); - } - - public GameRemoteLatestVersions(String snapshot, String release) { - this.snapshot = snapshot; - this.release = release; - } - - public String getRelease() { - return release; - } - - public String getSnapshot() { - return snapshot; - } +@JsonSerializable +public record GameRemoteLatestVersions( + @SerializedName("snapshot") String snapshot, + @SerializedName("release") String release) { } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java index cb72b90fe..e56e694c2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersion.java @@ -68,17 +68,12 @@ public final class GameRemoteVersion extends RemoteVersion { private static Type getReleaseType(ReleaseType type) { if (type == null) return Type.UNCATEGORIZED; - switch (type) { - case RELEASE: - return Type.RELEASE; - case SNAPSHOT: - return Type.SNAPSHOT; - case UNKNOWN: - return Type.UNCATEGORIZED; - case PENDING: - return Type.PENDING; - default: - return Type.OLD; - } + return switch (type) { + case RELEASE -> Type.RELEASE; + case SNAPSHOT -> Type.SNAPSHOT; + case UNKNOWN -> Type.UNCATEGORIZED; + case PENDING -> Type.PENDING; + default -> Type.OLD; + }; } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersionInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersionInfo.java index 430ddec37..5825d33a8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersionInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersionInfo.java @@ -20,8 +20,8 @@ package org.jackhuang.hmcl.download.game; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.game.ReleaseType; -import org.jackhuang.hmcl.util.Constants; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.Validation; import java.time.Instant; @@ -30,59 +30,14 @@ import java.time.Instant; * * @author huangyuhui */ -public final class GameRemoteVersionInfo implements Validation { +@JsonSerializable +public record GameRemoteVersionInfo( + @SerializedName("id") String gameVersion, + @SerializedName("time") Instant time, + @SerializedName("releaseTime") Instant releaseTime, + @SerializedName("type") ReleaseType type, + @SerializedName("url") String url) implements Validation { - @SerializedName("id") - private final String gameVersion; - - @SerializedName("time") - private final Instant time; - - @SerializedName("releaseTime") - private final Instant releaseTime; - - @SerializedName("type") - private final ReleaseType type; - - @SerializedName("url") - private final String url; - - public GameRemoteVersionInfo() { - this("", Instant.now(), Instant.now(), ReleaseType.UNKNOWN); - } - - public GameRemoteVersionInfo(String gameVersion, Instant time, Instant releaseTime, ReleaseType type) { - this(gameVersion, time, releaseTime, type, Constants.DEFAULT_LIBRARY_URL + gameVersion + "/" + gameVersion + ".json"); - } - - public GameRemoteVersionInfo(String gameVersion, Instant time, Instant releaseTime, ReleaseType type, String url) { - this.gameVersion = gameVersion; - this.time = time; - this.releaseTime = releaseTime; - this.type = type; - this.url = url; - } - - public String getGameVersion() { - return gameVersion; - } - - public Instant getTime() { - return time; - } - - public Instant getReleaseTime() { - return releaseTime; - } - - public ReleaseType getType() { - return type; - } - - public String getUrl() { - return url; - } - @Override public void validate() throws JsonParseException { if (StringUtils.isBlank(gameVersion)) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java index 6762fb111..4f035b24c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameRemoteVersions.java @@ -20,9 +20,9 @@ package org.jackhuang.hmcl.download.game; import com.google.gson.JsonParseException; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.Validation; -import java.util.Collections; import java.util.List; /** @@ -30,34 +30,10 @@ import java.util.List; * @author huangyuhui */ @Immutable -public final class GameRemoteVersions implements Validation { - - @SerializedName("versions") - private final List versions; - - @SerializedName("latest") - private final GameRemoteLatestVersions latest; - - /** - * No-arg constructor for Gson. - */ - @SuppressWarnings("unused") - public GameRemoteVersions() { - this(Collections.emptyList(), null); - } - - public GameRemoteVersions(List versions, GameRemoteLatestVersions latest) { - this.versions = versions; - this.latest = latest; - } - - public GameRemoteLatestVersions getLatest() { - return latest; - } - - public List getVersions() { - return versions; - } +@JsonSerializable +public record GameRemoteVersions( + @SerializedName("versions") List versions, + @SerializedName("latest") GameRemoteLatestVersions latest) implements Validation { @Override public void validate() throws JsonParseException { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java index 1224378f5..c76b8b754 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVerificationFixTask.java @@ -24,11 +24,11 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; -import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -62,11 +62,11 @@ public final class GameVerificationFixTask extends Task { @Override public void execute() throws IOException { - File jar = dependencyManager.getGameRepository().getVersionJar(version); + Path jar = dependencyManager.getGameRepository().getVersionJar(version).toPath(); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version, gameVersion); - if (jar.exists() && GameVersionNumber.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) { - try (FileSystem fs = CompressingUtils.createWritableZipFileSystem(jar.toPath(), StandardCharsets.UTF_8)) { + if (Files.exists(jar) && GameVersionNumber.compare(gameVersion, "1.6") < 0 && analyzer.has(LibraryAnalyzer.LibraryType.FORGE)) { + try (FileSystem fs = CompressingUtils.createWritableZipFileSystem(jar, StandardCharsets.UTF_8)) { Files.deleteIfExists(fs.getPath("META-INF/MOJANG_C.DSA")); Files.deleteIfExists(fs.getPath("META-INF/MOJANG_C.SF")); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java index 561e8eb94..97690772c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameVersionList.java @@ -70,21 +70,21 @@ public final class GameVersionList extends VersionList { versions.clear(); if (unlistedVersions != null) { - for (GameRemoteVersionInfo unlistedVersion : unlistedVersions.getVersions()) { - versions.put(unlistedVersion.getGameVersion(), new GameRemoteVersion( - unlistedVersion.getGameVersion(), - unlistedVersion.getGameVersion(), - Collections.singletonList(unlistedVersion.getUrl()), - unlistedVersion.getType(), unlistedVersion.getReleaseTime())); + for (GameRemoteVersionInfo unlistedVersion : unlistedVersions.versions()) { + versions.put(unlistedVersion.gameVersion(), new GameRemoteVersion( + unlistedVersion.gameVersion(), + unlistedVersion.gameVersion(), + Collections.singletonList(unlistedVersion.url()), + unlistedVersion.type(), unlistedVersion.releaseTime())); } } - for (GameRemoteVersionInfo remoteVersion : root.getVersions()) { - versions.put(remoteVersion.getGameVersion(), new GameRemoteVersion( - remoteVersion.getGameVersion(), - remoteVersion.getGameVersion(), - Collections.singletonList(remoteVersion.getUrl()), - remoteVersion.getType(), remoteVersion.getReleaseTime())); + for (GameRemoteVersionInfo remoteVersion : root.versions()) { + versions.put(remoteVersion.gameVersion(), new GameRemoteVersion( + remoteVersion.gameVersion(), + remoteVersion.gameVersion(), + Collections.singletonList(remoteVersion.url()), + remoteVersion.type(), remoteVersion.releaseTime())); } } finally { lock.writeLock().unlock(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java index f6fafa174..8c735e2b0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java @@ -28,7 +28,6 @@ import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.io.FileUtils; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -43,7 +42,7 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class LibraryDownloadTask extends Task { private FileDownloadTask task; - protected final File jar; + protected final Path jar; protected final DefaultCacheRepository cacheRepository; protected final AbstractDependencyManager dependencyManager; protected final Library library; @@ -51,7 +50,7 @@ public class LibraryDownloadTask extends Task { private final Library originalLibrary; private boolean cached = false; - public LibraryDownloadTask(AbstractDependencyManager dependencyManager, File file, Library library) { + public LibraryDownloadTask(AbstractDependencyManager dependencyManager, Path file, Library library) { this.dependencyManager = dependencyManager; this.originalLibrary = library; @@ -105,7 +104,7 @@ public class LibraryDownloadTask extends Task { Optional libPath = cacheRepository.getLibrary(originalLibrary); if (libPath.isPresent()) { try { - FileUtils.copyFile(libPath.get(), jar.toPath()); + FileUtils.copyFile(libPath.get(), jar); cached = true; return; } catch (IOException e) { @@ -117,7 +116,7 @@ public class LibraryDownloadTask extends Task { List uris = dependencyManager.getDownloadProvider().injectURLWithCandidates(url); - task = new FileDownloadTask(uris, jar.toPath(), + task = new FileDownloadTask(uris, jar, library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null); task.setCacheRepository(cacheRepository); task.setCaching(true); @@ -133,26 +132,26 @@ public class LibraryDownloadTask extends Task { public void postExecute() throws Exception { if (!cached) { try { - cacheRepository.cacheLibrary(library, jar.toPath(), false); + cacheRepository.cacheLibrary(library, jar, false); } catch (IOException e) { LOG.warning("Failed to cache downloaded library " + library, e); } } } - public static boolean checksumValid(File libPath, List checksums) { + public static boolean checksumValid(Path libPath, List checksums) { try { if (checksums == null || checksums.isEmpty()) { return true; } - byte[] fileData = Files.readAllBytes(libPath.toPath()); + byte[] fileData = Files.readAllBytes(libPath); boolean valid = checksums.contains(DigestUtils.digestToString("SHA-1", fileData)); - if (!valid && libPath.getName().endsWith(".jar")) { + if (!valid && FileUtils.getName(libPath).endsWith(".jar")) { valid = validateJar(fileData, checksums); } return valid; } catch (IOException e) { - e.printStackTrace(); + LOG.warning("Failed to validate " + libPath, e); } return false; } @@ -177,7 +176,7 @@ public class LibraryDownloadTask extends Task { boolean failed = !checksums.contains(files.get("checksums.sha1")); if (!failed) { for (String hash : hashes) { - if ((!hash.trim().equals("")) && (hash.contains(" "))) { + if (!hash.trim().isEmpty() && hash.contains(" ")) { String[] e = hash.split(" "); String validChecksum = e[0]; String target = hash.substring(validChecksum.length() + 1);