diff --git a/HMCL/build.gradle b/HMCL/build.gradle index be25e3cfe4..d5c7d4f1ef 100755 --- a/HMCL/build.gradle +++ b/HMCL/build.gradle @@ -38,7 +38,7 @@ def buildnumber = System.getenv("TRAVIS_BUILD_NUMBER") if (buildnumber == null) buildnumber = System.getenv("BUILD_NUMBER") if (buildnumber == null) -buildnumber = "51" +buildnumber = "52" def versionroot = System.getenv("VERSION_ROOT") if (versionroot == null) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Argument.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Argument.java index 5e550348bc..6e0195c5c2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Argument.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Argument.java @@ -32,7 +32,7 @@ import java.util.Map; * * @author huang */ -public interface Argument { +public interface Argument extends Cloneable { /** * Parse this argument in form: ${key name} or simply a string. @@ -42,6 +42,8 @@ public interface Argument { * @return parsed argument element, empty if this argument is ignored and will not be added. */ List toString(Map keys, Map features); + + public Object clone(); public static class ArgumentSerializer implements JsonDeserializer, JsonSerializer { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Arguments.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Arguments.java index c9de41dc1e..72229aa5bf 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Arguments.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Arguments.java @@ -18,11 +18,13 @@ package org.jackhuang.hmcl.core.version; import com.google.gson.annotations.SerializedName; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.jackhuang.hmcl.util.ArrayUtils; import org.jackhuang.hmcl.util.CollectionUtils; /** @@ -34,6 +36,25 @@ public class Arguments { public List game; @SerializedName("jvm") public List jvm; + + public static Arguments clone(Arguments original) { + if (original == null) + return null; + Arguments ret = new Arguments(); + ret.game = CollectionUtils.deepCopy(original.game, value -> (Argument) value.clone()); + ret.jvm = CollectionUtils.deepCopy(original.jvm, value -> (Argument) value.clone()); + return ret; + } + + public static Arguments merge(Arguments a, Arguments b) { + if (a == null) return b; + else if (b == null) return a; + Arguments ret = new Arguments(); + ret.game = ArrayUtils.merge(a.game, b.game); + ret.jvm = ArrayUtils.merge(a.jvm, b.jvm); + if (ret.game == null && ret.jvm == null) return null; + return ret; + } public static List parseStringArguments(List arguments, Map keys) { return CollectionUtils.map(arguments, str -> keys.getOrDefault(str, str)); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/MinecraftVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/MinecraftVersion.java index a8db1cec92..3c347da8aa 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/MinecraftVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/MinecraftVersion.java @@ -34,6 +34,7 @@ import org.jackhuang.hmcl.core.GameException; import org.jackhuang.hmcl.core.service.IMinecraftProvider; import org.jackhuang.hmcl.core.asset.AssetsIndex; import org.jackhuang.hmcl.util.ArrayUtils; +import org.jackhuang.hmcl.util.CollectionUtils; /** * @@ -81,8 +82,9 @@ public class MinecraftVersion implements Cloneable, Comparable public MinecraftVersion() { } - public MinecraftVersion(String minecraftArguments, String mainClass, String time, String id, String type, String processArguments, String releaseTime, String assets, String jar, String inheritsFrom, String runDir, int minimumLauncherVersion, List libraries, boolean hidden, Map downloads, AssetIndexDownloadInfo assetIndexDownloadInfo, Map logging) { + public MinecraftVersion(Arguments arguments, String minecraftArguments, String mainClass, String time, String id, String type, String processArguments, String releaseTime, String assets, String jar, String inheritsFrom, String runDir, int minimumLauncherVersion, List libraries, boolean hidden, Map downloads, AssetIndexDownloadInfo assetIndexDownloadInfo, Map logging) { this(); + this.arguments = Arguments.clone(arguments); this.minecraftArguments = minecraftArguments; this.mainClass = mainClass; this.time = time; @@ -100,28 +102,9 @@ public class MinecraftVersion implements Cloneable, Comparable this.assetIndex = null; else this.assetIndex = (AssetIndexDownloadInfo) assetIndexDownloadInfo.clone(); - if (libraries == null) - this.libraries = new ArrayList<>(); - else { - this.libraries = new ArrayList<>(libraries.size()); - for (MinecraftLibrary library : libraries) - if (library != null) - this.libraries.add((MinecraftLibrary) library.clone()); - } - if (downloads == null) - this.downloads = null; - else { - this.downloads = new HashMap<>(downloads.size()); - for (Map.Entry entry : downloads.entrySet()) - this.downloads.put(entry.getKey(), (GameDownloadInfo) entry.getValue().clone()); - } - if (logging == null) - this.logging = null; - else { - this.logging = new HashMap<>(logging.size()); - for (Map.Entry entry : logging.entrySet()) - this.logging.put(entry.getKey(), (LoggingInfo) entry.getValue().clone()); - } + this.libraries = CollectionUtils.deepCopy(libraries, value -> (MinecraftLibrary) value.clone()); + this.downloads = CollectionUtils.deepCopy(downloads, value -> (GameDownloadInfo) value.clone()); + this.logging = CollectionUtils.deepCopy(logging, value -> (LoggingInfo) value.clone()); } @Override @@ -153,6 +136,7 @@ public class MinecraftVersion implements Cloneable, Comparable } parent = parent.resolve(provider, resolvedSoFar); MinecraftVersion result = new MinecraftVersion( + Arguments.merge(parent.arguments, this.arguments), this.minecraftArguments != null ? this.minecraftArguments : parent.minecraftArguments, this.mainClass != null ? this.mainClass : parent.mainClass, this.time, this.id, this.type, parent.processArguments, this.releaseTime, diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/OSRestriction.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/OSRestriction.java index d55e3d7111..d7587868d3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/OSRestriction.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/OSRestriction.java @@ -26,7 +26,7 @@ import org.jackhuang.hmcl.util.sys.OS; * * @author huangyuhui */ -public class OSRestriction { +public class OSRestriction implements Cloneable { @SerializedName("version") private String version; @@ -90,4 +90,13 @@ public class OSRestriction { } return true; } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException ex) { + throw new AssertionError(ex); + } + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/RuledArgument.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/RuledArgument.java index 86f6e9e8ef..5a32e01b0c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/RuledArgument.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/RuledArgument.java @@ -47,6 +47,18 @@ public class RuledArgument implements Argument { this.value = args; } + @Override + public Object clone() { + try { + RuledArgument ret = (RuledArgument) super.clone(); + ret.rules = CollectionUtils.deepCopy(rules, value -> (Rules) value.clone()); + ret.value = CollectionUtils.copy(value); + return ret; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(ex); + } + } + @Override public List toString(Map keys, Map features) { if (!Rules.allow(rules, features)) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Rules.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Rules.java index 89811df3e9..60979a92a6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Rules.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/Rules.java @@ -21,12 +21,13 @@ import com.google.gson.annotations.SerializedName; import java.util.Collection; import java.util.Collections; import java.util.Map; +import org.jackhuang.hmcl.util.CollectionUtils; /** * * @author huangyuhui */ -public class Rules { +public class Rules implements Cloneable { @SerializedName("action") private String action; @@ -48,6 +49,18 @@ public class Rules { this.features = features; } + @Override + public Object clone() { + try { + Rules rules = (Rules) super.clone(); + rules.os = (OSRestriction) os.clone(); + rules.features = CollectionUtils.copy(features); + return rules; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(ex); + } + } + public String action(Map features) { if (os != null && !os.isCurrentOS()) return null; if (this.features != null) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/StringArgument.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/StringArgument.java index af89a88a0b..d16498fa0f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/StringArgument.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/core/version/StringArgument.java @@ -35,6 +35,15 @@ public class StringArgument implements Argument { this.argument = argument; } + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException ex) { + throw new AssertionError(ex); + } + } + @Override public List toString(Map keys, Map features) { String res = argument; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ArrayUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ArrayUtils.java index 2cd2d5cb7c..ca874a1d54 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ArrayUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ArrayUtils.java @@ -73,6 +73,9 @@ public final class ArrayUtils { } public static ArrayList merge(List a, List b) { + if (a == null && b == null) return null; + else if (a == null) return new ArrayList<>(b); + else if (b == null) return new ArrayList<>(a); ArrayList al = new ArrayList<>(a.size() + b.size()); al.addAll(a); al.addAll(b); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CollectionUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CollectionUtils.java index 8e4f91823c..f0a9cb0750 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CollectionUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CollectionUtils.java @@ -20,8 +20,10 @@ package org.jackhuang.hmcl.util; import org.jackhuang.hmcl.api.func.Predicate; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import org.jackhuang.hmcl.api.func.Function; /** @@ -65,4 +67,40 @@ public final class CollectionUtils { } return removed; } + + public static ArrayList deepCopy(List original, Function clone) { + if (original == null) + return null; + ArrayList ret = new ArrayList<>(original.size()); + for (T x : original) + ret.add(clone.apply(x)); + return ret; + } + + public static ArrayList copy(List original) { + if (original == null) + return null; + ArrayList ret = new ArrayList<>(original.size()); + for (T x : original) + ret.add(x); + return ret; + } + + public static Map deepCopy(Map original, Function clone) { + if (original == null) + return null; + HashMap ret = new HashMap<>(original.size()); + for (HashMap.Entry x : original.entrySet()) + ret.put(x.getKey(), clone.apply(x.getValue())); + return ret; + } + + public static Map copy(Map original) { + if (original == null) + return null; + HashMap ret = new HashMap<>(original.size()); + for (HashMap.Entry x : original.entrySet()) + ret.put(x.getKey(), x.getValue()); + return ret; + } }