From 6b78f562982c829cc44a66451888faaf2cd698ef Mon Sep 17 00:00:00 2001 From: Burning_TNT <88144530+burningtnt@users.noreply.github.com> Date: Fri, 31 May 2024 22:02:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=8A=A8=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E5=9C=A8=201.20.4=20=E7=9A=84=E4=B8=A5=E9=87=8D?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20(#3023)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: OptiFine and Forge has compatible problem on 1.20.4 . Fix: Forge changes it's main class on 1.20.4 . * Fix: HMCL cannot display the analyed versions installed by external launchers. * Refuse to configure installable widgets when it's from external process. * Add javadoc about LibraryStatus * Adapt to the new change of Forge: https://github.com/MinecraftForge/MinecraftForge/commit/b8270cd9ff71678bd71f93938b35e5904b5f507a * Re-arrange entries. * Fix. --- .../org/jackhuang/hmcl/ui/InstallerItem.java | 11 +-- .../hmcl/ui/versions/InstallerListPage.java | 19 ++---- .../resources/assets/lang/I18N.properties | 1 + .../resources/assets/lang/I18N_zh.properties | 1 + .../assets/lang/I18N_zh_CN.properties | 1 + .../hmcl/download/LibraryAnalyzer.java | 68 +++++++++++++++---- .../jackhuang/hmcl/download/MaintainTask.java | 15 ++-- .../hmcl/download/forge/ForgeInstallTask.java | 10 +-- .../hmcl/download/game/GameLibrariesTask.java | 65 ++++++++++++------ .../optifine/OptiFineInstallTask.java | 21 ++++-- .../java/org/jackhuang/hmcl/game/Version.java | 5 +- 11 files changed, 139 insertions(+), 78 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java index 4fe9995aa..b6cf2a2de 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/InstallerItem.java @@ -270,10 +270,12 @@ public class InstallerItem extends Control { return i18n("install.installer.incompatible", i18n("install.installer." + incompatibleWith)); } else if (version == null) { return i18n("install.installer.not_installed"); - } else { + } else if (control.id.equals(MINECRAFT.getPatchId()) || control.removable.get() || control.upgradable.get()) { return i18n("install.installer.version", version); + } else { + return i18n("install.installer.external_version", version); } - }, control.incompatibleLibraryName, control.incompatibleWithGame, control.libraryVersion)); + }, control.incompatibleLibraryName, control.incompatibleWithGame, control.libraryVersion, control.installable, control.removable, control.upgradable)); BorderPane.setMargin(statusLabel, new Insets(0, 0, 0, 8)); BorderPane.setAlignment(statusLabel, Pos.CENTER_LEFT); @@ -297,8 +299,9 @@ public class InstallerItem extends Control { control.upgradable)); arrowButton.getStyleClass().add("toggle-icon4"); arrowButton.visibleProperty().bind(Bindings.createBooleanBinding( - () -> control.installable.get() && control.incompatibleLibraryName.get() == null, - control.installable, control.incompatibleLibraryName)); + () -> control.installable.get() && control.libraryVersion.get() == null && control.incompatibleLibraryName.get() == null, + control.installable, control.libraryVersion, control.incompatibleLibraryName + )); arrowButton.managedProperty().bind(arrowButton.visibleProperty()); arrowButton.onMouseClickedProperty().bind(control.action); buttonsContainer.getChildren().add(arrowButton); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java index de495ed93..d65df18fc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java @@ -30,7 +30,6 @@ import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.task.TaskListener; import org.jackhuang.hmcl.ui.*; import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider; -import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.FileUtils; @@ -90,13 +89,15 @@ public class InstallerListPage extends ListPageBase implements Ve for (InstallerItem installerItem : group.getLibraries()) { String libraryId = installerItem.getLibraryId(); String libraryVersion = analyzer.getVersion(libraryId).orElse(null); + boolean libraryConfigurable = libraryVersion != null && analyzer.getLibraryStatus(libraryId) == LibraryAnalyzer.LibraryMark.LibraryStatus.CLEAR; + installerItem.libraryVersion.set(libraryVersion); - installerItem.upgradable.set(libraryVersion != null); + installerItem.upgradable.set(libraryConfigurable); installerItem.installable.set(true); installerItem.action.set(e -> { Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion)); }); - boolean removable = !LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(libraryId) && libraryVersion != null; + boolean removable = !LibraryAnalyzer.LibraryType.MINECRAFT.getPatchId().equals(libraryId) && libraryConfigurable; installerItem.removable.set(removable); if (removable) { Runnable action = removeAction.apply(libraryId); @@ -119,17 +120,10 @@ public class InstallerListPage extends ListPageBase implements Ve InstallerItem installerItem = new InstallerItem(libraryId); installerItem.libraryVersion.set(libraryVersion); installerItem.installable.set(false); - installerItem.upgradable.bind(installerItem.installable); + installerItem.upgradable.set(false); installerItem.removable.set(true); installerItem.removeAction.set(e -> action.run()); - if (libraryVersion != null && Lang.test(() -> profile.getDependency().getVersionList(libraryId))) { - installerItem.installable.set(true); - installerItem.action.set(e -> { - Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion)); - }); - } - itemsProperty().add(installerItem); } }, Platform::runLater); @@ -175,7 +169,8 @@ public class InstallerListPage extends ListPageBase implements Ve @Override protected List initializeToolbar(InstallerListPage skinnable) { return Collections.singletonList( - createToolbarButton2(i18n("install.installer.install_offline"), SVG.PLUS, skinnable::installOffline)); + createToolbarButton2(i18n("install.installer.install_offline"), SVG.PLUS, skinnable::installOffline) + ); } } } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 7ba11e7a8..77ed7f419 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -637,6 +637,7 @@ install.installer.optifine=OptiFine install.installer.quilt=Quilt install.installer.quilt-api=QSL/QFAPI install.installer.version=%s +install.installer.external_version=%s Installed by external process, which cannot be configured install.modpack=Install a Modpack install.new_game=Add a New Instance install.new_game.already_exists=This instance already exists. Please use another name. diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index f71543b97..0063a9cb9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -521,6 +521,7 @@ install.installer.optifine=OptiFine install.installer.quilt=Quilt install.installer.quilt-api=QSL/QFAPI install.installer.version=%s +install.installer.external_version=%s 由外部安裝的版本,無法解除安裝或更換 install.modpack=安裝模組包 install.new_game=安裝新遊戲版本 install.new_game.already_exists=此版本已經存在,請重新命名 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 2f31613ea..ee0fca1ee 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -520,6 +520,7 @@ install.installer.optifine=OptiFine install.installer.quilt=Quilt install.installer.quilt-api=QSL/QFAPI install.installer.version=%s +install.installer.external_version=%s 由外部安装的版本,无法卸载或更换 install.modpack=安装整合包 install.new_game=安装新游戏版本 install.new_game.already_exists=此版本已经存在,请换一个名字 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 8b189f066..71f963cfb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/LibraryAnalyzer.java @@ -19,7 +19,10 @@ package org.jackhuang.hmcl.download; import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.mod.ModLoaderType; +import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.Pair; +import org.jackhuang.hmcl.util.versioning.VersionNumber; +import org.jackhuang.hmcl.util.versioning.VersionRange; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,6 +54,15 @@ public final class LibraryAnalyzer implements Iterable iterator() { @@ -65,7 +77,7 @@ public final class LibraryAnalyzer implements Iterable> entry = impl.next(); - return new LibraryMark(entry.getKey(), entry.getValue().getValue()); + return new LibraryMark(entry.getKey(), entry.getValue().getValue(), getLibraryStatus(entry.getKey())); } }; } @@ -85,13 +97,9 @@ public final class LibraryAnalyzer implements Iterable modLauncher.equals(patch.getMainClass())); - } - - public boolean hasBootstrapLauncher() { - final String bootstrapLauncher = "cpw.mods.bootstraplauncher.BootstrapLauncher"; - return bootstrapLauncher.equals(version.getMainClass()) || version.getPatches().stream().anyMatch(patch -> bootstrapLauncher.equals(patch.getMainClass())); + return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(version.getMainClass()) || version.getPatches().stream().anyMatch( + patch -> LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(patch.getMainClass()) + ); } private Version removingMatchedLibrary(Version version, String libraryId) { @@ -184,7 +192,7 @@ public final class LibraryAnalyzer implements Iterable[0-9.]+)(-([0-9.]+))?$"); @Override - public String patchVersion(Version gameVersion, String libraryVersion) { + protected String patchVersion(Version gameVersion, String libraryVersion) { Matcher matcher = FORGE_VERSION_MATCHER.matcher(libraryVersion); if (matcher.find()) { return matcher.group("forge"); @@ -193,7 +201,7 @@ public final class LibraryAnalyzer implements Iterable libraries) { + protected boolean matchLibrary(Library library, List libraries) { for (Library l : libraries) { if (NEO_FORGE.matchLibrary(l, libraries)) { return false; @@ -206,7 +214,7 @@ public final class LibraryAnalyzer implements Iterable[0-9.]+)(-([0-9.]+))?$"); @Override - public String patchVersion(Version gameVersion, String libraryVersion) { + protected String patchVersion(Version gameVersion, String libraryVersion) { Matcher matcher = NEO_FORGE_VERSION_MATCHER.matcher(libraryVersion); if (matcher.find()) { return matcher.group("forge"); @@ -289,22 +297,37 @@ public final class LibraryAnalyzer implements Iterable libraries) { + protected boolean matchLibrary(Library library, List libraries) { return group.matcher(library.getGroupId()).matches() && artifact.matcher(library.getArtifactId()).matches(); } - public String patchVersion(Version gameVersion, String libraryVersion) { + protected String patchVersion(Version gameVersion, String libraryVersion) { return libraryVersion; } } - public static class LibraryMark { + public final static class LibraryMark { + /** + * If a library is provided in $.patches, it's structure is so clear that we can do any operation. + * Otherwise, we must guess how are these libraries mixed. + * Maybe a guessing implementation will be provided in the future. But by now, we simply set it to JUST_EXISTED. + */ + public enum LibraryStatus { + CLEAR, UNSURE, JUST_EXISTED + } + private final String libraryId; private final String libraryVersion; + /** + * If this version is installed by HMCL, instead of external process, + * which means $.patches contains this library, structureClear is true. + */ + private final LibraryStatus status; - public LibraryMark(@NotNull String libraryId, @Nullable String libraryVersion) { + private LibraryMark(@NotNull String libraryId, @Nullable String libraryVersion, LibraryStatus status) { this.libraryId = libraryId; this.libraryVersion = libraryVersion; + this.status = status; } @NotNull @@ -316,12 +339,27 @@ public final class LibraryAnalyzer implements Iterable FORGE_OPTIFINE_MAIN = new HashSet<>(Lang.immutableListOf( + LibraryAnalyzer.VANILLA_MAIN, + LibraryAnalyzer.LAUNCH_WRAPPER_MAIN, + LibraryAnalyzer.MOD_LAUNCHER_MAIN, + LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN, + LibraryAnalyzer.FORGE_BOOTSTRAP_MAIN + )); + + public static final VersionRange FORGE_OPTIFINE_BROKEN_RANGE = VersionNumber.between("48.0.0", "49.0.50"); public static final String[] FORGE_TWEAKERS = new String[]{ "net.minecraftforge.legacy._1_5_2.LibraryFixerTweaker", // 1.5.2 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java index 85a0455e6..2d29217a9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -76,7 +76,9 @@ public class MaintainTask extends Task { } List libraries = version.getLibraries(); - if (libraries.size() > 0) { + if (!libraries.isEmpty()) { + // HMCL once use log4j-patch to prevent virus. But now, we only modify log4j2.xml. + // Therefore, we remove this library. Library library = libraries.get(0); if ("org.glavo".equals(library.getGroupId()) && ("log4j-patch".equals(library.getArtifactId()) || "log4j-patch-beta9".equals(library.getArtifactId())) @@ -161,8 +163,8 @@ public class MaintainTask extends Task { Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath(); try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) { Files.createDirectories(libraryPath.getParent()); - Files.copy(input, libraryPath, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { + Files.copy(Objects.requireNonNull(input, "Bundled HMCLTransformerDiscoveryService is missing."), libraryPath, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException | NullPointerException e) { LOG.warning("Unable to unpack HMCLTransformerDiscoveryService", e); } }); @@ -282,13 +284,6 @@ public class MaintainTask extends Task { return version.setLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); } - public static boolean isPurePatched(Version version) { - if (!version.isResolvedPreservingPatches()) - throw new IllegalArgumentException("isPurePatched requires a version resolved preserving patches"); - - return version.hasPatch("game"); - } - public static Version unique(Version version) { List libraries = new ArrayList<>(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java index 35a60f422..5fe58b176 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java @@ -102,18 +102,10 @@ public final class ForgeInstallTask extends Task { String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); if (GameVersionNumber.compare("1.13", remote.getGameVersion()) <= 0) { // Forge 1.13 is not compatible with fabric. - if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) - && !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) - && !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) - && !LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass)) + if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass)) throw new UnsupportedInstallationException(UNSUPPORTED_LAUNCH_WRAPPER); - } else { - // Forge 1.12 and older versions is compatible with vanilla and launchwrapper. - // if (!"net.minecraft.client.main.Main".equals(originalMainClass) && !"net.minecraft.launchwrapper.Launch".equals(originalMainClass)) - // throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException(); } - if (detectForgeInstallerType(dependencyManager, version, installer)) dependency = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer); else 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 3090917b2..793a6b122 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 @@ -18,15 +18,21 @@ package org.jackhuang.hmcl.download.game; import org.jackhuang.hmcl.download.AbstractDependencyManager; +import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.versioning.GameVersionNumber; +import org.jackhuang.hmcl.util.versioning.VersionNumber; import java.io.File; import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -81,20 +87,23 @@ public final class GameLibrariesTask extends Task { File file = gameRepository.getLibraryFile(version, library); Path jar = file.toPath(); if (!file.isFile()) return true; + + if (!integrityCheck) { + return false; + } 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; - } + if (!library.getDownload().validateChecksum(jar, true)) { + return true; + } + if (library.getChecksums() != null && !library.getChecksums().isEmpty() && !LibraryDownloadTask.checksumValid(file, library.getChecksums())) { + return true; + } + if (FileUtils.getExtension(file).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) { @@ -105,16 +114,32 @@ public final class GameLibrariesTask extends Task { } @Override - public void execute() { - libraries.stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> { - File file = dependencyManager.getGameRepository().getLibraryFile(version, library); - if (shouldDownloadLibrary(dependencyManager.getGameRepository(), version, library, integrityCheck)) { - if (library.hasDownloadURL() || !"optifine".equals(library.getGroupId())) - dependencies.add(new LibraryDownloadTask(dependencyManager, file, library)); + public void execute() throws IOException { + GameRepository gameRepository = dependencyManager.getGameRepository(); + for (Library library : libraries) { + if (!library.appliesToCurrentEnvironment()) { + continue; + } + + File file = gameRepository.getLibraryFile(version, library); + if ("optifine".equals(library.getGroupId()) && file.exists() && 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())) { + Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml")); + } catch (IOException e) { + throw new IOException("Cannot fix optifine", e); + } + } + } + if (shouldDownloadLibrary(gameRepository, version, library, integrityCheck) && library.hasDownloadURL()) { + dependencies.add(new LibraryDownloadTask(dependencyManager, file, library)); } else { dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath()); } - }); + } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java index 03b7832f1..ffc73e3f1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java @@ -125,20 +125,23 @@ public final class OptiFineInstallTask extends Task { @Override public void execute() throws Exception { String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); - if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && - !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) && - !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) && - !LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass)) + if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass)) throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER); List libraries = new ArrayList<>(4); libraries.add(optiFineLibrary); - FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath()); + Path optiFineInstallerLibraryPath = gameRepository.getLibraryFile(version, optiFineInstallerLibrary).toPath(); + FileUtils.copyFile(dest, optiFineInstallerLibraryPath); + + try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(optiFineInstallerLibraryPath)) { + Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml")); + } // Install launch wrapper modified by OptiFine boolean hasLaunchWrapper = false; try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) { + Path optiFineLibraryPath = gameRepository.getLibraryFile(version, optiFineLibrary).toPath(); if (Files.exists(fs.getPath("optifine/Patcher.class"))) { String[] command = { JavaVersion.fromCurrentEnvironment().getBinary().toString(), @@ -147,13 +150,17 @@ public final class OptiFineInstallTask extends Task { "optifine.Patcher", gameRepository.getVersionJar(version).getAbsolutePath(), dest.toString(), - gameRepository.getLibraryFile(version, optiFineLibrary).toString() + optiFineLibraryPath.toString() }; int exitCode = SystemUtils.callExternalProcess(command); if (exitCode != 0) throw new IOException("OptiFine patcher failed, command: " + new CommandBuilder().addAll(Arrays.asList(command))); } else { - FileUtils.copyFile(dest, gameRepository.getLibraryFile(version, optiFineLibrary).toPath()); + FileUtils.copyFile(dest, optiFineLibraryPath); + } + + try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(optiFineLibraryPath)) { + Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml")); } Path launchWrapper2 = fs.getPath("launchwrapper-2.0.jar"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java index f77767f69..b50bdd83c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java @@ -295,7 +295,10 @@ public class Version implements Comparable, Validation { } } - if (patches != null && !patches.isEmpty()) { + if (patches == null) { + // This is a version from external launcher. + thisVersion = thisVersion.merge(this, true); + } else if (!patches.isEmpty()) { // Assume patches themselves do not have patches recursively. List sortedPatches = patches.stream() .sorted(Comparator.comparing(Version::getPriority))