清理 org.jackhuang.hmcl.download.game (#4495)

1. 将 `java.io.File` 迁移至 NIO;
2. 将部分类迁移至 record。
This commit is contained in:
Glavo 2025-09-16 21:22:02 +08:00 committed by GitHub
parent fc1cce5e5a
commit 6a497df0d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 65 additions and 162 deletions

View File

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

View File

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

View File

@ -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<Void> {
}
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<Void> {
}
}
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<Void> {
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());

View File

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

View File

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

View File

@ -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,58 +30,13 @@ import java.time.Instant;
*
* @author huangyuhui
*/
public final class GameRemoteVersionInfo 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;
}
@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 {
@Override
public void validate() throws JsonParseException {

View File

@ -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<GameRemoteVersionInfo> versions;
@SerializedName("latest")
private final GameRemoteLatestVersions latest;
/**
* No-arg constructor for Gson.
*/
@SuppressWarnings("unused")
public GameRemoteVersions() {
this(Collections.emptyList(), null);
}
public GameRemoteVersions(List<GameRemoteVersionInfo> versions, GameRemoteLatestVersions latest) {
this.versions = versions;
this.latest = latest;
}
public GameRemoteLatestVersions getLatest() {
return latest;
}
public List<GameRemoteVersionInfo> getVersions() {
return versions;
}
@JsonSerializable
public record GameRemoteVersions(
@SerializedName("versions") List<GameRemoteVersionInfo> versions,
@SerializedName("latest") GameRemoteLatestVersions latest) implements Validation {
@Override
public void validate() throws JsonParseException {

View File

@ -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<Void> {
@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"));
}

View File

@ -70,21 +70,21 @@ public final class GameVersionList extends VersionList<GameRemoteVersion> {
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();

View File

@ -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<Void> {
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<Void> {
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<Void> {
Optional<Path> 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<Void> {
List<URI> 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<Void> {
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<String> checksums) {
public static boolean checksumValid(Path libPath, List<String> 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<Void> {
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);