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 5f7adfd90..acec48374 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -124,7 +124,7 @@ public final class LauncherHelper { private void launch0() { HMCLGameRepository repository = profile.getRepository(); DefaultDependencyManager dependencyManager = profile.getDependency(); - Version version = MaintainTask.maintain(repository, repository.getResolvedVersion(selectedVersion)); + Version version = MaintainTask.maintain(repository, repository.getResolvedPreservingPatchesVersion(selectedVersion)); Optional gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version)); TaskExecutor executor = Task.allOf( 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 459d66d8e..a7246041d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -45,7 +45,7 @@ public class MaintainTask extends Task { this.repository = repository; this.version = version; - if (version.getInheritsFrom() != null) + if (!version.isResolvedPreservingPatches()) throw new IllegalArgumentException("MaintainTask requires independent game version"); } @@ -55,13 +55,31 @@ public class MaintainTask extends Task { } public static Version maintain(GameRepository repository, Version version) { - if (version.getInheritsFrom() != null) + if (!version.isResolvedPreservingPatches()) throw new IllegalArgumentException("MaintainTask requires independent game version"); - String mainClass = version.resolve(null).getMainClass(); + // We made a mistake that priority of OptiFine should be 90000 instead of 10000, + // manually reset priority here. + version = version.setPatches(version.getPatches().stream().map(patch -> { + if (FABRIC.getPatchId().equals(patch.getId())) { + return patch.setPriority(30000); + } else if (FORGE.getPatchId().equals(patch.getId())) { + return patch.setPriority(30000); + } else if (LITELOADER.getPatchId().equals(patch.getId())) { + return patch.setPriority(60000); + } else if (OPTIFINE.getPatchId().equals(patch.getId())) { + return patch.setPriority(90000); + } else if (MINECRAFT.getPatchId().equals(patch.getId())) { + return patch.setPriority(0); + } else { + return patch; + } + }).collect(Collectors.toList())).resolve(repository); + + String mainClass = version.getMainClass(); if (mainClass != null && mainClass.contains("launchwrapper")) { - return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version))); + return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true)); } else { // Vanilla Minecraft does not need maintain // Forge 1.13 support not implemented, not compatible with OptiFine currently. @@ -73,11 +91,11 @@ public class MaintainTask extends Task { public static Version maintainPreservingPatches(GameRepository repository, Version version) { if (!version.isResolvedPreservingPatches()) throw new IllegalArgumentException("MaintainTask requires independent game version"); - Version newVersion = maintain(repository, version.resolve(repository)); + Version newVersion = maintain(repository, version.resolvePreservingPatches(repository)); return newVersion.setPatches(version.getPatches()).markAsUnresolved(); } - private static Version maintainGameWithLaunchWrapper(Version version) { + private static Version maintainGameWithLaunchWrapper(Version version, boolean reorderTweakClass) { LibraryAnalyzer libraryAnalyzer = LibraryAnalyzer.analyze(version); VersionLibraryBuilder builder = new VersionLibraryBuilder(version); String mainClass = null; @@ -88,24 +106,27 @@ public class MaintainTask extends Task { // Installing Forge will override the Minecraft arguments in json, so LiteLoader and OptiFine Tweaker are being re-added. - builder.removeTweakClass("liteloader"); if (libraryAnalyzer.has(LITELOADER) && !libraryAnalyzer.hasModLauncher()) { - builder.addArgument("--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"); + builder.replaceTweakClass("liteloader", "com.mumfrey.liteloader.launch.LiteLoaderTweaker", !reorderTweakClass); + } else { + builder.removeTweakClass("liteloader"); } - builder.removeTweakClass("optifine"); if (libraryAnalyzer.has(OPTIFINE)) { if (!libraryAnalyzer.has(LITELOADER) && !libraryAnalyzer.has(FORGE)) { - builder.addArgument("--tweakClass", "optifine.OptiFineTweaker"); + builder.replaceTweakClass("optifine", "optifine.OptiFineTweaker", !reorderTweakClass); } else { if (libraryAnalyzer.hasModLauncher()) { // If ModLauncher installed, we use ModLauncher in place of LaunchWrapper. mainClass = "cpw.mods.modlauncher.Launcher"; + builder.replaceTweakClass("optifine", "optifine.OptiFineForgeTweaker", !reorderTweakClass); } else { // If forge or LiteLoader installed, OptiFine Forge Tweaker is needed. - builder.addArgument("--tweakClass", "optifine.OptiFineForgeTweaker"); + builder.replaceTweakClass("optifine", "optifine.OptiFineForgeTweaker", !reorderTweakClass); } } + } else { + builder.removeTweakClass("optifine"); } Version ret = builder.build(); @@ -141,6 +162,13 @@ 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/optifine/OptiFineInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java index f5a964003..382af286c 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 @@ -122,6 +122,9 @@ public final class OptiFineInstallTask extends Task { @Override public void execute() throws Exception { + if ("cpw.mods.modlauncher.Launcher".equals(version.resolve(dependencyManager.getGameRepository()).getMainClass())) + throw new OptiFineInstallTask.UnsupportedOptiFineInstallationException(); + List libraries = new LinkedList<>(); libraries.add(optiFineLibrary); @@ -171,7 +174,7 @@ public final class OptiFineInstallTask extends Task { setResult(new Version( LibraryAnalyzer.LibraryType.OPTIFINE.getPatchId(), remote.getSelfVersion(), - 10000, + 90000, new Arguments().addGameArguments("--tweakClass", "optifine.OptiFineTweaker"), "net.minecraft.launchwrapper.Launch", libraries 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 4f449ec19..896aa2e96 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Version.java @@ -257,7 +257,7 @@ public class Version implements Comparable, Validation { if (inheritsFrom == null) { if (isRoot()) - thisVersion = new Version(id).setJar(id); + thisVersion = new Version(id).setPatches(patches).setJar(id); else thisVersion = this.jar == null ? this.setJar(id) : this; } else { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java index 840010fef..f8f3e0ee2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/VersionLibraryBuilder.java @@ -51,14 +51,57 @@ public final class VersionLibraryBuilder { } public void removeTweakClass(String target) { + replaceTweakClass(target, null, true); + } + + /** + * Replace existing tweak class without reordering. + * If the tweak class does not exist, the new tweak class will be appended to the end of argument list. + * If the tweak class appears more than one time, the tweak classes will be removed excluding the first one. + * + * @param target the tweak class to replace + * @param replacement the new tweak class to be replaced with + */ + public void replaceTweakClass(String target, String replacement) { + replaceTweakClass(target, replacement, true); + } + + /** + * Replace existing tweak class and add the new tweak class to the end of argument list. + * + * @param target the tweak class to replace + * @param replacement the new tweak class to be replaced with + */ + public void addTweakClass(String target, String replacement) { + replaceTweakClass(target, replacement, false); + } + + /** + * Replace existing tweak class. + * If the tweak class does not exist, the new tweak class will be appended to the end of argument list. + * If the tweak class appears more than one time, the tweak classes will be removed excluding the first one. + * + * @param target the tweak class to replace + * @param replacement the new tweak class to be replaced with, if null, remove the tweak class only + * @param inPlace if true, replace the tweak class in place, otherwise add the tweak class to the end of the argument list without replacement. + */ + public void replaceTweakClass(String target, String replacement, boolean inPlace) { + boolean replaced = false; if (useMcArgs) { for (int i = 0; i + 1 < mcArgs.size(); ++i) { String arg0Str = mcArgs.get(i); String arg1Str = mcArgs.get(i + 1); if (arg0Str.equals("--tweakClass") && arg1Str.toLowerCase().contains(target)) { - mcArgs.remove(i); - mcArgs.remove(i); - --i; + if (!replaced && inPlace) { + // for the first one, we replace the tweak class only. + mcArgs.set(i + 1, replacement); + replaced = true; + } else { + // otherwise, we remove the duplicate tweak classes. + mcArgs.remove(i); + mcArgs.remove(i); + --i; + } } } } @@ -71,12 +114,25 @@ public final class VersionLibraryBuilder { String arg0Str = arg0.toString(); String arg1Str = arg1.toString(); if (arg0Str.equals("--tweakClass") && arg1Str.toLowerCase().contains(target)) { - game.remove(i); - game.remove(i); - --i; + if (!replaced && inPlace) { + // for the first one, we replace the tweak class only. + game.set(i + 1, new StringArgument(replacement)); + replaced = true; + } else { + // otherwise, we remove the duplicate tweak classes. + game.remove(i); + game.remove(i); + --i; + } } } } + + // if the tweak class does not exist, add a new one to the end. + if (!replaced && replacement != null) { + game.add(new StringArgument("--tweakClass")); + game.add(new StringArgument(replacement)); + } } public void addArgument(String... args) {