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 cd7191e6e..1029729fc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -126,26 +126,28 @@ public final class LauncherHelper { DefaultDependencyManager dependencyManager = profile.getDependency(); Version version = MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion)); Optional gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version)); + boolean integrityCheck = repository.unmarkVersionLaunchedAbnormally(selectedVersion); - TaskExecutor executor = Task.allOf( - Task.composeAsync(() -> { - if (setting.isNotCheckGame()) - return null; - else - return dependencyManager.checkGameCompletionAsync(version, repository.unmarkVersionLaunchedAbnormally(selectedVersion)); - }), Task.composeAsync(() -> { - try { - ModpackConfiguration configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion)); - if ("Curse".equals(configuration.getType())) - return new CurseCompletionTask(dependencyManager, selectedVersion); - else if ("Server".equals(configuration.getType())) - return new ServerModpackCompletionTask(dependencyManager, selectedVersion); - else - return null; - } catch (IOException e) { - return null; - } - })).withStage("launch.state.dependencies") + TaskExecutor executor = dependencyManager.checkPatchCompletionAsync(repository.getVersion(selectedVersion), integrityCheck) + .thenComposeAsync(Task.allOf( + Task.composeAsync(() -> { + if (setting.isNotCheckGame()) + return null; + else + return dependencyManager.checkGameCompletionAsync(version, integrityCheck); + }), Task.composeAsync(() -> { + try { + ModpackConfiguration configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion)); + if ("Curse".equals(configuration.getType())) + return new CurseCompletionTask(dependencyManager, selectedVersion); + else if ("Server".equals(configuration.getType())) + return new ServerModpackCompletionTask(dependencyManager, selectedVersion); + else + return null; + } catch (IOException e) { + return null; + } + }))).withStage("launch.state.dependencies") .thenComposeAsync(Task.supplyAsync(() -> { try { return account.logIn(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java index 7a54c11e6..07dbf9dc1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -23,11 +23,18 @@ import org.jackhuang.hmcl.download.game.GameDownloadTask; import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.download.optifine.OptiFineInstallTask; import org.jackhuang.hmcl.game.DefaultGameRepository; +import org.jackhuang.hmcl.game.GameVersion; +import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Task; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.OPTIFINE; /** * Note: This class has no state. @@ -86,6 +93,28 @@ public class DefaultDependencyManager extends AbstractDependencyManager { return new GameLibrariesTask(this, version, integrityCheck, version.getLibraries()); } + @Override + public Task checkPatchCompletionAsync(Version version, boolean integrityCheck) { + return Task.composeAsync(() -> { + List> tasks = new ArrayList<>(); + + Optional gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version)); + if (!gameVersion.isPresent()) return null; + + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(version.resolvePreservingPatches(getGameRepository())); + version.resolve(getGameRepository()).getLibraries().stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> { + Optional libraryVersion = analyzer.getVersion(OPTIFINE); + if (OPTIFINE.matchLibrary(library) && libraryVersion.isPresent()) { + if (GameLibrariesTask.shouldDownloadLibrary(repository, version, library, integrityCheck)) { + tasks.add(installLibraryAsync(gameVersion.get(), version, OPTIFINE.getPatchId(), libraryVersion.get())); + } + } + }); + + return Task.allOf(tasks); + }); + } + @Override public Task installLibraryAsync(String gameVersion, Version baseVersion, String libraryId, String libraryVersion) { if (baseVersion.isResolved()) throw new IllegalArgumentException("Version should not be resolved"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java index 5c89edd3c..c358fabb4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DependencyManager.java @@ -56,6 +56,16 @@ public interface DependencyManager { */ Task checkLibraryCompletionAsync(Version version, boolean integrityCheck); + /** + * Check if patches of this version in complete. + * If not, reinstall the patch if possible. + * + * @param version the version to be checked + * @param integrityCheck check if some libraries are corrupt. + * @return the task to check patches completion. + */ + Task checkPatchCompletionAsync(Version version, boolean integrityCheck); + /** * The builder to build a brand new game then libraries such as Forge, LiteLoader and OptiFine. */ diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java index 2520ac159..acdb8b235 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -91,10 +91,7 @@ public final class LibraryAnalyzer implements Iterable libraries = new ArrayList<>(); for (Library library : version.getLibraries()) { - String groupId = library.getGroupId(); - String artifactId = library.getArtifactId(); - - if (type.group.matcher(groupId).matches() && type.artifact.matcher(artifactId).matches()) { + if (type.matchLibrary(library)) { // skip } else { libraries.add(library); @@ -129,11 +126,8 @@ public final class LibraryAnalyzer implements Iterable> libraries = new HashMap<>(); for (Library library : version.resolve(null).getLibraries()) { - String groupId = library.getGroupId(); - String artifactId = library.getArtifactId(); - for (LibraryType type : LibraryType.values()) { - if (type.group.matcher(groupId).matches() && type.artifact.matcher(artifactId).matches()) { + if (type.matchLibrary(library)) { libraries.put(type.getPatchId(), pair(library, library.getVersion())); break; } @@ -180,6 +174,10 @@ public final class LibraryAnalyzer implements Iterable { return dependencies; } + 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; + try { + if (integrityCheck && !library.getDownload().validateChecksum(jar, true)) return true; + if (integrityCheck && + library.getChecksums() != null && !library.getChecksums().isEmpty() && + !LibraryDownloadTask.checksumValid(file, library.getChecksums())) return true; + if (integrityCheck) { + String ext = FileUtils.getExtension(file); + if (ext.equals("jar")) { + try { + FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar); + } catch (IOException ignored) { + // the Jar file is malformed, so re-download it. + return true; + } + } + } + } catch (IOException e) { + Logging.LOG.log(Level.WARNING, "Unable to calc hash value of file " + jar, e); + } + + return false; + } + @Override public void execute() { libraries.stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> { File file = dependencyManager.getGameRepository().getLibraryFile(version, library); - 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(file, library.getChecksums())) download = true; - if (!download && integrityCheck) { - String ext = FileUtils.getExtension(file); - if (ext.equals("jar")) { - try (JarFile jarFile = new JarFile(file)) { - jarFile.getManifest(); - } catch (IOException ignored) { - // the Jar file is malformed, so re-download it. - download = true; - } - } - } - } catch (IOException e) { - Logging.LOG.log(Level.WARNING, "Unable to calc hash value of file " + jar, e); - } - - if (download) { + if (shouldDownloadLibrary(dependencyManager.getGameRepository(), version, library, integrityCheck)) { dependencies.add(new LibraryDownloadTask(dependencyManager, file, library)); } else { dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath());