add: redownload corrupt files when game crashed

This commit is contained in:
huanghongxun
2020-02-20 00:02:56 +08:00
parent cd9180c69a
commit 91682aeafa
16 changed files with 122 additions and 76 deletions

View File

@@ -28,6 +28,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
@@ -221,6 +222,20 @@ public class HMCLGameRepository extends DefaultGameRepository {
beingModpackVersions.remove(id); beingModpackVersions.remove(id);
} }
public void markVersionLaunchedAbnormally(String id) {
try {
Files.createFile(getVersionRoot(id).toPath().resolve(".abnormal"));
} catch (IOException ignored) {
}
}
public boolean unmarkVersionLaunchedAbnormally(String id) {
File file = new File(getVersionRoot(id), ".abnormal");
boolean result = file.isFile();
file.delete();
return result;
}
private static final Gson GSON = new GsonBuilder() private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting() .setPrettyPrinting()
.create(); .create();

View File

@@ -135,7 +135,7 @@ public final class LauncherHelper {
if (setting.isNotCheckGame()) if (setting.isNotCheckGame())
return null; return null;
else else
return dependencyManager.checkGameCompletionAsync(version); return dependencyManager.checkGameCompletionAsync(version, repository.unmarkVersionLaunchedAbnormally(selectedVersion));
}), Task.composeAsync(null, () -> { }), Task.composeAsync(null, () -> {
try { try {
ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion)); ModpackConfiguration<?> configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion));
@@ -168,7 +168,7 @@ public final class LauncherHelper {
setting.toLaunchOptions(profile.getGameDir(), !setting.isNotCheckJVM()), setting.toLaunchOptions(profile.getGameDir(), !setting.isNotCheckJVM()),
launcherVisibility == LauncherVisibility.CLOSE launcherVisibility == LauncherVisibility.CLOSE
? null // Unnecessary to start listening to game process output when close launcher immediately after game launched. ? null // Unnecessary to start listening to game process output when close launcher immediately after game launched.
: new HMCLProcessListener(authInfo, gameVersion.isPresent()) : new HMCLProcessListener(repository, selectedVersion, authInfo, gameVersion.isPresent())
); );
}).thenComposeAsync(launcher -> { // launcher is prev task's result }).thenComposeAsync(launcher -> { // launcher is prev task's result
if (scriptFile == null) { if (scriptFile == null) {
@@ -209,12 +209,12 @@ public final class LauncherHelper {
@Override @Override
public void onStop(boolean success, TaskExecutor executor) { public void onStop(boolean success, TaskExecutor executor) {
if (!success && !Controllers.isStopped()) { Platform.runLater(() -> {
Platform.runLater(() -> { // Check if the application has stopped
// Check if the application has stopped // because onStop will be invoked if tasks fail when the executor service shut down.
// because onStop will be invoked if tasks fail when the executor service shut down. if (!Controllers.isStopped()) {
if (!Controllers.isStopped()) { launchingStepsPane.fireEvent(new DialogCloseEvent());
launchingStepsPane.fireEvent(new DialogCloseEvent()); if (!success) {
Exception ex = executor.getException(); Exception ex = executor.getException();
if (ex != null) { if (ex != null) {
String message; String message;
@@ -264,9 +264,9 @@ public final class LauncherHelper {
MessageType.ERROR); MessageType.ERROR);
} }
} }
}); }
} launchingStepsPane.setExecutor(null);
launchingStepsPane.setExecutor(null); });
} }
}); });
@@ -444,6 +444,8 @@ public final class LauncherHelper {
*/ */
class HMCLProcessListener implements ProcessListener { class HMCLProcessListener implements ProcessListener {
private final HMCLGameRepository repository;
private final String version;
private final Map<String, String> forbiddenTokens; private final Map<String, String> forbiddenTokens;
private ManagedProcess process; private ManagedProcess process;
private boolean lwjgl; private boolean lwjgl;
@@ -452,7 +454,9 @@ public final class LauncherHelper {
private final LinkedList<Pair<String, Log4jLevel>> logs; private final LinkedList<Pair<String, Log4jLevel>> logs;
private final CountDownLatch latch = new CountDownLatch(1); private final CountDownLatch latch = new CountDownLatch(1);
public HMCLProcessListener(AuthInfo authInfo, boolean detectWindow) { public HMCLProcessListener(HMCLGameRepository repository, String version, AuthInfo authInfo, boolean detectWindow) {
this.repository = repository;
this.version = version;
this.detectWindow = detectWindow; this.detectWindow = detectWindow;
if (authInfo == null) if (authInfo == null)
@@ -559,6 +563,12 @@ public final class LauncherHelper {
// Game crashed before opening the game window. // Game crashed before opening the game window.
if (!lwjgl) finishLaunch(); if (!lwjgl) finishLaunch();
launchingLatch.countDown();
if (exitType != ExitType.NORMAL) {
repository.markVersionLaunchedAbnormally(version);
}
if (exitType != ExitType.NORMAL && logWindow == null) if (exitType != ExitType.NORMAL && logWindow == null)
Platform.runLater(() -> { Platform.runLater(() -> {
logWindow = new LogWindow(); logWindow = new LogWindow();

View File

@@ -132,9 +132,9 @@ public final class TaskListPane extends StackPane {
task.setName(i18n("modpack.scan")); task.setName(i18n("modpack.scan"));
} }
ProgressListNode node = new ProgressListNode(task);
nodes.put(task, node);
Platform.runLater(() -> { Platform.runLater(() -> {
ProgressListNode node = new ProgressListNode(task);
nodes.put(task, node);
StageNode stageNode = stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().orElse(null); StageNode stageNode = stageNodes.stream().filter(x -> x.stage.equals(task.getStage())).findAny().orElse(null);
listBox.add(listBox.indexOf(stageNode) + 1, node); listBox.add(listBox.indexOf(stageNode) + 1, node);
}); });
@@ -148,11 +148,11 @@ public final class TaskListPane extends StackPane {
}); });
} }
ProgressListNode node = nodes.remove(task);
if (node == null)
return;
node.unbind();
Platform.runLater(() -> { Platform.runLater(() -> {
ProgressListNode node = nodes.remove(task);
if (node == null)
return;
node.unbind();
listBox.remove(node); listBox.remove(node);
}); });
} }

View File

@@ -76,7 +76,7 @@ public class Versions {
} }
public static void updateGameAssets(Profile profile, String version) { public static void updateGameAssets(Profile profile, String version) {
TaskExecutor executor = new GameAssetDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY) TaskExecutor executor = new GameAssetDownloadTask(profile.getDependency(), profile.getRepository().getVersion(version), GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true)
.executor(); .executor();
Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index")); Controllers.taskDialog(executor, i18n("version.manage.redownload_assets_index"));
executor.start(); executor.start();

View File

@@ -25,7 +25,6 @@ import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask;
import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
@@ -76,7 +75,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
} }
@Override @Override
public Task<?> checkGameCompletionAsync(Version original) { public Task<?> checkGameCompletionAsync(Version original, boolean integrityCheck) {
Version version = original.resolve(repository); Version version = original.resolve(repository);
return Task.allOf( return Task.allOf(
Task.composeAsync(() -> { Task.composeAsync(() -> {
@@ -85,14 +84,14 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
else else
return null; return null;
}), }),
new GameAssetDownloadTask(this, version, GameAssetDownloadTask.DOWNLOAD_INDEX_IF_NECESSARY), new GameAssetDownloadTask(this, version, GameAssetDownloadTask.DOWNLOAD_INDEX_IF_NECESSARY, integrityCheck),
new GameLibrariesTask(this, version) new GameLibrariesTask(this, version, integrityCheck)
); );
} }
@Override @Override
public Task<?> checkLibraryCompletionAsync(Version version) { public Task<?> checkLibraryCompletionAsync(Version version, boolean integrityCheck) {
return new GameLibrariesTask(this, version, version.getLibraries()); return new GameLibrariesTask(this, version, integrityCheck, version.getLibraries());
} }
@Override @Override

View File

@@ -46,7 +46,7 @@ public interface DependencyManager {
* *
* @return the task to check game completion. * @return the task to check game completion.
*/ */
Task<?> checkGameCompletionAsync(Version version); Task<?> checkGameCompletionAsync(Version version, boolean integrityCheck);
/** /**
* Check if the game is complete. * Check if the game is complete.
@@ -54,7 +54,7 @@ public interface DependencyManager {
* *
* @return the task to check game completion. * @return the task to check game completion.
*/ */
Task<?> checkLibraryCompletionAsync(Version version); Task<?> checkLibraryCompletionAsync(Version version, boolean integrityCheck);
/** /**
* The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine. * The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine.

View File

@@ -84,7 +84,7 @@ public final class FabricInstallTask extends Task<Version> {
public void execute() { public void execute() {
setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInfo.class), remote.getGameVersion(), remote.getSelfVersion())); setResult(getPatch(JsonUtils.GSON.fromJson(launchMetaTask.getResult(), FabricInfo.class), remote.getGameVersion(), remote.getSelfVersion()));
dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult())); dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true));
} }
private static String getLaunchMetaUrl(String gameVersion, String loaderVersion) { private static String getLaunchMetaUrl(String gameVersion, String loaderVersion) {

View File

@@ -45,14 +45,7 @@ import java.nio.file.FileSystem;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.zip.ZipException; import java.util.zip.ZipException;
@@ -133,7 +126,7 @@ public class ForgeNewInstallTask extends Task<Version> {
} }
} }
dependents.add(new GameLibrariesTask(dependencyManager, version, profile.getLibraries())); dependents.add(new GameLibrariesTask(dependencyManager, version, true, profile.getLibraries()));
} }
@Override @Override
@@ -273,7 +266,7 @@ public class ForgeNewInstallTask extends Task<Version> {
.setPriority(30000) .setPriority(30000)
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) .setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
.setVersion(selfVersion)); .setVersion(selfVersion));
dependencies.add(dependencyManager.checkLibraryCompletionAsync(forgeVersion)); dependencies.add(dependencyManager.checkLibraryCompletionAsync(forgeVersion, true));
FileUtils.deleteDirectory(temp.toFile()); FileUtils.deleteDirectory(temp.toFile());
} }

View File

@@ -27,11 +27,7 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils; import org.jackhuang.hmcl.util.io.IOUtils;
import java.io.File; import java.io.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -90,7 +86,7 @@ public class ForgeOldInstallTask extends Task<Version> {
.setPriority(30000) .setPriority(30000)
.setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId()) .setId(LibraryAnalyzer.LibraryType.FORGE.getPatchId())
.setVersion(selfVersion)); .setVersion(selfVersion));
dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo())); dependencies.add(dependencyManager.checkLibraryCompletionAsync(installProfile.getVersionInfo(), true));
} catch (ZipException ex) { } catch (ZipException ex) {
throw new ArtifactMalformedException("Malformed forge installer file", ex); throw new ArtifactMalformedException("Malformed forge installer file", ex);
} }

View File

@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.CacheRepository; import org.jackhuang.hmcl.util.CacheRepository;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
@@ -37,6 +38,7 @@ import java.net.URL;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -49,6 +51,7 @@ public final class GameAssetDownloadTask extends Task<Void> {
private final Version version; private final Version version;
private final AssetIndexInfo assetIndexInfo; private final AssetIndexInfo assetIndexInfo;
private final File assetIndexFile; private final File assetIndexFile;
private final boolean integrityCheck;
private final List<Task<?>> dependents = new LinkedList<>(); private final List<Task<?>> dependents = new LinkedList<>();
private final List<Task<?>> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
@@ -58,11 +61,12 @@ public final class GameAssetDownloadTask extends Task<Void> {
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the game version * @param version the game version
*/ */
public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloadingIndex) { public GameAssetDownloadTask(AbstractDependencyManager dependencyManager, Version version, boolean forceDownloadingIndex, boolean integrityCheck) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version.resolve(dependencyManager.getGameRepository()); this.version = version.resolve(dependencyManager.getGameRepository());
this.assetIndexInfo = this.version.getAssetIndex(); this.assetIndexInfo = this.version.getAssetIndex();
this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId()); this.assetIndexFile = dependencyManager.getGameRepository().getIndexFile(version.getId(), assetIndexInfo.getId());
this.integrityCheck = integrityCheck;
if (!assetIndexFile.exists() || forceDownloadingIndex) { if (!assetIndexFile.exists() || forceDownloadingIndex) {
dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version)); dependents.add(new GameAssetIndexDownloadTask(dependencyManager, this.version));
@@ -101,9 +105,14 @@ public final class GameAssetDownloadTask extends Task<Void> {
throw new InterruptedException(); throw new InterruptedException();
File file = dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject); File file = dependencyManager.getGameRepository().getAssetObject(version.getId(), assetIndexInfo.getId(), assetObject);
if (file.isFile()) boolean download = !file.isFile();
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash()); try {
else { if (!download && integrityCheck && !assetObject.validateChecksum(file.toPath(), true))
download = true;
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to calc hash value of file " + file.toPath(), e);
}
if (download) {
List<URL> urls = dependencyManager.getPreferredDownloadProviders().stream() List<URL> urls = dependencyManager.getPreferredDownloadProviders().stream()
.map(downloadProvider -> downloadProvider.getAssetBaseURL() + assetObject.getLocation()) .map(downloadProvider -> downloadProvider.getAssetBaseURL() + assetObject.getLocation())
.map(NetworkUtils::toURL) .map(NetworkUtils::toURL)
@@ -116,6 +125,8 @@ public final class GameAssetDownloadTask extends Task<Void> {
.setCaching(true) .setCaching(true)
.setCandidate(dependencyManager.getCacheRepository().getCommonDirectory() .setCandidate(dependencyManager.getCacheRepository().getCommonDirectory()
.resolve("assets").resolve("objects").resolve(assetObject.getLocation()))); .resolve("assets").resolve("objects").resolve(assetObject.getLocation())));
} else {
dependencyManager.getCacheRepository().tryCacheFile(file.toPath(), CacheRepository.SHA1, assetObject.getHash());
} }
updateProgress(++progress, index.getObjects().size()); updateProgress(++progress, index.getObjects().size());

View File

@@ -71,8 +71,8 @@ public class GameInstallTask extends Task<Version> {
Version version = new Version(this.version.getId()).addPatch(patch); Version version = new Version(this.version.getId()).addPatch(patch);
dependencies.add(new GameDownloadTask(dependencyManager, remote.getGameVersion(), version) dependencies.add(new GameDownloadTask(dependencyManager, remote.getGameVersion(), version)
.thenComposeAsync(Task.allOf( .thenComposeAsync(Task.allOf(
new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY), new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY, true),
new GameLibrariesTask(dependencyManager, version) new GameLibrariesTask(dependencyManager, version, true)
).withStage("hmcl.install.assets").withComposeAsync(gameRepository.save(version)))); ).withStage("hmcl.install.assets").withComposeAsync(gameRepository.save(version))));
} }

View File

@@ -21,10 +21,14 @@ import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Logging;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.logging.Level;
/** /**
* This task is to download game libraries. * This task is to download game libraries.
@@ -36,6 +40,7 @@ public final class GameLibrariesTask extends Task<Void> {
private final AbstractDependencyManager dependencyManager; private final AbstractDependencyManager dependencyManager;
private final Version version; private final Version version;
private final boolean integrityCheck;
private final List<Library> libraries; private final List<Library> libraries;
private final List<Task<?>> dependencies = new LinkedList<>(); private final List<Task<?>> dependencies = new LinkedList<>();
@@ -43,21 +48,22 @@ public final class GameLibrariesTask extends Task<Void> {
* Constructor. * Constructor.
* *
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the game version * @param version the game version
*/ */
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version) { public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, boolean integrityCheck) {
this(dependencyManager, version, version.resolve(dependencyManager.getGameRepository()).getLibraries()); this(dependencyManager, version, integrityCheck, version.resolve(dependencyManager.getGameRepository()).getLibraries());
} }
/** /**
* Constructor. * Constructor.
* *
* @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository} * @param dependencyManager the dependency manager that can provides {@link org.jackhuang.hmcl.game.GameRepository}
* @param version the game version * @param version the game version
*/ */
public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, List<Library> libraries) { public GameLibrariesTask(AbstractDependencyManager dependencyManager, Version version, boolean integrityCheck, List<Library> libraries) {
this.dependencyManager = dependencyManager; this.dependencyManager = dependencyManager;
this.version = version; this.version = version;
this.integrityCheck = integrityCheck;
this.libraries = libraries; this.libraries = libraries;
setSignificance(TaskSignificance.MODERATE); setSignificance(TaskSignificance.MODERATE);
@@ -72,10 +78,22 @@ public final class GameLibrariesTask extends Task<Void> {
public void execute() { public void execute() {
libraries.stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> { libraries.stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> {
File file = dependencyManager.getGameRepository().getLibraryFile(version, library); File file = dependencyManager.getGameRepository().getLibraryFile(version, library);
if (!file.exists()) Path jar = file.toPath();
boolean download = !file.isFile();
try {
if (!download && integrityCheck && !library.getDownload().validateChecksum(jar, true)) download = true;
if (!download && integrityCheck &&
library.getChecksums() != null && !library.getChecksums().isEmpty() &&
!LibraryDownloadTask.checksumValid(jar.toFile(), library.getChecksums())) download = true;
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to calc hash value of file " + jar, e);
}
if (download) {
dependencies.add(new LibraryDownloadTask(dependencyManager, file, library)); dependencies.add(new LibraryDownloadTask(dependencyManager, file, library));
else } else {
dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath()); dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath());
}
}); });
} }

View File

@@ -19,12 +19,7 @@ package org.jackhuang.hmcl.download.liteloader;
import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.game.Artifact;
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Lang;
@@ -79,7 +74,7 @@ public final class LiteLoaderInstallTask extends Task<Version> {
.setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration .setLogging(Collections.emptyMap()) // Mods may log in malformed format, causing XML parser to crash. So we suppress using official log4j configuration
); );
dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult())); dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true));
} }
} }

View File

@@ -20,14 +20,7 @@ package org.jackhuang.hmcl.download.optifine;
import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.download.DefaultDependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.download.VersionMismatchException; import org.jackhuang.hmcl.download.VersionMismatchException;
import org.jackhuang.hmcl.game.Arguments; import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.game.Artifact;
import org.jackhuang.hmcl.game.DefaultGameRepository;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.game.LibrariesDownloadInfo;
import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.LibraryDownloadInfo;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.CompressingUtils;
@@ -184,7 +177,7 @@ public final class OptiFineInstallTask extends Task<Version> {
libraries libraries
)); ));
dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult())); dependencies.add(dependencyManager.checkLibraryCompletionAsync(getResult(), true));
} }
public static class UnsupportedOptiFineInstallationException extends Exception { public static class UnsupportedOptiFineInstallationException extends Exception {

View File

@@ -18,9 +18,14 @@
package org.jackhuang.hmcl.game; package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.util.DigestUtils;
import org.jackhuang.hmcl.util.Hex;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.Validation; import org.jackhuang.hmcl.util.gson.Validation;
import java.io.IOException;
import java.nio.file.Path;
/** /**
* *
* @author huangyuhui * @author huangyuhui
@@ -56,4 +61,9 @@ public final class AssetObject implements Validation {
if (StringUtils.isBlank(hash) || hash.length() < 2) if (StringUtils.isBlank(hash) || hash.length() < 2)
throw new JsonParseException("AssetObject hash cannot be blank."); throw new JsonParseException("AssetObject hash cannot be blank.");
} }
public boolean validateChecksum(Path file, boolean defaultValue) throws IOException {
if (hash == null) return defaultValue;
return Hex.encodeHex(DigestUtils.digest("SHA-1", file)).equalsIgnoreCase(hash);
}
} }

View File

@@ -19,12 +19,13 @@ package org.jackhuang.hmcl.game;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.*;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.ToStringBuilder;
import org.jackhuang.hmcl.util.gson.TolerableValidationException; import org.jackhuang.hmcl.util.gson.TolerableValidationException;
import org.jackhuang.hmcl.util.gson.Validation; import org.jackhuang.hmcl.util.gson.Validation;
import java.io.IOException;
import java.nio.file.Path;
/** /**
* *
* @author huangyuhui * @author huangyuhui
@@ -79,4 +80,9 @@ public class DownloadInfo implements Validation {
if (StringUtils.isBlank(url)) if (StringUtils.isBlank(url))
throw new TolerableValidationException(); throw new TolerableValidationException();
} }
public boolean validateChecksum(Path file, boolean defaultValue) throws IOException {
if (getSha1() == null) return defaultValue;
return Hex.encodeHex(DigestUtils.digest("SHA-1", file)).equalsIgnoreCase(getSha1());
}
} }