修复自动安装在 1.20.4 的严重问题 (#3023)

* 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: b8270cd9ff

* Re-arrange entries.

* Fix.
This commit is contained in:
Burning_TNT 2024-05-31 22:02:56 +08:00 committed by GitHub
parent b43c8ceae5
commit 6b78f56298
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 139 additions and 78 deletions

View File

@ -270,10 +270,12 @@ public class InstallerItem extends Control {
return i18n("install.installer.incompatible", i18n("install.installer." + incompatibleWith)); return i18n("install.installer.incompatible", i18n("install.installer." + incompatibleWith));
} else if (version == null) { } else if (version == null) {
return i18n("install.installer.not_installed"); 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); 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.setMargin(statusLabel, new Insets(0, 0, 0, 8));
BorderPane.setAlignment(statusLabel, Pos.CENTER_LEFT); BorderPane.setAlignment(statusLabel, Pos.CENTER_LEFT);
@ -297,8 +299,9 @@ public class InstallerItem extends Control {
control.upgradable)); control.upgradable));
arrowButton.getStyleClass().add("toggle-icon4"); arrowButton.getStyleClass().add("toggle-icon4");
arrowButton.visibleProperty().bind(Bindings.createBooleanBinding( arrowButton.visibleProperty().bind(Bindings.createBooleanBinding(
() -> control.installable.get() && control.incompatibleLibraryName.get() == null, () -> control.installable.get() && control.libraryVersion.get() == null && control.incompatibleLibraryName.get() == null,
control.installable, control.incompatibleLibraryName)); control.installable, control.libraryVersion, control.incompatibleLibraryName
));
arrowButton.managedProperty().bind(arrowButton.visibleProperty()); arrowButton.managedProperty().bind(arrowButton.visibleProperty());
arrowButton.onMouseClickedProperty().bind(control.action); arrowButton.onMouseClickedProperty().bind(control.action);
buttonsContainer.getChildren().add(arrowButton); buttonsContainer.getChildren().add(arrowButton);

View File

@ -30,7 +30,6 @@ import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.task.TaskListener; import org.jackhuang.hmcl.task.TaskListener;
import org.jackhuang.hmcl.ui.*; import org.jackhuang.hmcl.ui.*;
import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider; import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
@ -90,13 +89,15 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
for (InstallerItem installerItem : group.getLibraries()) { for (InstallerItem installerItem : group.getLibraries()) {
String libraryId = installerItem.getLibraryId(); String libraryId = installerItem.getLibraryId();
String libraryVersion = analyzer.getVersion(libraryId).orElse(null); String libraryVersion = analyzer.getVersion(libraryId).orElse(null);
boolean libraryConfigurable = libraryVersion != null && analyzer.getLibraryStatus(libraryId) == LibraryAnalyzer.LibraryMark.LibraryStatus.CLEAR;
installerItem.libraryVersion.set(libraryVersion); installerItem.libraryVersion.set(libraryVersion);
installerItem.upgradable.set(libraryVersion != null); installerItem.upgradable.set(libraryConfigurable);
installerItem.installable.set(true); installerItem.installable.set(true);
installerItem.action.set(e -> { installerItem.action.set(e -> {
Controllers.getDecorator().startWizard(new UpdateInstallerWizardProvider(profile, gameVersion, version, libraryId, libraryVersion)); 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); installerItem.removable.set(removable);
if (removable) { if (removable) {
Runnable action = removeAction.apply(libraryId); Runnable action = removeAction.apply(libraryId);
@ -119,17 +120,10 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
InstallerItem installerItem = new InstallerItem(libraryId); InstallerItem installerItem = new InstallerItem(libraryId);
installerItem.libraryVersion.set(libraryVersion); installerItem.libraryVersion.set(libraryVersion);
installerItem.installable.set(false); installerItem.installable.set(false);
installerItem.upgradable.bind(installerItem.installable); installerItem.upgradable.set(false);
installerItem.removable.set(true); installerItem.removable.set(true);
installerItem.removeAction.set(e -> action.run()); 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); itemsProperty().add(installerItem);
} }
}, Platform::runLater); }, Platform::runLater);
@ -175,7 +169,8 @@ public class InstallerListPage extends ListPageBase<InstallerItem> implements Ve
@Override @Override
protected List<Node> initializeToolbar(InstallerListPage skinnable) { protected List<Node> initializeToolbar(InstallerListPage skinnable) {
return Collections.singletonList( return Collections.singletonList(
createToolbarButton2(i18n("install.installer.install_offline"), SVG.PLUS, skinnable::installOffline)); createToolbarButton2(i18n("install.installer.install_offline"), SVG.PLUS, skinnable::installOffline)
);
} }
} }
} }

View File

@ -637,6 +637,7 @@ install.installer.optifine=OptiFine
install.installer.quilt=Quilt install.installer.quilt=Quilt
install.installer.quilt-api=QSL/QFAPI install.installer.quilt-api=QSL/QFAPI
install.installer.version=%s install.installer.version=%s
install.installer.external_version=%s Installed by external process, which cannot be configured
install.modpack=Install a Modpack install.modpack=Install a Modpack
install.new_game=Add a New Instance install.new_game=Add a New Instance
install.new_game.already_exists=This instance already exists. Please use another name. install.new_game.already_exists=This instance already exists. Please use another name.

View File

@ -521,6 +521,7 @@ install.installer.optifine=OptiFine
install.installer.quilt=Quilt install.installer.quilt=Quilt
install.installer.quilt-api=QSL/QFAPI install.installer.quilt-api=QSL/QFAPI
install.installer.version=%s install.installer.version=%s
install.installer.external_version=%s 由外部安裝的版本,無法解除安裝或更換
install.modpack=安裝模組包 install.modpack=安裝模組包
install.new_game=安裝新遊戲版本 install.new_game=安裝新遊戲版本
install.new_game.already_exists=此版本已經存在,請重新命名 install.new_game.already_exists=此版本已經存在,請重新命名

View File

@ -520,6 +520,7 @@ install.installer.optifine=OptiFine
install.installer.quilt=Quilt install.installer.quilt=Quilt
install.installer.quilt-api=QSL/QFAPI install.installer.quilt-api=QSL/QFAPI
install.installer.version=%s install.installer.version=%s
install.installer.external_version=%s 由外部安装的版本,无法卸载或更换
install.modpack=安装整合包 install.modpack=安装整合包
install.new_game=安装新游戏版本 install.new_game=安装新游戏版本
install.new_game.already_exists=此版本已经存在,请换一个名字 install.new_game.already_exists=此版本已经存在,请换一个名字

View File

@ -19,7 +19,10 @@ package org.jackhuang.hmcl.download;
import org.jackhuang.hmcl.game.*; import org.jackhuang.hmcl.game.*;
import org.jackhuang.hmcl.mod.ModLoaderType; import org.jackhuang.hmcl.mod.ModLoaderType;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Pair; 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.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -51,6 +54,15 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
return Optional.ofNullable(libraries.get(type.getPatchId())).map(Pair::getKey); return Optional.ofNullable(libraries.get(type.getPatchId())).map(Pair::getKey);
} }
/**
* 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 LibraryMark.LibraryStatus getLibraryStatus(String type) {
return version.hasPatch(type) ? LibraryMark.LibraryStatus.CLEAR : LibraryMark.LibraryStatus.JUST_EXISTED;
}
@NotNull @NotNull
@Override @Override
public Iterator<LibraryMark> iterator() { public Iterator<LibraryMark> iterator() {
@ -65,7 +77,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
@Override @Override
public LibraryMark next() { public LibraryMark next() {
Map.Entry<String, Pair<Library, String>> entry = impl.next(); Map.Entry<String, Pair<Library, String>> 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<LibraryAnalyzer.LibraryMa
} }
public boolean hasModLauncher() { public boolean hasModLauncher() {
final String modLauncher = "cpw.mods.modlauncher.Launcher"; return LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(version.getMainClass()) || version.getPatches().stream().anyMatch(
return modLauncher.equals(version.getMainClass()) || version.getPatches().stream().anyMatch(patch -> modLauncher.equals(patch.getMainClass())); patch -> LibraryAnalyzer.MOD_LAUNCHER_MAIN.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()));
} }
private Version removingMatchedLibrary(Version version, String libraryId) { private Version removingMatchedLibrary(Version version, String libraryId) {
@ -184,7 +192,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
private final Pattern FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?<forge>[0-9.]+)(-([0-9.]+))?$"); private final Pattern FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?<forge>[0-9.]+)(-([0-9.]+))?$");
@Override @Override
public String patchVersion(Version gameVersion, String libraryVersion) { protected String patchVersion(Version gameVersion, String libraryVersion) {
Matcher matcher = FORGE_VERSION_MATCHER.matcher(libraryVersion); Matcher matcher = FORGE_VERSION_MATCHER.matcher(libraryVersion);
if (matcher.find()) { if (matcher.find()) {
return matcher.group("forge"); return matcher.group("forge");
@ -193,7 +201,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
} }
@Override @Override
public boolean matchLibrary(Library library, List<Library> libraries) { protected boolean matchLibrary(Library library, List<Library> libraries) {
for (Library l : libraries) { for (Library l : libraries) {
if (NEO_FORGE.matchLibrary(l, libraries)) { if (NEO_FORGE.matchLibrary(l, libraries)) {
return false; return false;
@ -206,7 +214,7 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
private final Pattern NEO_FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?<forge>[0-9.]+)(-([0-9.]+))?$"); private final Pattern NEO_FORGE_VERSION_MATCHER = Pattern.compile("^([0-9.]+)-(?<forge>[0-9.]+)(-([0-9.]+))?$");
@Override @Override
public String patchVersion(Version gameVersion, String libraryVersion) { protected String patchVersion(Version gameVersion, String libraryVersion) {
Matcher matcher = NEO_FORGE_VERSION_MATCHER.matcher(libraryVersion); Matcher matcher = NEO_FORGE_VERSION_MATCHER.matcher(libraryVersion);
if (matcher.find()) { if (matcher.find()) {
return matcher.group("forge"); return matcher.group("forge");
@ -289,22 +297,37 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
return null; return null;
} }
public boolean matchLibrary(Library library, List<Library> libraries) { protected boolean matchLibrary(Library library, List<Library> libraries) {
return group.matcher(library.getGroupId()).matches() && artifact.matcher(library.getArtifactId()).matches(); 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; 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 libraryId;
private final String libraryVersion; 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.libraryId = libraryId;
this.libraryVersion = libraryVersion; this.libraryVersion = libraryVersion;
this.status = status;
} }
@NotNull @NotNull
@ -316,12 +339,27 @@ public final class LibraryAnalyzer implements Iterable<LibraryAnalyzer.LibraryMa
public String getLibraryVersion() { public String getLibraryVersion() {
return libraryVersion; return libraryVersion;
} }
public LibraryStatus getStatus() {
return status;
}
} }
public static final String VANILLA_MAIN = "net.minecraft.client.main.Main"; public static final String VANILLA_MAIN = "net.minecraft.client.main.Main";
public static final String LAUNCH_WRAPPER_MAIN = "net.minecraft.launchwrapper.Launch"; public static final String LAUNCH_WRAPPER_MAIN = "net.minecraft.launchwrapper.Launch";
public static final String MOD_LAUNCHER_MAIN = "cpw.mods.modlauncher.Launcher"; public static final String MOD_LAUNCHER_MAIN = "cpw.mods.modlauncher.Launcher";
public static final String BOOTSTRAP_LAUNCHER_MAIN = "cpw.mods.bootstraplauncher.BootstrapLauncher"; public static final String BOOTSTRAP_LAUNCHER_MAIN = "cpw.mods.bootstraplauncher.BootstrapLauncher";
public static final String FORGE_BOOTSTRAP_MAIN = "net.minecraftforge.bootstrap.ForgeBootstrap";
public static final Set<String> 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<VersionNumber> FORGE_OPTIFINE_BROKEN_RANGE = VersionNumber.between("48.0.0", "49.0.50");
public static final String[] FORGE_TWEAKERS = new String[]{ public static final String[] FORGE_TWEAKERS = new String[]{
"net.minecraftforge.legacy._1_5_2.LibraryFixerTweaker", // 1.5.2 "net.minecraftforge.legacy._1_5_2.LibraryFixerTweaker", // 1.5.2

View File

@ -76,7 +76,9 @@ public class MaintainTask extends Task<Version> {
} }
List<Library> libraries = version.getLibraries(); List<Library> 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); Library library = libraries.get(0);
if ("org.glavo".equals(library.getGroupId()) if ("org.glavo".equals(library.getGroupId())
&& ("log4j-patch".equals(library.getArtifactId()) || "log4j-patch-beta9".equals(library.getArtifactId())) && ("log4j-patch".equals(library.getArtifactId()) || "log4j-patch-beta9".equals(library.getArtifactId()))
@ -161,8 +163,8 @@ public class MaintainTask extends Task<Version> {
Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath(); Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath();
try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) { try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) {
Files.createDirectories(libraryPath.getParent()); Files.createDirectories(libraryPath.getParent());
Files.copy(input, libraryPath, StandardCopyOption.REPLACE_EXISTING); Files.copy(Objects.requireNonNull(input, "Bundled HMCLTransformerDiscoveryService is missing."), libraryPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) { } catch (IOException | NullPointerException e) {
LOG.warning("Unable to unpack HMCLTransformerDiscoveryService", e); LOG.warning("Unable to unpack HMCLTransformerDiscoveryService", e);
} }
}); });
@ -282,13 +284,6 @@ public class MaintainTask extends Task<Version> {
return version.setLibraries(libraries.stream().filter(Objects::nonNull).collect(Collectors.toList())); 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) { public static Version unique(Version version) {
List<Library> libraries = new ArrayList<>(); List<Library> libraries = new ArrayList<>();

View File

@ -102,18 +102,10 @@ public final class ForgeInstallTask extends Task<Version> {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (GameVersionNumber.compare("1.13", remote.getGameVersion()) <= 0) { if (GameVersionNumber.compare("1.13", remote.getGameVersion()) <= 0) {
// Forge 1.13 is not compatible with fabric. // Forge 1.13 is not compatible with fabric.
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass))
&& !LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass)
&& !LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass))
throw new UnsupportedInstallationException(UNSUPPORTED_LAUNCH_WRAPPER); 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)) if (detectForgeInstallerType(dependencyManager, version, installer))
dependency = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer); dependency = new ForgeNewInstallTask(dependencyManager, version, remote.getSelfVersion(), installer);
else else

View File

@ -18,15 +18,21 @@
package org.jackhuang.hmcl.download.game; package org.jackhuang.hmcl.download.game;
import org.jackhuang.hmcl.download.AbstractDependencyManager; import org.jackhuang.hmcl.download.AbstractDependencyManager;
import org.jackhuang.hmcl.download.LibraryAnalyzer;
import org.jackhuang.hmcl.game.GameRepository; import org.jackhuang.hmcl.game.GameRepository;
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.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.FileUtils; 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.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -81,20 +87,23 @@ public final class GameLibrariesTask extends Task<Void> {
File file = gameRepository.getLibraryFile(version, library); File file = gameRepository.getLibraryFile(version, library);
Path jar = file.toPath(); Path jar = file.toPath();
if (!file.isFile()) return true; if (!file.isFile()) return true;
if (!integrityCheck) {
return false;
}
try { try {
if (integrityCheck && !library.getDownload().validateChecksum(jar, true)) return true; if (!library.getDownload().validateChecksum(jar, true)) {
if (integrityCheck && return true;
library.getChecksums() != null && !library.getChecksums().isEmpty() && }
!LibraryDownloadTask.checksumValid(file, library.getChecksums())) return true; if (library.getChecksums() != null && !library.getChecksums().isEmpty() && !LibraryDownloadTask.checksumValid(file, library.getChecksums())) {
if (integrityCheck) { return true;
String ext = FileUtils.getExtension(file); }
if (ext.equals("jar")) { if (FileUtils.getExtension(file).equals("jar")) {
try { try {
FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar); FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar);
} catch (IOException ignored) { } catch (IOException ignored) {
// the Jar file is malformed, so re-download it. // the Jar file is malformed, so re-download it.
return true; return true;
}
} }
} }
} catch (IOException e) { } catch (IOException e) {
@ -105,16 +114,32 @@ public final class GameLibrariesTask extends Task<Void> {
} }
@Override @Override
public void execute() { public void execute() throws IOException {
libraries.stream().filter(Library::appliesToCurrentEnvironment).forEach(library -> { GameRepository gameRepository = dependencyManager.getGameRepository();
File file = dependencyManager.getGameRepository().getLibraryFile(version, library); for (Library library : libraries) {
if (shouldDownloadLibrary(dependencyManager.getGameRepository(), version, library, integrityCheck)) { if (!library.appliesToCurrentEnvironment()) {
if (library.hasDownloadURL() || !"optifine".equals(library.getGroupId())) continue;
dependencies.add(new LibraryDownloadTask(dependencyManager, file, library)); }
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 { } else {
dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath()); dependencyManager.getCacheRepository().tryCacheLibrary(library, file.toPath());
} }
}); }
} }
} }

View File

@ -125,20 +125,23 @@ public final class OptiFineInstallTask extends Task<Version> {
@Override @Override
public void execute() throws Exception { public void execute() throws Exception {
String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass(); String originalMainClass = version.resolve(dependencyManager.getGameRepository()).getMainClass();
if (!LibraryAnalyzer.VANILLA_MAIN.equals(originalMainClass) && if (!LibraryAnalyzer.FORGE_OPTIFINE_MAIN.contains(originalMainClass))
!LibraryAnalyzer.LAUNCH_WRAPPER_MAIN.equals(originalMainClass) &&
!LibraryAnalyzer.MOD_LAUNCHER_MAIN.equals(originalMainClass) &&
!LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN.equals(originalMainClass))
throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER); throw new UnsupportedInstallationException(UnsupportedInstallationException.UNSUPPORTED_LAUNCH_WRAPPER);
List<Library> libraries = new ArrayList<>(4); List<Library> libraries = new ArrayList<>(4);
libraries.add(optiFineLibrary); 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 // Install launch wrapper modified by OptiFine
boolean hasLaunchWrapper = false; boolean hasLaunchWrapper = false;
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(dest)) {
Path optiFineLibraryPath = gameRepository.getLibraryFile(version, optiFineLibrary).toPath();
if (Files.exists(fs.getPath("optifine/Patcher.class"))) { if (Files.exists(fs.getPath("optifine/Patcher.class"))) {
String[] command = { String[] command = {
JavaVersion.fromCurrentEnvironment().getBinary().toString(), JavaVersion.fromCurrentEnvironment().getBinary().toString(),
@ -147,13 +150,17 @@ public final class OptiFineInstallTask extends Task<Version> {
"optifine.Patcher", "optifine.Patcher",
gameRepository.getVersionJar(version).getAbsolutePath(), gameRepository.getVersionJar(version).getAbsolutePath(),
dest.toString(), dest.toString(),
gameRepository.getLibraryFile(version, optiFineLibrary).toString() optiFineLibraryPath.toString()
}; };
int exitCode = SystemUtils.callExternalProcess(command); int exitCode = SystemUtils.callExternalProcess(command);
if (exitCode != 0) if (exitCode != 0)
throw new IOException("OptiFine patcher failed, command: " + new CommandBuilder().addAll(Arrays.asList(command))); throw new IOException("OptiFine patcher failed, command: " + new CommandBuilder().addAll(Arrays.asList(command)));
} else { } 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"); Path launchWrapper2 = fs.getPath("launchwrapper-2.0.jar");

View File

@ -295,7 +295,10 @@ public class Version implements Comparable<Version>, 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. // Assume patches themselves do not have patches recursively.
List<Version> sortedPatches = patches.stream() List<Version> sortedPatches = patches.stream()
.sorted(Comparator.comparing(Version::getPriority)) .sorted(Comparator.comparing(Version::getPriority))